当输出写入到/dev/stdout/
或/dev/stderr/
在Windows上,PhantomJS
经过以下步骤(如被看见在在\phantomjs\src\webpage.cpprender
法):
- 在缺乏
/dev/stdout/
和/dev/stderr/
的TEM porary文件路径被分配。
- 用临时文件路径调用
renderPdf
。
- 将网页呈现到此文件路径。
- 阅读本文件的内容到
QByteArray
。
- 在字节阵列上调用
QString::fromAscii
并写入stdout
或stderr
。
- 删除临时文件。
首先,我构建了PhantomJS
的源代码,但注释掉了文件删除。在下一次运行中,我能够检查它所呈现的临时文件,结果证明它是完全正确的。我也尝试运行phantomjs.exe rasterize.js http://google.com > test.png
,结果相同。这立即排除了渲染问题,或任何与PDF相关的问题,这意味着问题必须与数据写入stdout
的方式相关。
在这个阶段,我怀疑是否有一些文本编码shenanigans正在进行。从以前的运行中,我有同一个文件的有效和无效版本(在这种情况下是PNG)。
使用一些C#代码,我跑了如下实验:
//Read the contents of the known good file.
byte[] bytesFromGoodFile = File.ReadAllBytes("valid_file.png");
//Read the contents of the known bad file.
byte[] bytesFromBadFile = File.ReadAllBytes("invalid_file.png");
//Take the bytes from the valid file and convert to a string
//using the Latin-1 encoding.
string iso88591String = Encoding.GetEncoding("iso-8859-1").GetString(bytesFromGoodFile);
//Take the Latin-1 encoded string and retrieve its bytes using the UTF-8 encoding.
byte[] bytesFromIso88591String = Encoding.UTF8.GetBytes(iso88591String);
//If the bytes from the Latin-1 string are all the same as the ones from the
//known bad file, we have an encoding problem.
Debug.Assert(bytesFromBadFile
.Select((b, i) => b == bytesFromIso88591String[i])
.All(c => c));
请注意,我用ISO-8859-1编码为QT
使用此名称作为default encoding for c-strings。事实证明,所有这些字节都是一样的。该练习的重点在于看我是否可以模仿导致有效数据无效的编码步骤。
有关进一步的证据,我调查了\phantomjs\src\system.cpp和\phantomjs\src\filesystem.cpp。
- 在
system.cpp
,所述System
类持有引用,除其他外,File
对象为stdout
,stdin
和stderr
,其被设置为使用UTF-8
编码。
- 当写入
stdout
时,将调用File
对象的write
函数。此函数支持写入文本和二进制文件,但由于System
类初始化它们的方式,所有写入都将被视为文本文件。
所以,问题归结为:我们需要将执行二进制写stdout
,但我们写操作最终会被视为文本,并具有适用于他们的编码,导致生成的文件是无效的。
鉴于上述问题,我看不出有什么办法让这个工作,你想在Windows的方式未做更改PhantomJS
代码。因此,它们是:
第一次更改将提供一个函数,我们可以调用File
对象来明确执行二进制写入。
添加下面的函数原型\phantomjs\src\filesystem.h
:
bool binaryWrite(const QString &data);
,并放置在\phantomjs\src\filesystem.cpp
其定义(代码此方法来源于write
方法在这个文件中):
bool File::binaryWrite(const QString &data)
{
if (!m_file->isWritable()) {
qDebug() << "File::write - " << "Couldn't write:" << m_file->fileName();
return true;
}
QByteArray bytes(data.size(), Qt::Uninitialized);
for(int i = 0; i < data.size(); ++i) {
bytes[i] = data.at(i).toAscii();
}
return m_file->write(bytes);
}
在身边第0123行\phantomjs\src\webpage.cpp
您会看到一段代码,如下所示:
if(fileName == STDOUT_FILENAME){
#ifdef Q_OS_WIN32
_setmode(_fileno(stdout), O_BINARY);
#endif
((File *)system->_stderr())->write(QString::fromAscii(name.constData(), name.size()));
#ifdef Q_OS_WIN32
_setmode(_fileno(stdout), O_TEXT);
#endif
}
它改成这样:
if(fileName == STDOUT_FILENAME){
#ifdef Q_OS_WIN32
_setmode(_fileno(stdout), O_BINARY);
((File *)system->_stdout())->binaryWrite(QString::fromAscii(ba.constData(), ba.size()));
#elif
((File *)system->_stderr())->write(QString::fromAscii(name.constData(), name.size()));
#endif
#ifdef Q_OS_WIN32
_setmode(_fileno(stdout), O_TEXT);
#endif
}
那么,什么是代码替换所做的就是调用我们的新binaryWrite
功能,而是通过#ifdef Q_OS_WIN32
块不那么森严。我这样做是为了保留非Windows系统上的旧功能,这些功能似乎没有表现出这种问题(或者他们是这样做的?)。请注意,此修正仅适用于写入stdout
- 如果您希望始终可以将其应用于stderr
,但在此情况下可能无关紧要。
如果你只是想要一个预先构建的二进制文件(谁不会?),你可以在我的SkyDrive上找到phantomjs.exe
这些修复程序。我的版本大约是19MB,而我之前下载的版本只有大约6MB,但我按照指示here,所以它应该没问题。
什么版本的phantomjs?尝试升级到最新版本。 – philfreo
我在1.9.2 Win8x64上看到了同样的问题。不输出管道似乎在控制台中有一些pdf内容,但通过phantomjs将输出直接输出到文件rasterize.js> test.pdf没有任何进展。 –
@philfreo我在Win7上使用1.9.2 – michaeltintiuc