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