19

有没有办法在不指定扩展名为 .pdf 的输出文件的情况下触发 PhantomJS 中的 PDF 导出功能?我们想用它stdout来输出 PDF。

4

4 回答 4

20

您可以直接输出到标准输出,而不需要临时文件。

page.render('/dev/stdout', { format: 'pdf' });

有关添加时间的历史记录,请参见此处

如果您想从标准输入获取 HTML 并将 PDF 输出到标准输出,请参见此处

于 2013-06-24T18:30:14.897 回答
19

很抱歉回答太长了;我有一种感觉,我一生中需要参考这种方法几十次,所以我会写“一个答案来统治他们”。我将首先讨论一下文件、文件描述符、(命名的)管道和输出重定向,然后回答您的问题。


考虑这个简单的 C99 程序:

#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[])
{

  if (argc < 2) {
    printf("Usage: %s file_name\n", argv[0]);
    return 1;
  }

  FILE* file = fopen(argv[1], "w");
  if (!file) {
    printf("No such file: %s\n", argv[1]);
    return 2;
  }

  fprintf(file, "some text...");

  fclose(file); 

  return 0;
}

非常简单。它接受一个参数(文件名)并将一些文本打印到其中。再简单不过了。


clang write_to_file.c -o write_to_file.o用or编译它gcc write_to_file.c -o write_to_file.o

现在,运行./write_to_file.o some_file(打印到some_file)。然后运行cat some_file。结果正如预期的那样some text...

现在让我们变得更花哨。输入(./write_to_file.o /dev/stdout) > some_file终端。我们要求程序写入其标准输出(而不是常规文件),然后我们将其重定向stdoutsome_file(使用> some_file)。我们可以使用以下任何方法来实现这一点:

  • (./write_to_file.o /dev/stdout) > some_file,这意味着“使用stdout

  • (./write_to_file.o /dev/stderr) 2> some_file,这意味着“使用stderr,并使用重定向它2>

  • (./write_to_file.o /dev/fd/2) 2> some_file, 同上; stderr是默认分配给 Unix 进程的第三个文件描述符(在stdin和之后stdout

  • (./write_to_file.o /dev/fd/5) 5> some_file,这意味着“使用你的第六个文件描述符,并将其重定向到some_file

如果不清楚,我们使用的是 Unix 管道而不是实际文件(毕竟在 Unix 中一切都是文件)。我们可以用这个管道做各种花哨的事情:将其写入文件,或将其写入命名管道并在不同进程之间共享。


现在,让我们创建一个命名管道:

mkfifo my_pipe

如果你ls -l现在输入,你会看到:

total 32
prw-r--r--  1 pooriaazimi  staff     0 Jul 15 09:12 my_pipe
-rw-r--r--  1 pooriaazimi  staff   336 Jul 15 08:29 write_to_file.c
-rwxr-xr-x  1 pooriaazimi  staff  8832 Jul 15 08:34 write_to_file.o

注意第二行开头的p 。这意味着这my_pipe是一个(命名的)管道。

现在,让我们指定我们想要对管道做什么:

gzip -c < my_pipe > out.gz &

这意味着:gzip我放入里面my_pipe并将结果写入out.gz. 最后&的 要求外壳在后台运行此命令。你会得到类似的东西[1] 10449,并且控制回到终端。

然后,只需将我们的 C 程序的输出重定向到这个管道:

(./write_to_file.o /dev/fd/5) 5> my_pipe

或者

./write_to_file.o my_pipe

你会得到

[1]+  Done                    gzip -c < my_pipe > out.gz

这意味着gzip命令已经完成。

现在,做另一个ls -l

total 40
prw-r--r--  1 pooriaazimi  staff     0 Jul 15 09:14 my_pipe
-rw-r--r--  1 pooriaazimi  staff    32 Jul 15 09:14 out.gz
-rw-r--r--  1 pooriaazimi  staff   336 Jul 15 08:29 write_to_file.c
-rwxr-xr-x  1 pooriaazimi  staff  8832 Jul 15 08:34 write_to_file.o

我们已经成功gzip编辑了我们的文本!

执行gzip -d out.gz解压这个gziped 文件。它将被删除并out创建一个新文件 ( )。cat out得到我们:

some text...

这是我们所期望的。

别忘了用rm my_pipe!


现在回到 PhantomJS。

这是一个简单的 PhantomJS 脚本(render.coffee用 CoffeeScript 编写),它接受两个参数:一个 URL 和一个文件名。它加载 URL,呈现它并将其写入给定的文件名:

system = require 'system'

renderUrlToFile = (url, file, callback) ->
  page = require('webpage').create()
  page.viewportSize = { width: 1024, height : 800 }
  page.settings.userAgent = 'Phantom.js bot'

  page.open url, (status) ->
    if status isnt 'success'
      console.log "Unable to render '#{url}'"
    else
      page.render file

    delete page
    callback url, file


url         = system.args[1]
file_name   = system.args[2]

console.log "Will render to #{file_name}"
renderUrlToFile "http://#{url}", file_name, (url, file) ->
  console.log "Rendered '#{url}' to '#{file}'"
  phantom.exit()

现在phantomjs render.coffee news.ycombinator.com hn.png在终端中输入以将 Hacker News 的首页呈现到文件hn.png中。它按预期工作。也是如此phantomjs render.coffee news.ycombinator.com hn.pdf

让我们重复我们之前对 C 程序所做的操作:

(phantomjs render.coffee news.ycombinator.com /dev/fd/5) 5> hn.pdf

它不起作用...... :(为什么?因为,如PhantomJS 的手册所述:

渲染(文件名)

将网页渲染到图像缓冲区并将其保存为指定文件。

目前,输出格式是根据文件扩展名自动设置的。支持的格式为 PNG、JPEG 和 PDF。

它失败了,仅仅是因为既不/dev/fd/2也不以等/dev/stdout结尾。.PNG

但不用担心,命名管道可以帮助您!

创建另一个命名管道,但这次使用扩展名.pdf

mkfifo my_pipe.pdf

现在,告诉它简单地cat输入到hn.pdf

cat < my_pipe.pdf > hn.pdf &

然后运行:

phantomjs render.coffee news.ycombinator.com my_pipe.pdf 

看美丽hn.pdf

显然你想做一些更复杂的事情,只是cat输出,但我相信现在你应该做什么很清楚:)


TL;博士:

  1. 创建一个命名管道,使用“.pdf”文件扩展名(所以它会让 PhantomJS 误以为它是一个 PDF 文件):

    mkfifo my_pipe.pdf
    
  2. 对文件的内容做任何你想做的事情,比如:

    cat < my_pipe.pdf > hn.pdf
    

    这简直cat就是hn.pdf

  3. 在 PhantomJS 中,渲染到这个文件/管道。

  4. 稍后,您应该移除管道:

    rm my_pipe.pdf
    
于 2012-07-15T05:19:35.220 回答
14

正如 Niko 所指出的,您可以使用renderBase64()将网页呈现到图像缓冲区并将结果作为 base64 编码的字符串返回。
但目前这仅适用于 PNG、JPEG 和 GIF。

要将 phantomjs 脚本中的某些内容写入标准输出,只需使用文件系统 API。

我对图像使用这样的东西:

var base64image = page.renderBase64('PNG');
var fs = require("fs");
fs.write("/dev/stdout", base64image, "w");

我不知道 PDF 格式是否renderBase64()会出现在 phanthomjs 的未来版本中,但作为一种解决方法,这些方面的内容可能对您有用:

page.render(output);
var fs = require("fs");
var pdf = fs.read(output);
fs.write("/dev/stdout", pdf, "w");
fs.remove(output);

outputpdf文件的路径在哪里。

于 2012-07-16T15:58:16.747 回答
2

我不知道它是否能解决您的问题,但您也可以检查renderBase64()添加到 PhantomJS 1.6 的新方法:https ://github.com/ariya/phantomjs/blob/master/src/webpage.cpp#L623

不幸的是,该功能尚未在 wiki 上记录:/

于 2012-07-15T09:07:24.607 回答