PDFKit в Node.js — полное руководство по созданию PDF

Важно: перед началом убедитесь, что на машине установлены Node.js и npm.
Что такое PDFKit (одно предложение)
PDFKit — это библиотека для Node.js, предоставляющая API для программного создания PDF-файлов: текст, изображения, графика, страницы и метаданные.
Основные сценарии использования
- Генерация отчётов и счётов на сервере
- Создание динамических документов (договоры, квитанции)
- Потоковая отправка PDF в браузер или на принтер
- Создание шаблонов для печати
Быстрый старт — установка
Проверьте версии Node.js и npm:
node -v
npm -vУстановите PDFKit в проект:
npm install pdfkitЕсли используете TypeScript, рассмотрите установку типов для Node.js и собственных деклараций для PDFKit.
Минимальный рабочий пример
Ниже пример скрипта, который создаёт PDF-файл в текущей папке.
const PDFDocument = require('pdfkit');
const fs = require('fs');
const doc = new PDFDocument();
doc.pipe(fs.createWriteStream('MyPDFDoc.pdf'));
doc.font('Times-Roman')
.fontSize(12)
.text('Hello from PDFKit!');
doc.end();Примечание: doc — это читаемый поток (readable stream). Метод pipe() направляет байты в fs.createWriteStream.
Добавление текста
PDFKit использует doc.text(text, [x, y], [options]) для вставки текста. По умолчанию текст добавляется построчно, позиционирование ведётся от текущей позиции курсора.
Примеры:
doc.text('Coding is Easy!');
doc.text('Coding is Easy!', 100, 100);
doc.moveDown(3);
doc.moveUp();
doc.font('Times-Roman').text('Текст с шрифтом Times-Roman');
// Цвет и размер
doc.fillColor('red').fontSize(8).text('Красный маленький текст');Поддерживаются 14 стандартных PDF-шрифтов и подключаемые файлы шрифтов (.ttf, .otf).
Совет: при использовании кастомных шрифтов применяйте абсолютные пути или включайте шрифты в сборку приложения.
Добавление изображений
PDFKit поддерживает PNG и JPEG. Простейший вызов:
doc.image('path/to/image.jpeg');Изменение размера и выравнивания:
// Ширина 300
doc.image('path/to/image.jpg', { width: 300 });
// Ширина и высота
doc.image('path/to/image.jpg', { width: 300, height: 200 });Важно: PDFKit вставляет изображение в текущую позицию курсора. При работе с большими изображениями учитывайте память и размер итогового файла.
Страницы, размеры и поля
Новый документ и добавление страниц:
const doc = new PDFDocument({ size: 'A4' });
// добавить страницу
doc.addPage();Вы можете слушать событие pageAdded:
doc.on('pageAdded', () => doc.text('Новая страница — автоматический заголовок'));Задание полей (margins) и размеров:
// Добавление страницы с индивидуальными полями в пунктах (points)
doc.addPage({
margins: {
top: 72,
bottom: 72,
left: 50,
right: 50
}
});
// Одинаковые поля по всем сторонам (60 пунктов)
doc.addPage({ margin: 60 });Примечание: 72 пункта = 1 дюйм (≈2.54 см). По умолчанию PDFKit использует 1-inch (72 pt) поля.
Работа со шрифтами и кодировкой
- Для кириллицы требуется подключить шрифт, поддерживающий нужные глифы (например, DejaVu Sans, Roboto или системные шрифты).
Пример подключения TTF:
doc.registerFont('DejaVu', 'fonts/DejaVuSans.ttf');
doc.font('DejaVu').text('Пример кириллического текста');Совет: храните шрифты рядом с приложением и указывайте относительные пути во всех средах (CI, прод).
Стриминг PDF в HTTP-ответ (Express)
Часто PDF генерируется по запросу и немедленно передаётся пользователю.
const express = require('express');
const PDFDocument = require('pdfkit');
const app = express();
app.get('/invoice', (req, res) => {
const doc = new PDFDocument({ size: 'A4', margin: 50 });
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', 'inline; filename=invoice.pdf');
doc.pipe(res);
doc.fontSize(20).text('Invoice', { align: 'center' });
// ... добавляем содержимое
doc.end();
});
app.listen(3000);Важно: не используйте большие промежуточные буферы при стриминге — направляйте doc напрямую в ответ.
Встраивание таблиц и верстка
PDFKit не предоставляет полноценный движок верстки таблиц, но шаблоны таблиц можно реализовать вручную: вычислять координаты, отрисовывать линии и размещать текст по колонкам.
Пример простейшей сетки:
// рисуем линию
doc.moveTo(50, 150).lineTo(550, 150).stroke();
// ячейки: печатаем текст с позиционированием
doc.text('Колонка 1', 60, 160);
doc.text('Колонка 2', 200, 160);Для готовых реализаций таблиц рассмотрите внешние утилиты/модули-надстройки, либо используйте HTML->PDF подход (Puppeteer). См. раздел «Альтернативы».
Метаданные и закладки
Добавьте метаданные к документу:
doc.info.Title = 'Отчёт о продажах';
doc.info.Author = 'Отдел аналитики';PDFKit также поддерживает аннотации и ссылки внутри документа.
Локализация и форматирование дат/чисел
При генерации документов с датами и валютой форматируйте значения до вставки в PDF с помощью библиотек (Intl, moment, date-fns, numeral), чтобы обеспечить соответствие локали пользователя.
Пример:
const total = new Intl.NumberFormat('ru-RU', { style: 'currency', currency: 'RUB' }).format(12345.67);
doc.text(`Итого: ${total}`);Производительность и память
- PDFKit генерирует PDF в потоке, поэтому общая память обычно ограничена размером изображения/ресурсов и буфером потока.
- Избегайте загрузки больших изображений целиком в память; используйте оптимизированные версии или стримы.
- Генерация большого количества документов параллельно может привести к росту потребления памяти; применяйте очередь задач или пул воркеров.
Рекомендации:
- Сжимайте изображения перед вставкой.
- Используйте потоки для источников данных.
- Оценивайте конечный размер PDF и при необходимости разделяйте контент на страницы.
Безопасность и конфиденциальность
- Не вставляйте в PDF персональные данные без необходимости и согласия пользователя.
- Если генерируете PDF с пользовательским вводом, экранируйте или валидируйте данные, чтобы избежать непреднамеренного внедрения служебных символов или скрытых ссылок.
- Хранение сгенерированных PDF: задавайте ограничения срока хранения и права доступа.
Примечание о GDPR: для данных EU убедитесь, что документы удаляются по истечении срока хранения и что доступ к ним логируется и ограничен.
Тестирование, критерии приёмки и контроль качества
Критерии приёмки:
- PDF корректно открывается в основных ридерах (Adobe Reader, браузеры).
- Текст и изображения корректно отображаются для целевой локали (кириллица, валюты).
- Страницы, заголовки и номера страниц соответствуют требованиям дизайна.
- Метаданные заполнены.
Тесты/приёмочные проверки:
- Визуальная проверка (ручная) на ключевых платформах.
- Unit-тесты: проверяйте, что API-методы вызываются с корректными параметрами (мокирование потоков).
- Контроль размеров: проверка, что итоговый PDF не превышает лимит.
Отладка и типичные проблемы
- Пустой PDF: забыли вызвать doc.end().
- Кириллица отображается некорректно: подключите TTF-шрифт с поддержкой кириллицы.
- Большой размер файла: оптимизируйте изображения и используйте сжатие.
- Ошибки при стриминге в HTTP: установите заголовки до первого байта ответа и не вызывайте res.send() параллельно с потоком.
Пример типичной ошибки и исправления:
Ошибка: PDF скачивается, но открывается с ошибкой. Возможная причина — дважды были отправлены заголовки или поток не завершён.
Решение:
- Убедитесь, что doc.pipe(res) вызван единожды.
- Вызовите doc.end() и не обращайтесь к res после завершения.
Альтернативы и когда PDFKit не подходит
- Puppeteer / headless Chrome — хорош для конвертации HTML/CSS в PDF, если требуется точная верстка и сложные стили.
- pdf-lib — библиотека для модификации существующих PDF, может быть удобнее для заполнения шаблонов.
- wkhtmltopdf — рендерит HTML в PDF, подходит в проектах с готовыми HTML-шаблонами.
Когда не использовать PDFKit:
- Если вам нужен WYSIWYG-рендеринг сложного HTML/CSS.
- Если требуется массовая постобработка существующих PDF-файлов (лучше pdf-lib).
Шаблон: генерация счёта с разбиением на функции
// invoice.js
const PDFDocument = require('pdfkit');
function generateInvoice(invoice, stream) {
const doc = new PDFDocument({ size: 'A4', margin: 50 });
doc.pipe(stream);
renderHeader(doc, invoice);
renderCustomerInfo(doc, invoice);
renderTable(doc, invoice);
renderFooter(doc, invoice);
doc.end();
}
module.exports = { generateInvoice };Разделите рендеринг на небольшие функции: header, footer, table, totals. Это улучшает тестируемость и поддержку.
Роль‑ориентированные чек-листы
Разработчик:
- Подключил шрифты для нужных локалей.
- Использует потоки для больших ресурсов.
- Покрыл основные сценарии тестами.
QA:
- Проверил открытие в разных ридерах.
- Проверил граничные случаи: пустые табличные строки, очень длинные поля.
DevOps:
- Убедился в ограничениях памяти на сервере при пиковых нагрузках.
- Настроил хранение и удаление временных файлов.
Мини‑методология разработки функций PDF
- Опишите шаблон документа и поля.
- Подготовьте шрифты и ресурсы.
- Напишите модуль, рендерящий документ по частям.
- Добавьте тесты для каждой части (мок потоков).
- Протестируйте на целевых платформах и в разных локалях.
- Оптимизируйте изображения и ресурсы.
Примеры тестовых случаев
- Сценарий: генерируется счёт с 0 позиций — ожидается страница с текстом «Нет позиций».
- Сценарий: длинное название товара — текст обрезается или переносится без выхода за границы.
- Сценарий: кириллические символы в полях — отображаются корректно.
Миграция и совместимость
- PDFKit активно развивается; при обновлении проверяйте изменение API и поведение по умолчанию.
- При миграции от HTML->PDF к PDFKit перепроектируйте шаблоны: верстка будет интуитивно другой.
Шаблоны для социальных превью
OG title: Генерация PDF в Node.js с PDFKit OG description: Легко создавайте PDF: установка, текст, изображения, стриминг и советы по безопасности.
Заключение
PDFKit — мощный инструмент для серверной генерации PDF в Node.js. Он даёт контроль над каждой частью страницы и подходит для большинства сценариев, где нужен программный контроль над контентом. Для случаев с сложной HTML-вёрсткой рассмотрите альтернативы.
Важно: выбирайте подход в зависимости от требований к верстке, локализации и объёму данных.
Краткое содержание:
- Установите PDFKit и протестируйте версии Node.js.
- Подключите шрифты для поддержки кириллицы.
- Используйте потоки для оптимального потребления памяти.
- Тестируйте документы в разных ридерах и локалях.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone