将输出写入Windows/dev/stdout/
或/dev/stderr/
在 Windows 上时,PhantomJS
请执行以下步骤(如\phantomjs\src\webpage.cpprender
中的方法所示):
- 如果没有,
/dev/stdout/
则/dev/stderr/
分配一个临时文件路径。
renderPdf
使用临时文件路径调用。
- 将网页渲染到此文件路径。
- 将此文件的内容读入
QByteArray
.
- 调用
QString::fromAscii
字节数组并写入stdout
or stderr
。
- 删除临时文件。
首先,我为 构建了源代码PhantomJS
,但注释掉了文件删除。在下一次运行中,我能够检查它渲染的临时文件,结果证明它完全没问题。我也尝试phantomjs.exe rasterize.js http://google.com > test.png
以相同的结果运行。这立即排除了渲染问题,或者任何与 PDF 相关的问题,这意味着问题必须与数据写入的方式有关stdout
。
在这个阶段,我怀疑是否有一些文本编码恶作剧正在发生。从之前的运行中,我有同一个文件的有效和无效版本(在这种情况下是 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 编码作为c-stringsQT
的默认编码。事实证明,所有这些字节都是相同的。该练习的目的是看看我是否可以模仿导致有效数据无效的编码步骤。
为了进一步的证据,我调查了 \phantomjs\src\system.cpp和\phantomjs\src\filesystem.cpp。
- 在
system.cpp
中,该类包含对和的对象的System
引用,这些对象被设置为使用编码。File
stdout
stdin
stderr
UTF-8
- 写入时
stdout
,将调用对象的write
函数。File
此函数支持写入文本文件和二进制文件,但由于System
类初始化它们的方式,所有写入都将被视为写入文本文件。
所以问题归结为:我们需要对 执行二进制写入stdout
,但我们的写入最终被视为文本并对其应用了编码,导致生成的文件无效。
鉴于上述问题,我看不到任何方法可以在不更改PhantomJS
代码的情况下在 Windows 上按照您想要的方式工作。所以他们在这里:
第一个更改将提供一个函数,我们可以调用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);
}
在第 920 行左右,\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
但在这种情况下可能并不重要。
如果您只想要一个预构建的二进制文件(谁不想要?),您可以在我的SkyDrivephantomjs.exe
上找到这些修复程序。我的版本大约是 19MB,而我之前下载的只有大约 6MB,尽管我按照这里的说明进行操作,所以应该没问题。