Изменение заголовка/нижнего колонтитула по умолчанию при печати в PDF

Я пытаюсь использовать Google Chrome в качестве замены PhantomJS для рендеринга HTML в PDF. До сих пор это работало хорошо для меня. Единственная проблема, которую я имею, что я не нашел никакого способа перевести следующий код PhantomJS:

page.paperSize = {
  footer: {
    contents: phantom.callback(function(pageNum, numPages) {
      return "Power by MyWebsite. Created on "+formatDate(new Date())+"<span style='float:right'>" + pageNum + " / " + numPages + "</span>";
    })
  }
}

Format date-это та же функция, что и в вопросе как отформатировать дату JavaScript

однако я не нашел способа воспроизвести это поведение в Google Chrome без головы. Я использую удаленный интерфейс Chrome (CDP) от https://github.com/cyrus-and/chrome-remote-interface

это набросок моего кода удаленного интерфейса chrome:

return new Promise(async function (resolve, reject) {
    const url = "<MyURL here>";
    const [tab] = await Cdp.List()
    const client = await Cdp({ host: '127.0.0.1', target: tab });
    await Promise.all([
       Network.enable(),
       Page.enable()
    ]);

    Page.loadEventFired(function () { 
         setTimeout(function () {
             resolve(Page.printToPDF({displayHeaderFooter:true}))); //https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF
         }, 3000);
    });
    await Page.navigate({ url }); 
};

Я получаю PDF просто отлично, но могу получить только верхние и нижние колонтитулы Chrome по умолчанию. Любой способ изменить их?

Примечание: я понимаю, что могу использовать JavaScript на своей странице для добавления элементов в нижнюю часть каждой страницы, но я бы предпочел изменить нижний колонтитул, который добавляется браузером, когда экспорт / печать как я обнаружил, гораздо надежнее правильно разместить и не вызывать странного перетекания других divs на странице.

6 ответов


Это обновление/ответ на вопрос. Начиная с Chromium 64 можно использовать headerTemplate и footerTemplate параметры printToPDF

используя удаленный интерфейс chrome вот пример кода, который должен работать:

return new Promise(async function (resolve, reject) {
    const url = "<MyURL here>";
    const [tab] = await Cdp.List()
    const client = await Cdp({ host: '127.0.0.1', target: tab });
    await Promise.all([
       Network.enable(),
       Page.enable()
    ]);

    Page.loadEventFired(function () { 
         setTimeout(function () {
    //https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF
             resolve(Page.printToPDF({
                  displayHeaderFooter:true,
                  footerTemplate: "<span class='pageNumber'> of <span class='totalPages'>"
             }))); 
         }, 3000);
    });
    await Page.navigate({ url }); 
};

можно создавать пользовательские верхние и нижние колонтитулы с помощью <header> и <footer> теги. Я использую это для создания PDF-файлов с использованием Chrome Headless. Я не тестировал его в Firefox, IE и т. д...

<header>
  Custom Header
  <img src="http://imageurl.com/image.jpg"/>
</header>
<div class="content">Page Content - as long as you want</div>
<footer>
  Footer Content
</footer>

CSS

@page {
  margin: 0;
}
@media print {
  footer {
    position: fixed;
    bottom: 0;
  }
  header {
    position: fixed;
    top: 0;
  }
}

на @page { margin: 0 } удаляет верхний и Нижний колонтитулы по умолчанию.

надеюсь, что это помогает.


есть два решения вашей проблемы

A) нажмите на заголовок chrome, не оставляя поля:

 @page { 
     margin: 0;
     size: auto;
 }

или

 @media print {
   @page { margin: 0; }
   body { margin: 1.6cm; }
 }

B) первоначально решение Firefox, которое должно мир для Chrome

 <html moznomarginboxes mozdisallowselectionprint>

пример:

<!DOCTYPE html>
<html moznomarginboxes mozdisallowselectionprint>
<head>
<title>Print PDF without header</title>
<style>
@media print {
    @page { margin: 0; }
    body { margin: 1.6cm; }
}
</style>
</head>
<body>
<p>Some Text in Paragraph to print!</p>
<a href="javascript:print()">Print</a>
</body>
</html>

по данным этот форум, в настоящее время нет способа сделать это в google chrome. Все, что вы можете сделать, это включить или выключить верхний/нижний колонтитул. На это указывает комментарий:

В настоящее время нет способа редактировать заголовок при печати документа. В настоящее время можно включить или выключить только верхний и Нижний колонтитулы, которые включают дату, имя веб-страницы, URL-адрес страницы и количество страниц печатаемого документа. Вы можете проверить Интернет-магазин Chrome, чтобы узнать, есть ли какие-либо удобные сторонние расширения, которые вы можете установить на Chrome, которые могут соответствовать тому, что вы ищете с точки зрения печати -- источник

могут быть сторонние расширения, чтобы получить функциональность, которую вы ищете, или как вы предлагаете, вы можете использовать JavaScript, чтобы добавить элементы, которые вы хотите напечатать.


Если вы действительно хотите сделать это не хакерским способом, вам нужно перейти к протоколу chrome devtools, так как интерфейс командной строки не поддерживает много (aka --print-to-pdf Это все, что вы получаете, нет параметров печати)

протокол документирован здесь:https://chromedevtools.github.io/devtools-protocol/tot

клиентские библиотеки доступны на многих языках, доступных через менеджеры пакетов.

вот несколько примеров, которые я собрал для продемонстрировать использование:

на Page.printToPDF метод протокола поддерживает аргументы для передачи пользовательской разметки для верхнего и нижнего колонтитулов.

В основном, протокол определяется в protocol.json, которые клиентские библиотеки потребляют для создания классы/методы для использования в любом приложении. Эти методы инкапсулируют связи нижнего уровня с протоколом.

все, что мне нужно было сделать, это установить пакет клиентской библиотеки (через npm или composer), убедиться, что chrome установлен, написать немного кода, и я генерирую PDF-файлы без верхнего/нижнего колонтитула! Фантастический.


для тех из вас, кто просто хочет что-то, что работает из коробки, я хотел бы поделиться своим сценарием, который я написал сегодня, на основе ответа apokryfos.

сначала вам нужно установить зависимости

yarn global add chrome-remote-interface

Далее вам нужно запустить безголовый хром с включенным портом отладки

chromium-browser --headless --disable-gpu --run-all-compositor-stages-before-draw --remote-debugging-port=9222

теперь вам нужно сохранить мой скрипт, т. е. как print-via-chrome.js:

#!/usr/bin/env node

const homedir = require('os').homedir();
const CDP = require(homedir+'/.config/yarn/global/node_modules/chrome-remote-interface/');
const fs = require('fs');

const port = process.argv[2];
const htmlFilePath = process.argv[3];
const pdfFilePath = process.argv[4];

(async function() {

        const protocol = await CDP({port: port});

        // Extract the DevTools protocol domains we need and enable them.
        // See API docs: https://chromedevtools.github.io/devtools-protocol/
        const {Page} = protocol;
        await Page.enable();

        Page.loadEventFired(function () {
                console.log("Waiting 100ms just to be sure.")
                setTimeout(function () {
                        //https://chromedevtools.github.io/devtools-protocol/tot/Page/#method-printToPDF
                        console.log("Printing...")
                        Page.printToPDF({
                                displayHeaderFooter: true,
                                headerTemplate: '<div></div>',
                                footerTemplate: '<div class="text center"><span class="pageNumber"></span></div>',
                                //footerTemplate: '<div class="text center"><span class="pageNumber"></span> of <span class="totalPages"></span></div>'
                        }).then((base64EncodedPdf) => {
                                fs.writeFileSync(pdfFilePath, Buffer.from(base64EncodedPdf.data, 'base64'), 'utf8');
                                console.log("Done")
                                protocol.close();
                        });
                }, 100);
        });

        Page.navigate({url: 'file://'+htmlFilePath});
})();

сделав его исполняемым с chmod +x print-via-chrome.js вы должны иметь возможность конвертировать html-файлы в pdf-файлы, например:

./print-via-chrome.js 9222 my.html my.pdf

Не забудьте выйти из хрома после того, как вы закончили трансформацию.

Я уверен, что это решение далеко не идеально, но, по крайней мере, оно работает, и поскольку я видел много вопросов об этой функции и должен был инвестировать несколько часов своего времени, чтобы заставить его работать, я хотел поделиться своим решением. Некоторые проблемы у меня были связаны с заголовка - и footerTemplates как кажется, что пустые шаблоны не заменяют существующие (вам нужно <div></div>) и в то время как по-разному документированные новые шаблоны не появляются в видимой области, пока не обернуты <div class="text center"> или что-то подобное.