简体   繁体   English

如何使用Chrome DevTools Protocol的printToPDF修改first pageNumber或执行header或footer模板中的JS

[英]How to modify the first pageNumber or execute JS in header or footer template with Chrome DevTools Protocol's printToPDF

I'm using Headless Chrome to print out PDF files by using the printToPDF CDP method.我正在使用Headless Chrome通过printToPDF CDP 方法打印出 PDF 个文件。 If we set displayHeaderFooter parameter to true , then we can set specific page header and footer by using the parameters headerTemplate and footerTemplate .如果我们将displayHeaderFooter参数设置为true ,那么我们可以使用参数headerTemplatefooterTemplate设置特定页面 header 和页脚。 The protocol provides some HTML classes to display some information, these are: date , title , url , pageNumber , totalPages .该协议提供了一些 HTML 类来显示一些信息,它们是: datetitleurlpageNumbertotalPages

For example, we can set footerTemplate to <span class="pageNumber"></span> to display the current page number in the footer.例如,我们可以将footerTemplate设置为<span class="pageNumber"></span>以在页脚中显示当前页码。 We also need to add some style to display it properly.我们还需要添加一些样式来正确显示它。 The default header and footer settings can be found here , and the renderer C++ component is here .默认的 header 和页脚设置可以在这里找到,渲染器 C++ 组件在这里

I would like to modify the displayed pageNumber values.我想修改显示的 pageNumber 值。 My goal is to count pages from a given number.我的目标是从给定的数字开始计算页数。

The Puppeteer API documentation note that headerTemplate and footerTemplate markup have the following limitations: Puppeteer API 文档指出headerTemplatefooterTemplate标记具有以下限制:

  1. Script tags inside templates are not evaluated.不评估模板内的脚本标签。
  2. Page styles are not visible inside templates.页面 styles 在模板中不可见。

A GitHub comment provides the following: GitHub 评论提供以下内容:

<div style="font-size: 10px;">
  <div id="test">header test</div>
  <img src='http://www.chromium.org/_/rsrc/1438879449147/config/customLogo.gif?revision=3' onload='document.getElementById("test").style.color = "green";this.parentNode.removeChild(this);'/>
</div>

It says, if we use an onload attribute on an img tag, then we can run JavaScript in the templates.它说,如果我们在img标签上使用onload属性,那么我们可以在模板中运行 JavaScript。 However, I was not able to reproduce the result, what is shown on the screenshot under the snipplet.但是,我无法重现结果,即片段下方屏幕截图中显示的内容。

For example, the following JavaScript could count pages from 10:例如,以下 JavaScript 可以从 10 开始计算页数:

<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7" alt="tmpimg" 
onload="var x = document.getElementById('pn').innerHTML; var y = 10; document.getElementById('pn').innerHTML = parseInt(x) + y; this.parentNode.removeChild(this);"/>
<span id="pn" class="pageNumber"></span>

But unfortunately this script does not modify the page numbering, and I have no idea how to solve this problem.但不幸的是这个脚本没有修改页码,我不知道如何解决这个问题。 I've also tried to use pure CSS solutions, but without success.我也尝试过使用纯 CSS 解决方案,但没有成功。

Any ideas are welcome to resolve this issue.欢迎任何想法来解决这个问题。

Have you tried moving to span above the img tag?您是否尝试过跨越 img 标签?

One approach you can try is to use JavaScript to modify the pageNumber element in the DOM before calling the printToPDF method.您可以尝试的一种方法是在调用 printToPDF 方法之前使用 JavaScript 修改 DOM 中的 pageNumber 元素。 You can do this by injecting a script into the page that runs before the PDF is generated.您可以通过将脚本注入到在生成 PDF 之前运行的页面中来执行此操作。

Here is an example of how you can do this using Puppeteer:以下是如何使用 Puppeteer 执行此操作的示例:

const page = await browser.newPage();
// Navigate to the page you want to generate a PDF for
await page.goto('https://www.example.com');
// Inject a script into the page to modify the page numbering
await page.evaluate(() => {
  // Get the pageNumber element
  const pageNumberElement = document.querySelector('.pageNumber');
  // Modify the innerHTML of the element to start counting from 10
pageNumberElement.innerHTML = parseInt(pageNumberElement.innerHTML) + 10;
});
// Generate the PDF using the modified page numbering
const pdf = await page.pdf({ displayHeaderFooter: true });
// Save the PDF to a file
fs.writeFileSync('output.pdf', pdf);
await page.close();

I tried straight forward approaches to solve this problem and they didn't work.我尝试了直接的方法来解决这个问题,但没有奏效。 Even obscure apis like CSS expressions and counters don't work to solve this.即使像 CSS 表达式和计数器这样晦涩的 API 也无法解决这个问题。 Fortunately there seems to be a simple enough work around.幸运的是,似乎有一个足够简单的解决方法。

We print each page separately using the pageRange parameter and then combine all the pages to generate the required pdf. This enables us to print each header/footer if it were a function of the pageNumber .我们使用pageRange参数分别打印每个页面,然后组合所有页面以生成所需的 pdf。这使我们能够打印每个页眉/页脚,如果它是 pageNumber 的pageNumber For example:例如:

const footerTemplate = function (pageNumber) {
    return `<div>Page number: ${pageNumber + 24}</div>`;
};

We need to iterate over each page and print it.我们需要遍历每一页并打印它。

const printPage = function (pageNumber) {
    return {
        ...
        path: `html-page-${pageNumber}.pdf`,
        footerTemplate: footerTemplate(pageNumber),
        pageRanges: String(pageNumber)
    };
};


(async function () {
    ...
    const page = await browser.newPage();
    var pageNumber = 1;
    try {
        while (pageNumber > 0) {
            await page.pdf(printPage(pageNumber));
            pageNumber += 1;
        }
    } catch (e) {
    } finally {
       // Merge and clean up
    }
})();

There is no trivial way to determine the total number of pages to print.没有简单的方法来确定要打印的总页数。 So we don't know when to stop.所以我们不知道什么时候停止。 Fortunately, Chrome sends an error when we try to print a page that's out of range.幸运的是,当我们尝试打印超出范围的页面时, Chrome 会发送错误 So we can use that to stop our printing.所以我们可以用它来停止打印。

Attached below is a working sample with page numbers offset by 24. Run with dependencies: fs , pdf-merger-js and puppeteer .下面附上了一个工作示例,页码偏移了 24。使用依赖项运行: fspdf-merger-jspuppeteer

 const puppeteer = require("puppeteer"); const PDFMerger = require('pdf-merger-js'); const fs = require("fs"); const footerTemplate = function (pageNumber) { return `<div style="font-size: 10px; display: flex; flex-direction: row; justify-content: space-between; width: 100%" id='template'> <div>Page number: ${pageNumber + 24}</div> </div>`; }; const mergePdfs = async function (totalPages, fileName) { var merger = new PDFMerger(); for (var pageNumber = 1; pageNumber < totalPages; pageNumber++) { await merger.add(`html-page-${pageNumber}.pdf`); } await merger.save(fileName); }; const cleanup = function (totalPages) { for (var pageNumber = 1; pageNumber < totalPages; pageNumber++) { var path = `html-page-${pageNumber}.pdf` fs.rmSync(path); } }; const printPage = function (pageNumber) { return { path: `html-page-${pageNumber}.pdf`, format: 'Letter', printBackground: true, displayHeaderFooter: true, footerTemplate: footerTemplate(pageNumber), pageRanges: String(pageNumber), margin: { top: '1in', right: '0in', bottom: '1in', left: '0in' } }; }; (async function () { const browser = await puppeteer.launch({ ignoreHTTPSErrors: true, dumpio: true, headless: true }); const page = await browser.newPage(); await page.goto('http://worrydream.com/KillMath/'); var pageNumber = 1; try { while (pageNumber > 0) { await page.pdf(printPage(pageNumber)); pageNumber += 1; } } catch (e) { await mergePdfs(pageNumber, 'html-page.pdf'); cleanup(pageNumber); } await browser.close(); })();

You can modify the first page number or execute JavaScript in the header or footer template of a webpage when using the Chrome DevTools Protocol's printToPDF method by using the headerTemplate and footerTemplate options.使用Chrome DevTools Protocol的printToPDF方法时,可以通过headerTemplate和footerTemplate选项修改页码或执行header或页脚模板中的JavaScript。 These options allow you to specify a string that will be used as the HTML template for the header and footer of the PDF.这些选项允许您指定一个字符串,该字符串将用作 header 的 HTML 模板和 PDF 的页脚。

To set the first page number, you can use the pageNumber option and set it to the desired number.要设置第一个页码,您可以使用 pageNumber 选项并将其设置为所需的页码。

Here is an example of how you can use these options to modify the first page number and execute JavaScript in the header and footer templates:这是一个示例,说明如何使用这些选项修改首页页码并在 header 和页脚模板中执行 JavaScript:

const { CDP } = require('chrome-remote-interface');

(async function() {
    const client = await CDP();

    const { Page } = client;

    await Page.enable();

    // Define the header and footer templates
    const headerTemplate = `
        <div>
            <p>My custom header</p>
            <script>
                document.querySelector('p').innerHTML += ' - Page ' + document.querySelector('#pageNumber').textContent;
            </script>
        </div>
    `;

    const footerTemplate = `
        <div>
            <p>My custom footer</p>
            <script>
                document.querySelector('p').innerHTML += ' - Page ' + document.querySelector('#pageNumber').textContent;
            </script>
        </div>
    `;

    // Print the PDF
    const pdf = await Page.printToPDF({
        displayHeaderFooter: true,
        headerTemplate: headerTemplate,
        footerTemplate: footerTemplate,
        pageNumber: 1,
    });

    // Close the connection
    client.close();
})();

Note: This is just an example, you should replace the url and other options with the one you need.注意:这只是一个示例,您应该将 url 和其他选项替换为您需要的选项。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用 Puppeteer 和 Chrome DevTools 协议修改请求标头? (可能是JS语法问题) - How to modify request headers using Puppeteer & Chrome DevTools Protocol? (Possibly a JS syntax issue) 如何重定向到chrome-devtools://协议? - How to redirect to the chrome-devtools:// protocol? 在Chrome Devtools协议中,什么是`injectScriptId`? - In Chrome Devtools Protocol, what is `injectedScriptId`? Chrome DevTools 协议:如何获取节点的单击事件处理程序名称 - Chrome DevTools Protocol: How to get click event handler name of a Node 如何在没有 Puppeteer 的情况下通过 DevTools 协议模拟 Chrome window 中的点击? - How to simulate clicks in a Chrome window via the DevTools Protocol without Puppeteer? Chrome DevTools 协议:如何取消订阅 Page.javascriptDialogOpening 事件 - Chrome DevTools Protocol: How to unsubscribe Page.javascriptDialogOpening event 如何将JS源代码映射添加到Chrome devtools? - How to add JS source map into Chrome devtools? CKEditor中的页眉和页脚模板 - Header and Footer template in CKEditor Header 和页脚先加载 - Header and Footer load first 如何在 Chrome DevTools 中调试 ajax 请求中加载的 js - How to debug js loaded in ajax request in Chrome DevTools
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM