Изменение заголовка/нижнего колонтитула по умолчанию при печати в 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
клиентские библиотеки доступны на многих языках, доступных через менеджеры пакетов.
вот несколько примеров, которые я собрал для продемонстрировать использование:
- PHP:https://github.com/jehoshua02/experiment-chrome-devtools-protocol-php
- узел:https://github.com/jehoshua02/experiment-chrome-remote-interface-node
на 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">
или что-то подобное.