Реализация генерации PDF-документов на сайте

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

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

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Реализация генерации PDF-документов на сайте
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1217
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1161
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    852
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1046
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    823
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    815

Реализация генерации PDF

Генерация PDF применяется для счетов, договоров, отчётов, справок. Два подхода: HTML-to-PDF (рендеринг через headless браузер или библиотеку) и построение из примитивов (TCPDF, FPDF). HTML-to-PDF проще для сложных макетов.

Laravel: Browsershot (Puppeteer)

Browsershot использует headless Chrome для рендеринга HTML в PDF — поддерживает CSS Grid, Flexbox, переменные, шрифты.

use Spatie\Browsershot\Browsershot;

class InvoicePdfService
{
    public function generate(Invoice $invoice): string
    {
        $html = view('pdf.invoice', ['invoice' => $invoice])->render();

        $path = storage_path("app/invoices/invoice-{$invoice->id}.pdf");

        Browsershot::html($html)
            ->format('A4')
            ->margins(15, 15, 15, 15)  // мм
            ->showBackground()
            ->emulateMedia('print')
            ->waitUntilNetworkIdle()   // дождаться загрузки шрифтов
            ->save($path);

        return $path;
    }
}

// Controller
public function download(Invoice $invoice): Response
{
    $path = $this->invoicePdfService->generate($invoice);

    return response()->download(
        $path,
        "invoice-{$invoice->number}.pdf",
        ['Content-Type' => 'application/pdf']
    );
}
<!-- resources/views/pdf/invoice.blade.php -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700&display=swap');

  * { margin: 0; padding: 0; box-sizing: border-box; }
  body { font-family: 'Inter', sans-serif; font-size: 12px; color: #1a1a1a; }

  .header { display: flex; justify-content: space-between; margin-bottom: 40px; }
  .invoice-number { font-size: 24px; font-weight: 700; }

  table { width: 100%; border-collapse: collapse; margin-top: 20px; }
  th { background: #f3f4f6; padding: 8px; text-align: left; font-weight: 600; }
  td { padding: 8px; border-bottom: 1px solid #e5e7eb; }

  .total { font-size: 16px; font-weight: 700; text-align: right; margin-top: 20px; }

  @media print {
    .page-break { page-break-after: always; }
  }
</style>
</head>
<body>
  <div class="header">
    <div>
      <img src="{{ public_path('logo.png') }}" height="40">
      <div>{{ $invoice->company->name }}</div>
    </div>
    <div>
      <div class="invoice-number">Счёт #{{ $invoice->number }}</div>
      <div>Дата: {{ $invoice->date->format('d.m.Y') }}</div>
    </div>
  </div>

  <table>
    <thead>
      <tr><th>Описание</th><th>Кол-во</th><th>Цена</th><th>Сумма</th></tr>
    </thead>
    <tbody>
      @foreach($invoice->items as $item)
      <tr>
        <td>{{ $item->description }}</td>
        <td>{{ $item->quantity }}</td>
        <td>{{ number_format($item->price, 2) }} ₽</td>
        <td>{{ number_format($item->total, 2) }} ₽</td>
      </tr>
      @endforeach
    </tbody>
  </table>

  <div class="total">Итого: {{ number_format($invoice->total, 2) }} ₽</div>
</body>
</html>

Node.js: Puppeteer

import puppeteer from 'puppeteer';
import Handlebars from 'handlebars';

async function generateInvoicePdf(invoice: Invoice): Promise<Buffer> {
  const templateSource = await fs.readFile('./templates/invoice.html', 'utf-8');
  const template = Handlebars.compile(templateSource);
  const html = template(invoice);

  const browser = await puppeteer.launch({
    headless: true,
    args: ['--no-sandbox', '--disable-setuid-sandbox'],
  });

  try {
    const page = await browser.newPage();
    await page.setContent(html, { waitUntil: 'networkidle0' });

    return await page.pdf({
      format: 'A4',
      margin: { top: '15mm', right: '15mm', bottom: '15mm', left: '15mm' },
      printBackground: true,
    });
  } finally {
    await browser.close();
  }
}

TCPDF: PHP-нативная генерация (без браузера)

Подходит для простых документов без сложного CSS:

use TCPDF;

class ContractPdfService
{
    public function generate(Contract $contract): string
    {
        $pdf = new TCPDF('P', 'mm', 'A4', true, 'UTF-8');
        $pdf->SetCreator('MyApp');
        $pdf->SetAuthor($contract->company->name);
        $pdf->SetTitle('Договор №' . $contract->number);

        $pdf->SetFont('dejavusans', '', 10);
        $pdf->AddPage();

        $html = view('pdf.contract-simple', compact('contract'))->render();
        $pdf->writeHTML($html, true, false, true, false, '');

        $path = storage_path("app/contracts/contract-{$contract->id}.pdf");
        $pdf->Output($path, 'F');

        return $path;
    }
}

Асинхронная генерация в очереди

class GenerateInvoicePdfJob implements ShouldQueue
{
    public int $timeout = 120;

    public function __construct(private Invoice $invoice) {}

    public function handle(InvoicePdfService $service): void
    {
        $path = $service->generate($this->invoice);

        // Загрузить в S3
        $s3Key = "invoices/{$this->invoice->user_id}/{$this->invoice->id}.pdf";
        Storage::disk('s3')->put($s3Key, file_get_contents($path));

        $this->invoice->update(['pdf_key' => $s3Key, 'pdf_generated_at' => now()]);

        unlink($path);

        // Уведомить пользователя
        $this->invoice->user->notify(new InvoiceReadyNotification($this->invoice));
    }
}

Срок реализации

PDF-генерация через Browsershot/Puppeteer для Laravel или Node.js (счета, договоры): 2–3 дня. С асинхронной очередью и сохранением в S3: 3–4 дня.