Генерация PDF-отчётов на TypeScript: библиотека PDF-Lib

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Генерация PDF-отчётов на TypeScript с библиотекой PDF-Lib

В современной веб-разработке часто возникает необходимость создавать сложные PDF-документы: отчёты, счета, сертификаты или аналитические дашборды. TypeScript, благодаря строгой типизации, делает этот процесс надёжным и предсказуемым. Среди множества библиотек для работы с PDF на платформе Node.js и в браузере выделяется PDF-Lib — легковесный, мощный и полностью написанный на TypeScript инструмент.

PDF-Lib не требует установки дополнительных нативных зависимостей (в отличие от тяжеловесных решений вроде Puppeteer или wkhtmltopdf), работает как в Node.js, так и в браузере, и поддерживает все современные возможности PDF: встраивание шрифтов, изображений, создание форм, аннотаций и многое другое.

Установка и первый документ

Для начала работы достаточно установить пакет через npm:

npm install pdf-lib

Создадим простейший PDF-документ с текстом:

import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';

async function createSimplePdf() {
  // Создаём новый PDF-документ
  const pdfDoc = await PDFDocument.create();

  // Добавляем страницу (формат A4)
  const page = pdfDoc.addPage([595.28, 841.89]); // A4 в пунктах

  // Встраиваем стандартный шрифт
  const font = await pdfDoc.embedFont(StandardFonts.Helvetica);

  // Рисуем текст
  page.drawText('Привет, мир!', {
    x: 50,
    y: 700,
    size: 30,
    font: font,
    color: rgb(0.2, 0.4, 0.8),
  });

  // Сохраняем документ в байты
  const pdfBytes = await pdfDoc.save();
  return pdfBytes;
}

// Пример использования
createSimplePdf().then((bytes) => {
  // В Node.js можно записать в файл
  // require('fs').writeFileSync('hello.pdf', bytes);
  console.log('PDF создан! Размер:', bytes.length, 'байт');
});

Работа с русским текстом и кастомными шрифтами

Одна из ключевых задач при генерации отчётов на русском языке — правильное отображение кириллицы. Стандартные шрифты PDF (Helvetica, Times, Courier) не содержат кириллических глифов. Решение — встроить TrueType-шрифт с поддержкой Unicode.

import { PDFDocument, rgb } from 'pdf-lib';
import fontkit from '@pdf-lib/fontkit';
import fs from 'fs';

async function createRussianPdf() {
  const pdfDoc = await PDFDocument.create();

  // Регистрируем fontkit для работы с кастомными шрифтами
  pdfDoc.registerFontkit(fontkit);

  // Загружаем шрифт с кириллицей (например, Roboto)
  const fontBytes = fs.readFileSync('Roboto-Regular.ttf');
  const customFont = await pdfDoc.embedFont(fontBytes);

  const page = pdfDoc.addPage([595.28, 841.89]);

  page.drawText('Генерация отчёта на TypeScript', {
    x: 50,
    y: 750,
    size: 24,
    font: customFont,
    color: rgb(0, 0, 0),
  });

  page.drawText('Дата: 2024-01-15', {
    x: 50,
    y: 700,
    size: 14,
    font: customFont,
    color: rgb(0.3, 0.3, 0.3),
  });

  return await pdfDoc.save();
}

Создание таблиц и структурированных отчётов

PDF-Lib не имеет встроенного модуля для таблиц, но их легко реализовать вручную через рисование линий и позиционирование текста. Для удобства можно создать вспомогательную функцию:

interface TableCell {
  text: string;
  width: number;
  height?: number;
  align?: 'left' | 'center' | 'right';
}

async function drawTable(
  page: PDFPage,
  headers: TableCell[],
  rows: TableCell[][],
  startX: number,
  startY: number,
  font: PDFFont,
  fontSize: number = 12
) {
  const cellPadding = 5;
  const rowHeight = fontSize + 2 * cellPadding;
  let currentY = startY;

  // Рисуем заголовки
  let currentX = startX;
  for (const header of headers) {
    page.drawRectangle({
      x: currentX,
      y: currentY - rowHeight,
      width: header.width,
      height: rowHeight,
      color: rgb(0.9, 0.9, 0.9),
      borderColor: rgb(0.7, 0.7, 0.7),
      borderWidth: 1,
    });
    page.drawText(header.text, {
      x: currentX + cellPadding,
      y: currentY - rowHeight + cellPadding,
      size: fontSize,
      font: font,
      color: rgb(0, 0, 0),
    });
    currentX += header.width;
  }
  currentY -= rowHeight;

  // Рисуем строки данных
  for (const row of rows) {
    currentX =                    

Рекомендаций