phantomjs pdf to stdout

时间:2013-10-22 08:40:05

标签: javascript pdf stdout phantomjs

我拼命想把phantomJS生成的PDF输出到像here这样的标准输出

我得到的是一个空的PDF文件,虽然它的大小不是0,但它显示的是空白页。

var page = require('webpage').create(),
system = require('system'),
address;

address = system.args[1];
page.paperSize = {format: 'A4'};

page.open(address, function (status) {
    if (status !== 'success') {
        console.log('Unable to load the address!');
        phantom.exit();
    } else {
        window.setTimeout(function () {
            page.render('/dev/stdout', { format: 'pdf' });
            phantom.exit();
        }, 1000);
    }
});

我称之为:phantomjs rasterize.js http://google.com>test.pdf

我尝试将/dev/stdout更改为system.stdout但不是运气。直接将PDF写入文件可以毫无问题地工作。

我正在寻找一个跨平台的实现,所以我希望这可以在非Linux系统上实现。

3 个答案:

答案 0 :(得分:15)

在Windows上将输出写入/dev/stdout//dev/stderr/时,PhantomJS会执行以下步骤(如\phantomjs\src\webpage.cpp中的render方法所示):

  1. 如果没有/dev/stdout//dev/stderr/,则会分配临时文件路径。
  2. 使用临时文件路径调用renderPdf
  3. 将网页渲染到此文件路径。
  4. 将此文件的内容读入QByteArray
  5. 在字节数组上调用QString::fromAscii并写入stdoutstderr
  6. 删除临时文件。
  7. 首先,我构建了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编码,QT将其用作default encoding for c-strings。事实证明,所有这些字节都是相同的。该练习的目的是看我是否可以模仿导致有效数据无效的编码步骤。

    有关进一步的证据,我调查了\phantomjs\src\system.cpp\phantomjs\src\filesystem.cpp

    • system.cpp中,System类包含对Filestdoutstdin的{​​{1}}个对象的引用,设置为使用stderr编码。
    • 写入UTF-8时,会调用stdout对象的write函数。此函数支持写入文本和二进制文件,但由于File类初始化它们的方式,所有写入都将被视为文本文件。

    所以问题可以归结为:我们需要对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); } 的第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块保护的。我这样做是为了保留非Windows系统上的旧功能,这些功能似乎没有出现这个问题(或者他们呢?)。请注意,此修复仅适用于写入#ifdef Q_OS_WIN32 - 如果您愿意,您可以随时将其应用于stdout,但在这种情况下可能并不重要。

    如果您只想要一个预先构建的二进制文件(谁不会?),您可以在SkyDrive上找到stderr这些修补程序。我的版本大约是19MB,而我之前下载的版本只有大约6MB,虽然我按照here的说明操作,所以应该没问题。

答案 1 :(得分:8)

是的,这是正确的ISO-8859-1是QT的默认编码,因此您需要将所需参数添加到命令行--output-encoding = ISO-8859-1,因此pdf输出不会腐败

即。

phantomjs.exe rasterize.js --output-encoding = ISO-8859-1&lt; input.html&gt;输出.pdf

和rasterize.js看起来像这样(经过测试,适用于Unix和Windows)

var page = require('webpage').create(),
system = require('system');

page.viewportSize = {width: 600, height: 600};
page.paperSize = {format: 'A4', orientation: system.args[1], margin: '1cm'};

page.content = system.stdin.read();

window.setTimeout(function () {
    try {
        page.render('/dev/stdout', {format: 'pdf'});
    }
    catch (e) {
        console.log(e.message + ';;' + output_file);
    }
    phantom.exit();
}, 1000);

或者您可以使用stdout设置编码,如果您正在读取UTF-8流,那么您可能还必须为stdin设置编码;

system.stdout.setEncoding('ISO-8859-1');
system.stdin.setEncoding('UTF-8');
page.content = system.stdin.read();

答案 2 :(得分:0)

是否必须将pdf输出到stdout?你无法将代码更改为:

var page = require('webpage').create(),
system = require('system'),
address;

address = system.args[1];
output  = system.args[2];
page.paperSize = {format: 'A4'};

page.open(address, function (status) {
    if (status !== 'success') {
        console.log('Unable to load the address!');
        phantom.exit();
    } else {
        window.setTimeout(function () {
            page.render(output, { format: 'pdf' });
            phantom.exit();
        }, 1000);
    }
});

并像这样使用它:

phantomjs rasterize.js http://google.com test.pdf