【问题标题】:HTML to PDF with Node.js使用 Node.js 将 HTML 转为 PDF
【发布时间】:2013-01-11 04:56:19
【问题描述】:

我正在寻找我的网站网页的可打印pdf 版本。像express.render() 这样的东西只会将页面呈现为pdf

有谁知道这样做的节点模块?

如果没有,您将如何实施?我见过一些方法谈论使用无头浏览器,如phantom.js,但不确定流程是什么。

【问题讨论】:

  • 我希望这仍然是相关的,但现在有这个网站simpe.li,它有一些预定义的模板,您可以选择和使用。在某些情况下可能很有用。

标签: node.js express pdf-generation


【解决方案1】:

扩展穆斯塔法的回答。

A) 安装http://phantomjs.org/ 然后

B) 安装幻象节点模块https://github.com/amir20/phantomjs-node

C) 这里是一个渲染 pdf 的例子

var phantom = require('phantom');   

phantom.create().then(function(ph) {
    ph.createPage().then(function(page) {
        page.open("http://www.google.com").then(function(status) {
            page.render('google.pdf').then(function() {
                console.log('Page Rendered');
                ph.exit();
            });
        });
    });
});

PDF 的输出:

编辑: Silent printing that PDF

java -jar pdfbox-app-2.0.2.jar PrintPDF -silentPrint C:\print_mypdf.pdf

【讨论】:

  • 这也加载 CSS 吗?当我渲染页面时,会显示文本但没有 CSS。
  • 此解决方案的一个问题是,您将无法获得网页上的可点击链接。这与截屏并将图像嵌入 PDF 相同。如果这对您有用,那么这是一个很好的解决方案。
  • 这个模块phantomjs-node在NPM上不存在,对于小于5.0的节点v使用npm install phantom@2 -S或者对于5.0或更高版本的节点使用npm install phantom -S
  • 当我将 html 转换为 pdf 时,html 中有 4-5 页。我想在两页之间使用分页符。这是我要转换为 pdf 的 url 链接。 "ishtech.xyz//web/#/reports_view?StartDate=11/14/…"
  • PhantomJS 不再是一个活跃的项目
【解决方案2】:

Phantom.js 是一个无头 webkit 服务器,它将加载任何网页并将其呈现在内存中,尽管您可能看不到它,但有一个屏幕捕获功能,您可以将当前视图导出为PNG、PDF、JPEG 和 GIF。看看这个example from phantom.js documentation

【讨论】:

    【解决方案3】:

    如果您想将 HTML 导出为 PDF。你有很多选择。甚至没有节点

    选项 1:在您的 html 页面上有一个调用 window.print() 函数的按钮。使用浏览器原生 html 转 pdf。使用媒体查询使您的 html 页面在 pdf 上看起来不错。并且您还可以在事件之前和之后进行打印,以便在打印之前对页面进行更改。

    选项 2。htmltocanvasrasterizeHTML。将您的 html 转换为 canvas ,然后在 canvas 对象上调用 toDataURL() 以获取图像。并使用 jsPDF 之类的 JavaScript 库将该图像添加到 PDF 文件中。这种方法的缺点是 pdf 无法编辑。如果你想从 PDF 中提取数据,有不同的方法。

    选项 3。@Jozzhard 回答

    【讨论】:

    • 哪些浏览器有内置的 html 到 pdf 选项?我现在只能在 Chrome 中看到它。
    【解决方案4】:

    尝试使用 Puppeteer 从 HTML 创建 PDF

    示例来自https://github.com/chuongtrh/html_to_pdf

    https://github.com/GoogleChrome/puppeteer

    【讨论】:

    • puppeteer 现在比 phantom 更有意义,因为后者已被弃用,而前者有更好、更稳定的 api。
    • Puppeteer 是使用现代标记从 HTML 创建 PDF 的唯一方法。
    【解决方案5】:

    我找到的最佳解决方案是 html-pdf。它很简单,并且适用于大 html。

    https://www.npmjs.com/package/html-pdf

    就这么简单:

        pdf.create(htm, options).toFile('./pdfname.pdf', function(err, res) {
            if (err) {
              console.log(err);
            }
        });
    

    【讨论】:

    • 太棒了。如果将它与 requestify 结合使用,它也适用于外部 URL。
    • 是否考虑了css?上课?
    • @gabodev77,是的。
    • 是否支持style标签?
    • 仅供参考 - 这个包自 2017 年以来没有更新,并且有一个严重的漏洞 npmjs.com/advisories/1095 可能最好使用另一个选项 :)
    【解决方案6】:

    我用html-pdf

    易于使用,不仅可以将 pdf 保存为文件,还可以将 pdf 内容通过管道传输到 WriteStream(因此我可以将其直接流式传输到 Google 存储以保存我的报告)。

    使用 css + 图片

    它考虑了css。我面临的唯一问题 - 它忽略了我的图像。我找到的解决方案是将src属性值中的url替换为base64,例如

    <img src="data:image/png;base64,iVBOR...kSuQmCC">

    您可以使用您的代码或使用其中一种在线转换器,例如https://www.base64-image.de/

    从html片段+css编译有效的html代码

    1. 我必须获取我的html 文档的片段(我刚刚在 jQuery 选择器上应用了 .html() 方法)。
    2. 那我已经阅读了相关css文件的内容。

    使用这两个值(相应地存储在变量htmlcss 中)我使用Template string 编译了一个有效的html 代码

    var htmlContent = `
    <!DOCTYPE html>
    <html>
      <head>
        <style>
          ${css}
        </style>
      </head>
      <body id=direct-sellers-bill>
        ${html}
      </body>
    </html>`
    

    并将其传递给html-pdfcreate 方法。

    【讨论】:

    • 可以从无效的url下载html-pdf,例如从谷歌Chrome扩展/gtar.html页面吗?
    • 你如何期望任何系统从无效的 url 中获取任何东西?
    • 可以从文件中加载图像,只需设置正确的位置并使用file:// 前缀即可。所以,你说模板&lt;img src="static/logo.png"&gt;,然后在转换之前,通过前缀const html = htmlOrig.replace(new RegExp('&lt;img src="', 'g'), `&lt;img src="${base}`);准备模板
    【解决方案7】:

    对于那些不想在他们的服务器上安装 PhantomJS 和 Chrome/Firefox 实例的人 - 或者因为PhantomJS project is currently suspended,这里有一个替代方案。

    您可以将转换外部化为 API 来完成这项工作。许多存在并且变化,但您将获得的是具有最新功能的可靠服务(我认为 CSS3、Web 字体、SVG、Canvas 兼容)。

    例如,使用PDFShift(免责声明,我是创始人),您只需使用request 包即可:

    const request = require('request')
    request.post(
        'https://api.pdfshift.io/v2/convert/',
        {
            'auth': {'user': 'your_api_key'},
            'json': {'source': 'https://www.google.com'},
            'encoding': null
        },
        (error, response, body) => {
            if (response === undefined) {
                return reject({'message': 'Invalid response from the server.', 'code': 0, 'response': response})
            }
            if (response.statusCode == 200) {
                // Do what you want with `body`, that contains the binary PDF
                // Like returning it to the client - or saving it as a file locally or on AWS S3
                return True
            }
    
            // Handle any errors that might have occured
        }
    );
    

    【讨论】:

      【解决方案8】:

      从外部 URL 创建 PDF

      这是对先前答案的改编,它利用 html-pdf,但也将它与 requestify 结合使用,因此它可以与外部 URL 一起使用:

      安装你的依赖项

      npm i -S html-pdf requestify
      

      然后,创建脚本:

      //MakePDF.js
      
      var pdf = require('html-pdf');
      var requestify = require('requestify');
      var externalURL= 'http://www.google.com';
      
      requestify.get(externalURL).then(function (response) {
         // Get the raw HTML response body
         var html = response.body; 
         var config = {format: 'A4'}; // or format: 'letter' - see https://github.com/marcbachmann/node-html-pdf#options
      
      // Create the PDF
         pdf.create(html, config).toFile('pathtooutput/generated.pdf', function (err, res) {
            if (err) return console.log(err);
            console.log(res); // { filename: '/pathtooutput/generated.pdf' }
         });
      });
      

      然后你只需从命令行运行:

      node MakePDF.js
      

      观看为您创建的美化像素完美 PDF(免费!)

      【讨论】:

      • 有一个问题导致 html-pdf 有时只能成功制作 PDF - github.com/marcbachmann/node-html-pdf/issues/181
      • 如何将创建的 PDF 直接渲染到浏览器而不需要先存储文件?
      • 使用二进制流可以做到。理论上它不会被保存,只是直接通过管道传输到浏览器。虽然使用节点,但我只能通过首先保存临时 pdf,然后获取二进制流,下载二进制流,然后删除临时 pdf 来使其工作。
      • 我收到来自 html-pdf 的错误 - ReferenceError: 找不到变量 $。这可能是因为我正在加载的页面有需要执行的 javascript 吗?任何想法都会有所帮助。
      • @TetraDev:我需要限制生成 1 页 pdf,会有什么变化?
      【解决方案9】:

      在我看来,最好的方法是通过 API 来实现,这样您就不会在运行非托管代码的应用程序中添加大量复杂的依赖项,而这些依赖项需要经常更新。

      这是一个简单的方法,每月 800 个请求免费:

      var CloudmersiveConvertApiClient = require('cloudmersive-convert-api-client');
      var defaultClient = CloudmersiveConvertApiClient.ApiClient.instance;
      
      // Configure API key authorization: Apikey
      var Apikey = defaultClient.authentications['Apikey'];
      Apikey.apiKey = 'YOUR API KEY';
      
      
      
      var apiInstance = new CloudmersiveConvertApiClient.ConvertWebApi();
      
      var input = new CloudmersiveConvertApiClient.HtmlToPdfRequest(); // HtmlToPdfRequest | HTML to PDF request parameters
      input.Html = "<b>Hello, world!</b>";
      
      
      var callback = function(error, data, response) {
        if (error) {
          console.error(error);
        } else {
          console.log('API called successfully. Returned data: ' + data);
        }
      };
      apiInstance.convertWebHtmlToPdf(input, callback);
      

      使用上述方法,您还可以根据需要在本地或您自己的基础架构上安装 API。

      【讨论】:

        【解决方案10】:

        使用html-pdf

        var fs = require('fs');
        var pdf = require('html-pdf');
        var html = fs.readFileSync('./test/businesscard.html', 'utf8');
        var options = { format: 'Letter' };
        
        pdf.create(html, options).toFile('./businesscard.pdf', function(err, res) {
          if (err) return console.log(err);
          console.log(res); // { filename: '/app/businesscard.pdf' } 
        });
        

        【讨论】:

          【解决方案11】:

          如果您来到这里寻找一种从 Express 中的视图模板制作 PDF 的方法,我和一位同事制作了 express-template-to-pdf

          它允许您从您在 Express 中使用的任何模板生成 PDF - Pug、Nunjucks 等等。

          它依赖于 html-pdf 并且被编写成在你的路由中使用,就像你使用 res.render 一样:

          const pdfRenderer = require('@ministryofjustice/express-template-to-pdf')
          
          app.set('views', path.join(__dirname, 'views'))
          app.set('view engine', 'pug')
          
          app.use(pdfRenderer())
          
          

          如果您使用过 res.render,那么使用它应该很明显:

          app.use('/pdf', (req, res) => {
              res.renderPDF('helloWorld', { message: 'Hello World!' });
          })
          

          您可以将选项传递给 html-pdf 以控制 PDF 文档页面大小等

          仅以他人的出色工作为基础。

          【讨论】:

            【解决方案12】:

            除了@Jozzhart Answer,还可以制作本地html;用快递送达;并使用 phantom 从中制作 PDF;像这样:

            const exp = require('express');
            const app = exp();
            const pth = require("path");
            const phantom = require('phantom');
            const ip = require("ip");
            
            const PORT = 3000;
            const PDF_SOURCE = "index"; //index.html
            const PDF_OUTPUT = "out"; //out.pdf
            
            const source = pth.join(__dirname, "", `${PDF_SOURCE}.html`);
            const output = pth.join(__dirname, "", `${PDF_OUTPUT}.pdf`);
            
            app.use("/" + PDF_SOURCE, exp.static(source));
            app.use("/" + PDF_OUTPUT, exp.static(output));
            
            app.listen(PORT);
            
            let makePDF = async (fn) => {
                let local = `http://${ip.address()}:${PORT}/${PDF_SOURCE}`;
                phantom.create().then((ph) => {
                    ph.createPage().then((page) => {
                        page.open(local).then(() =>
                            page.render(output).then(() => { ph.exit(); fn() })
                        );
                    });
                });
            }
            
            makePDF(() => {
                console.log("PDF Created From Local File");
                console.log("PDF is downloadable from link:");
                console.log(`http://${ip.address()}:${PORT}/${PDF_OUTPUT}`);
            });
            

            并且 index.html 可以是任何东西:

            <h1>PDF HEAD</h1>
            <a href="#">LINK</a>
            

            结果:

            【讨论】:

              【解决方案13】:
              const fs = require('fs')
              const path = require('path')
              const utils = require('util')
              const puppeteer = require('puppeteer')
              const hb = require('handlebars')
              const readFile = utils.promisify(fs.readFile)
              
              async function getTemplateHtml() {
              
                  console.log("Loading template file in memory")
                  try {
                      const invoicePath = path.resolve("./invoice.html");
                      return await readFile(invoicePath, 'utf8');
                  } catch (err) {
                      return Promise.reject("Could not load html template");
                  }
              }
              
              
              async function generatePdf() {
              
                  let data = {};
              
                  getTemplateHtml()
                      .then(async (res) => {
                          // Now we have the html code of our template in res object
                          // you can check by logging it on console
                          // console.log(res)
              
                          console.log("Compiing the template with handlebars")
                          const template = hb.compile(res, { strict: true });
                          // we have compile our code with handlebars
                          const result = template(data);
                          // We can use this to add dyamic data to our handlebas template at run time from database or API as per need. you can read the official doc to learn more https://handlebarsjs.com/
                          const html = result;
              
                          // we are using headless mode 
                          const browser = await puppeteer.launch();
                          const page = await browser.newPage()
              
                          // We set the page content as the generated html by handlebars
                          await page.setContent(html)
              
                          // we Use pdf function to generate the pdf in the same folder as this file.
                          await page.pdf({ path: 'invoice.pdf', format: 'A4' })
              
                          await browser.close();
                          console.log("PDF Generated")
              
                      })
                      .catch(err => {
                          console.error(err)
                      });
              }
              
              generatePdf();
              

              【讨论】:

                【解决方案14】:

                https://www.npmjs.com/package/dynamic-html-pdf

                我用的是dynamic-html-pdf,这个很简单,也可以将动态变量传递给html。

                var html = fs.readFileSync('./uploads/your-html-tpl.html', 'utf8');
                var options = {
                    format: "A4",
                    orientation: "portrait"
                    // border: "10mm"
                };
                var document = {
                    type: 'file',     // 'file' or 'buffer'
                    template: html,
                    context: {
                       'your_key':'your_values'
                    },
                    path: '/pdf/1.pdf'   // pdf save path
                };
                
                pdf.create(document, options)
                .then(res => {
                    console.log(res)
                }).catch(error => {
                    console.error(error)
                });
                

                在 html 上你可以使用 {{your_key}}

                【讨论】:

                  【解决方案15】:

                  你也可以使用pdf节点创建器包

                  包裹网址 - https://www.npmjs.com/package/pdf-creator-node

                  【讨论】:

                    猜你喜欢
                    • 2019-08-19
                    • 1970-01-01
                    • 2019-10-24
                    • 1970-01-01
                    • 2015-03-02
                    • 1970-01-01
                    • 2015-09-27
                    • 1970-01-01
                    相关资源
                    最近更新 更多