Від jsPDF до Chrome: вирішення складної задачі рендерингу PDF з таблицями
Створення PDF-документів із таблицями в JavaScript може стати складним викликом, особливо коли потрібно дотримуватися гарного дизайну та враховувати різні розміри сторінок, форматування, шрифти тощо. Є декілька інструментів і бібліотек, що дають змогу генерації PDF у браузері або на сервері, серед найпопулярніших — jsPDF, а також підхід через рендеринг HTML у Chrome (headless). У цій статті розглянемо, які нюанси варто врахувати під час складного рендерингу, як створювати таблиці за допомогою jsPDF і коли краще використовувати інші підходи, такі як headless Chrome.
Чому складно генерувати PDF із таблицями
Проблеми з flow-логікою
Більшість бібліотек JavaScript не мають автоматичного “перенесення” рядків таблиці на наступну сторінку з урахуванням заголовків і футерів.
Обмежений набір інструментів для дизайну
jsPDF пропонує API для малювання ліній, тексту тощо, але щоб зробити адаптивну таблицю або “вписати” її в сторінку, потрібно писати додаткову логіку.
Шрифти та локалізація
Якщо потрібно використовувати шрифти з Unicode (кирилиця, ієрогліфи), доведеться підключати додаткові файли шрифтів і налаштовувати бібліотеку.
Довгі списки даних
Якщо обсяг даних великий (декілька сотень чи тисяч рядків), формування PDF може бути занадто повільним у браузері або вимагати нетривіальних оптимізацій.
Підходи до рендерингу PDF із таблицями
- jsPDF і додаткові плагіни
jsPDF — досить популярна бібліотека, яка дає змогу “з нуля” малювати на PDF-канві. Для таблиць існує плагін jspdf-autotable, що спрощує побудову таблиці, відстеження ширини та висоти колонок, перенесення рядків тощо.
Приклад використання jsPDF з плагіном autotable
import jsPDF from 'jspdf';
import 'jspdf-autotable';
function generatePdf(data) {
const doc = new jsPDF('p', 'pt', 'a4');
const columns = [
{ header: 'ID', dataKey: 'id' },
{ header: 'Name', dataKey: 'name' },
{ header: 'Email', dataKey: 'email' },
];
doc.autoTable({
head: [columns.map(col => col.header)],
body: data.map(row => columns.map(col => row[col.dataKey])),
startY: 50,
margin: { left: 40, right: 40 },
theme: 'grid',
didDrawPage: (dataArg) => {
doc.text('Report', 40, 30);
},
});
doc.save('table.pdf');
}
Переваги та недоліки
- Перевага: Можна працювати прямо в браузері, швидко створювати прості таблиці.
- Недолік: Якщо таблиця дуже складна (багаторівневі заголовки, колSPAN, rowSPAN), треба писати кастомний код.
- Використання headless Chrome
Підхід через headless Chrome (наприклад, за допомогою Puppeteer) дозволяє рендерити HTML-сторінку до PDF. Це означає, що:
- Можемо використати звичайну верстку (HTML+CSS) для побудови таблиці.
- Chrome сам визначить розміри сторінок, зробить перенесення на наступні сторінки, відобразить усі стилі.
Приклад використання Puppeteer для генерації PDF:
const puppeteer = require('puppeteer');
async function generatePdfUsingChrome(htmlContent) {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setContent(htmlContent, { waitUntil: 'networkidle0' });
await page.pdf({
path: 'output.pdf',
format: 'A4',
printBackground: true,
});
await browser.close();
}
Що робимо:
- Формуємо HTML-шаблон із таблицями (можна використати CSS для стилювання).
- Передаємо цей HTML у page.setContent().
- Викликаємо page.pdf(), що створює готовий PDF-файл.
Коли обирати headless Chrome
- Потрібна складна верстка з багатьма стилями, адаптивна таблиця, кілька сторінок, колSPAN/rowSPAN, заголовки при перенесенні сторінки.
- Маємо серверну частину (наприклад, Node.js), що може запускати Puppeteer.
- Хочемо full-результат, максимально схожий на друковану версію веб-сторінки.
Поради щодо продуктивності та якості
Оптимізація для jsPDF
- Менше операцій
Якщо таблиця дуже довга, генерування кожного рядка у режимі реального часу може бути повільним. Варто підготувати дані та викликати autoTable один раз. - Правильні шрифти
Якщо потрібна кирилиця чи інші символи, слід підключити шрифт: doc.addFileToVFS(‘MyFont.ttf’, fontData), doc.addFont(‘MyFont.ttf’, ‘MyFont’, ‘normal’). Інакше символи можуть відображатися некоректно.
Для headless Chrome
- Використовуйте CSS-правила для друку
Наприклад: @page { size: A4; margin: 1cm; } table { page-break-inside: auto; } tr { page-break-inside: avoid; } - Налаштуйте page.pdf
path, format, printBackground (true або false), щоб отримати потрібний результат. - Можливе використання cache, якщо потрібно часто генерувати один і той самий звіт (змінювати тільки дані).
Обробка дуже великих таблиць
Якщо таблиця має тисячі рядків, обидва методи можуть уповільнитися. Можливі варіанти:
- Розбивати таблицю на кілька сегментів (пагінація).
- Якщо користувачу потрібен повний звіт, генерувати офлайн на сервері, а потім віддавати посилання на результат.
Якщо таблиця настільки велика, що PDF стає десятками мегабайт, можливо слід запропонувати інший формат (Excel).
Приклади змішаних підходів
- Використання jsPDF для формування титульної сторінки, а потім рендеринг довгої таблиці через headless Chrome, і об’єднання двох PDF (через PDF-кидану бібліотеку).
- Генерування HTML у браузері і передача його на сервер, де запускається Puppeteer, повертаючи PDF.
Висновок
Створення PDF із таблицями може бути складним завданням, особливо коли потрібна підтримка багатьох стилів, перенесення сторінок, локалізовані шрифти. jsPDF дозволяє швидко генерувати документи безпосередньо в браузері, але вимагає додаткових плагінів та логіки для складних таблиць. Якщо верстка дуже складна, може стати у пригоді підхід через headless Chrome, де верстка робиться звичайним HTML+CSS і Puppeteer генерує кінцевий PDF.
Обидва методи мають свої переваги й недоліки, вибір залежить від конкретних вимог до дизайну, обсягу даних, середовища виконання і потреби в інтерактивності. Важливо врахувати продуктивність (особливо для великих таблиць) та правильну обробку шрифтів і локалізації. Тоді і jsPDF, і Chrome headless стануть потужними інструментами для вирішення складного рендерингу PDF.