Измените верхний/нижний колонтитул по умолчанию при печати в 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>";
    })
  }
}

Дата форматирования - та же самая функция, что и в вопросе. Как форматировать дату 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 на своей странице, чтобы добавлять элементы к нижней части каждой страницы, но я бы предпочел изменить нижний колонтитул, добавленный браузером при экспорте/печати, поскольку я нашел его намного более надежным правильно размещаться и не вызывать каких-либо странных перетоков других разделов на странице.

Ответ 1

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

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

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 }); 
};

Ответ 2

Можно создавать пользовательские верхние и нижние колонтитулы с помощью тегов <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 } удаляет верхний и нижний колонтитулы по умолчанию.

Надеюсь, это поможет.

Ответ 3

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

A) Выдвиньте хром-заголовок, не оставляя поля:

 @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>

Ответ 4

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

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

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

Ответ 5

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

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

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

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

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

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

Все, что мне нужно было сделать, это установить пакет клиентской библиотеки (через npm или композитор), убедиться, что Chrome установлен, написать небольшой код, и я создаю pdf файлы без заголовка/нижнего колонтитула! Фантастика.

Ответ 6

Для тех из вас, кто просто хочет что-то, что работает из коробки, я хотел бы поделиться своим сценарием, который я написал сегодня, на основе ответа 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

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

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