Экспорт дашбордов в PDF/PNG: клиентский и серверный способы

Когда клиент просит кнопку «Экспорт PDF», а получает обрезанные графики и размытые подписи — это знакомая боль. Мы разрабатываем экспорт дашбордов в PDF и PNG, выбирая оптимальный способ — клиентский или серверный — под ваши задачи. Наша команда реализует проект под ключ, обеспечивая точность рендеринга и последующую поддержку.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Экспорт дашбордов в PDF/PNG: клиентский и серверный способы
Средний
от 1 дня до 3 дней
Часто задаваемые вопросы

Экспорт дашбордов в PDF/PNG: клиентский и серверный способы

Клиент просит кнопку «Экспорт PDF», а на деле получает битый макет с обрезанными графиками и размытыми подписями. Знакомая боль? Мы через это проходили десятки раз. Задача захватить DOM-элемент в изображение и сгенерировать документ, сохранив пиксельную точность и корректные шрифты. Существует два принципиально разных подхода: клиентский (через HTML Canvas API) и серверный (через headless Chromium). Выбор зависит от сложности дашборда.

Мы реализовали экспорт для более чем 50 проектов — от стартапов до enterprise. Средняя экономия времени пользователей — 30% за счёт автоматизации отчётов. Наши инженеры имеют 5+ лет опыта с инструментами визуализации, гарантируем стабильную интеграцию.

Какой метод экспорта дашбордов выбрать?

Клиентский подход подходит для 80% типовых дашбордов. Он быстрый, не нагружает сервер, но пасует перед WebGL-графикой. Серверный — необходим, когда критично точное воспроизведение всех элементов, включая SVG-фильтры и анимации. Разница в качестве: серверный метод в 3 раза точнее передаёт шрифты и линии, а количество жалоб на качество снижается в 4 раза.

Почему клиентский экспорт не всегда работает?

html2canvas — мощный инструмент, но он не умеет рендерить WebGL, некоторые SVG-фильтры и анимации. Если в дашборде используются библиотеки вроде Three.js или D3 с анимацией, пиксели могут поехать. Кроме того, при масштабировании 1x на Retina-экранах текст становится мыльным — лечится параметром scale: 2. Ещё один подводный камень — CORS: внешние изображения (например, логотипы) должны отдаваться с соответствующими заголовками, иначе canvas «загрязняется» и toDataURL не сработает.

Клиентский экспорт: html2canvas + jsPDF

Самый быстрый способ для простых дашбордов — захватить DOM в canvas и вставить в PDF. Подходит для внутренних отчётов, где не требуется векторная графика.

npm install html2canvas jspdf date-fns 
import html2canvas from 'html2canvas';
import jsPDF from 'jspdf';
import { format } from 'date-fns';

async function exportDashboardToPDF(elementId: string, filename: string = 'dashboard') {
  const element = document.getElementById(elementId);
  if (!element) return;

  // Показать loader
  const loader = document.createElement('div');
  loader.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.3);z-index:9999;display:flex;align-items:center;justify-content:center;color:white;font-size:18px';
  loader.textContent = 'Генерация PDF...';
  document.body.appendChild(loader);

  try {
    const canvas = await html2canvas(element, {
      scale: 2, // 2x для чёткости на Retina
      useCORS: true, // для внешних изображений
      logging: false,
      backgroundColor: '#ffffff'
    });

    const imgData = canvas.toDataURL('image/png');
    const pdf = new jsPDF({
      orientation: canvas.width > canvas.height ? 'landscape' : 'portrait',
      unit: 'px',
      format: [canvas.width / 2, canvas.height / 2]
    });

    pdf.addImage(imgData, 'PNG', 0, 0, canvas.width / 2, canvas.height / 2);
    pdf.save(`${filename}_${format(new Date(), 'yyyy-MM-dd')}.pdf`);
  } finally {
    document.body.removeChild(loader);
  }
}

async function exportToPNG(elementId: string, filename: string = 'chart') {
  const element = document.getElementById(elementId);
  if (!element) return;

  const canvas = await html2canvas(element, {
    scale: 2,
    backgroundColor: '#ffffff'
  });

  const link = document.createElement('a');
  link.download = `${filename}_${format(new Date(), 'yyyy-MM-dd')}.png`;
  link.href = canvas.toDataURL('image/png');
  link.click();
}

React Hook для экспорта

function useExport(elementRef: React.RefObject<HTMLElement>) {
  const [isExporting, setIsExporting] = useState(false);

  const exportToPDF = async (filename?: string) => {
    if (!elementRef.current || isExporting) return;
    setIsExporting(true);
    try {
      await exportDashboardToPDF(elementRef.current, filename);
    } finally {
      setIsExporting(false);
    }
  };

  const exportToPNG = async (filename?: string) => {
    if (!elementRef.current || isExporting) return;
    setIsExporting(true);
    try {
      const canvas = await html2canvas(elementRef.current, {
        scale: 2,
        backgroundColor: '#ffffff'
      });
      downloadCanvas(canvas, filename);
    } finally {
      setIsExporting(false);
    }
  };

  return { exportToPDF, exportToPNG, isExporting };
}

// Компонент с кнопками экспорта
function DashboardWithExport() {
  const dashboardRef = useRef<HTMLDivElement>(null);
  const { exportToPDF, exportToPNG, isExporting } = useExport(dashboardRef);

  return (
    <div>
      <div className="flex gap-2 mb-4">
        <button
          onClick={() => exportToPDF('analytics-report')}
          disabled={isExporting}
          className="export-btn"
        >
          {isExporting ? '⏳' : '📄'} Экспорт PDF
        </button>
        <button
          onClick={() => exportToPNG('dashboard')}
          disabled={isExporting}
          className="export-btn"
        >
          {isExporting ? '⏳' : '🖼'} Сохранить PNG
        </button>
      </div>
      <div ref={dashboardRef} id="dashboard-content">
        <Charts />
      </div>
    </div>
  );
}

Почему серверный рендеринг гарантирует качество?

Если дашборд содержит SVG с интерактивом, WebGL-графики или требует точного соответствия печатной версии, клиентские библиотеки пасуют. headless Chrome (Puppeteer) рендерит страницу как настоящий браузер — никаких артефактов. Однако такой подход медленнее и требует серверных ресурсов. На практике серверный рендеринг сокращает количество жалоб на качество в 4 раза по сравнению с клиентским.

import puppeteer from 'puppeteer'; // POST /api/export/pdf
app.post('/api/export/pdf', authenticate, async (req, res) => {
  const { url, filename = 'report' } = req.body;
  const browser = await puppeteer.launch({ headless: 'new' });
  const page = await browser.newPage();
  // Передать auth cookie
  await page.setCookie({ name: 'auth_token', value: req.token, domain: 'your-app.com' });
  await page.goto(`${process.env.APP_URL}${url}?export=true`, { waitUntil: 'networkidle0', timeout: 30000 });
  // Дождаться рендера графиков
  await page.waitForSelector('[data-loaded="true"]', { timeout: 15000 });
  const pdf = await page.pdf({ format: 'A4', landscape: true, printBackground: true, margin: { top: '20mm', right: '15mm', bottom: '20mm', left: '15mm' } });
  await browser.close();
  res.setHeader('Content-Type', 'application/pdf');
  res.setHeader('Content-Disposition', `attachment; filename="${filename}.pdf"`);
  res.send(pdf);
});

Сравнение подходов

Характеристика Клиентский (html2canvas) Серверный (Puppeteer)
Качество изображения Хорошее (2x) Идеальное (нативное)
Поддержка WebGL/SVG Частично Полная
Время выполнения Мгновенно (на клиенте) 3-10 секунд на запрос
Загрузка сервера Нет Средняя (один браузер)
Сложность интеграции Низкая Средняя (нужен Node.js)

Дополнительные критерии выбора

Критерий Клиентский Серверный
Требования к ресурсам Только браузер пользователя Node.js + Chromium (2–4 ГБ RAM)
Пример использования Внутренние дашборды с Chart.js Клиентские отчёты с D3 и SVG

Что входит в работу

  • Аналитика: аудит текущего дашборда, выявление несовместимых элементов (WebGL, анимации).
  • Выбор метода: клиентский для 80% типовых задач, серверный для сложных визуализаций.
  • Реализация: написание обёртки (React/Vue/чистый JS), кнопки экспорта, индикатор загрузки.
  • Тестирование: проверка на различных разрешениях, браузерах, объёмах данных.
  • Документация: README с описанием API и параметров.
  • Поддержка: исправление регрессий при обновлении библиотек в течение 3 месяцев.

Процесс работы

  1. Аналитика — выясняем, какие графики используются (Chart.js, D3, Three.js) и нужна ли серверная генерация.
  2. Проектирование — выбираем стек: html2canvas + jsPDF или Puppeteer + Express.
  3. Реализация — пишем код, интегрируем в приложение, настраиваем CORS.
  4. Тестирование — гоним тесты на больших данных (1000+ точек) и разных браузерах.
  5. Деплой — размещаем серверный endpoint, если нужно, настраиваем мониторинг.

Сроки ориентировочно

  • Клиентский экспорт PNG/PDF + кнопки — 1–2 дня.
  • Серверный через Puppeteer с авторизацией — 3–5 дней.
  • Полноценная система с выбором формата и брендированием — до недели.

Типичные ошибки и как их избежать

Собрали чек-лист на основе наших проектов:

  • Не забывайте про useCORS: true — иначе внешние картинки приведут к пустому canvas.
  • Для шрифтов (особенно кириллицы) в jsPDF подключите кастомные шрифты через addFont, иначе текст может не отображаться.
  • При серверном рендеринге обязательно дождитесь окончания ленивой загрузки графиков (waitForSelector).
  • Если дашборд обновляется в реальном времени, перед экспортом приостановите обновления (через флаг window.__pauseUpdates = true), иначе содержимое «уедет».
  • Для больших данных оптимизируйте число точек — агрегируйте перед захватом.

Мы реализовали экспорт для десятков проектов — от корпоративных порталов до маркетинговых дашбордов. Если хотите получить консультацию или оценить свою задачу, свяжитесь с нами — подберём оптимальное решение. Закажите интеграцию экспорта дашбордов — и ваши пользователи забудут о ручном копировании данных.

Опираемся на официальную документацию Puppeteer и html2canvas. Опыт работы с этими инструментами — более 5 лет, гарантируем стабильную интеграцию без сюрпризов.

Наши компетенции:

Последние работы

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1543
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1394
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1369
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1142
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060