Экспорт графиков из Chart.js в PDF основан на преобразовании canvas-элемента в изображение с последующим встраиванием этого изображения в PDF-документ. Библиотека jsPDF выступает генератором PDF, принимая растровые данные (PNG, JPEG) и размещая их на страницах документа.
В основе процесса лежит цепочка:
<canvas>Chart.js не выполняет экспорт самостоятельно, он лишь предоставляет готовый canvas.
Для работы требуется наличие двух ключевых компонентов:
Типовая установка через npm:
npm install chart.js jspdf
Подключение в модуле:
import { Chart } from 'chart.js';
import { jsPDF } from 'jspdf';
Если используется CDN:
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/jspdf"></script>
Chart.js предоставляет встроенный механизм экспорта изображения через
метод toBase64Image().
Пример получения PNG-строки:
const canvas = document.getElementById('myChart');
const base64Image = canvas.toDataURL('image/png');
Альтернативный способ через экземпляр графика:
const image = myChart.toBase64Image();
Оба варианта возвращают строку вида:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...
Эта строка напрямую используется в jsPDF.
Минимальный сценарий заключается в создании PDF и вставке изображения:
import { jsPDF } from 'jspdf';
const pdf = new jsPDF();
const canvas = document.getElementById('myChart');
const image = canvas.toDataURL('image/png');
pdf.addImage(image, 'PNG', 10, 10, 190, 100);
pdf.save('chart.pdf');
Параметры addImage:
image — base64 строка'PNG' — формат10, 10 — координаты на странице190, 100 — ширина и высотаPDF имеет фиксированный формат страницы, поэтому важно учитывать пропорции canvas.
Стандартный лист A4 в jsPDF:
const pdf = new jsPDF('p', 'mm', 'a4');
Размеры страницы:
Пример масштабирования с сохранением пропорций:
const pdf = new jsPDF('p', 'mm', 'a4');
const img = myChart.toBase64Image();
const pageWidth = pdf.internal.pageSize.getWidth();
const pageHeight = pdf.internal.pageSize.getHeight();
const imgProps = pdf.getImageProperties(img);
const ratio = imgProps.height / imgProps.width;
const width = pageWidth - 20;
const height = width * ratio;
pdf.addImage(img, 'PNG', 10, 10, width, height);
Ключевая логика:
При размещении нескольких диаграмм используется фиксированная сетка координат.
const pdf = new jsPDF();
const charts = [chart1, chart2, chart3];
let y = 10;
charts.forEach((chart) => {
const img = chart.toBase64Image();
pdf.addImage(img, 'PNG', 10, y, 180, 60);
y += 70;
});
Если координаты выходят за пределы страницы, требуется переход:
if (y > 250) {
pdf.addPage();
y = 10;
}
Каждый график может занимать отдельную страницу:
const pdf = new jsPDF();
charts.forEach((chart, index) => {
if (index !== 0) {
pdf.addPage();
}
const img = chart.toBase64Image();
pdf.addImage(img, 'PNG', 10, 10, 190, 120);
});
pdf.save('charts.pdf');
Такой подход используется при отчётной генерации.
В некоторых случаях график находится не напрямую в Chart.js, а в сложной DOM-структуре. Тогда применяется библиотека html2canvas:
import html2canvas from 'html2canvas';
import { jsPDF } from 'jspdf';
html2canvas(document.querySelector('#chartContainer')).then((canvas) => {
const img = canvas.toDataURL('image/png');
const pdf = new jsPDF();
pdf.addImage(img, 'PNG', 10, 10, 190, 100);
pdf.save('report.pdf');
});
Особенности:
Canvas по умолчанию может быть недостаточно чётким при масштабировании. Для улучшения качества используется увеличение разрешения:
html2canvas(element, {
scale: 2
});
Для Chart.js также применяется увеличение размера canvas:
const chart = new Chart(ctx, {
options: {
responsive: false,
maintainAspectRatio: false
}
});
Дополнительно можно задавать размеры canvas вручную:
canvas.width = 1200;
canvas.height = 600;
jsPDF поддерживает портретную и альбомную ориентацию:
const pdf = new jsPDF('landscape', 'mm', 'a4');
Альбомная ориентация применяется для широких графиков:
Универсальный подход включает вычисление доступной области:
function addChart(pdf, chart, x, y, maxWidth) {
const img = chart.toBase64Image();
const props = pdf.getImageProperties(img);
const ratio = props.height / props.width;
const width = maxWidth;
const height = width * ratio;
pdf.addImage(img, 'PNG', x, y, width, height);
return height;
}
Использование:
const pdf = new jsPDF();
let y = 10;
y += addChart(pdf, chart1, 10, y, 180);
y += 10;
y += addChart(pdf, chart2, 10, y, 180);
PDF редко содержит только изображения. Часто добавляются подписи:
pdf.setFontSize(12);
pdf.text('Анализ продаж за период', 10, 10);
pdf.addImage(img, 'PNG', 10, 20, 180, 100);
Дополнительно:
pdf.setFontSize(10);
pdf.text('Источник: внутренние данные системы', 10, 280);
Причина: низкое разрешение canvas Решение: увеличение
scale или размера canvas
Причина: неверные размеры addImage Решение: вычисление
пропорций через getImageProperties
Причина: экспорт до завершения рендера Chart.js Решение: ожидание
события animation complete
options: {
animation: {
onComplete: function () {
exportPDF();
}
}
}
Причина: несоответствие DPI canvas и PDF Решение: фиксированное масштабирование и отказ от responsive режима
При построении систем аналитики используется комбинированный подход:
Структура отчёта:
Каждая секция формируется как отдельная страница PDF, а графики вставляются как изображения canvas без потери логики визуализации.