Looking to hire Laravel developers? Try LaraJobs

laravel maintained by pdf-block

Description
Server-side PDF renderer for pdf-block DSL documents. Converts JSON DSL to native PDF via Browsershot (Puppeteer).
Last update
2026/04/18 03:07 (dev-main)
License
Downloads
0

Comments
comments powered by Disqus

pdf-block/laravel

Pacote Laravel para renderizar documentos da DSL do pdf-block em PDF nativo. Texto selecionável, links clicáveis, imagens embutidas — PDF de verdade, não screenshot.

Usa Browsershot (Puppeteer) para converter HTML com inline styles em PDF via Chrome headless.

Instalação

composer require pdf-block/laravel

O service provider é registrado automaticamente via auto-discovery do Laravel.

Pré-requisitos no servidor

O Browsershot requer Node.js e Puppeteer instalados no servidor:

# Node.js
sudo apt install nodejs npm

# Puppeteer (Chrome headless)
npm install -g puppeteer
# ou instale o Chromium do SO:
sudo apt install chromium-browser

Publicar config (opcional)

php artisan vendor:publish --tag=pdf-block-config

Isso cria config/pdf-block.php com as opções de configuração.

Publicar views (opcional)

php artisan vendor:publish --tag=pdf-block-views

Permite customizar os Blade templates de renderização em resources/views/vendor/pdf-block/.

Uso

Injeção de dependência

use PdfBlock\Laravel\PdfBlockRenderer;

class ExportController extends Controller
{
    public function pdf(Request $request, PdfBlockRenderer $renderer)
    {
        $document = $request->input('document');

        // Gera PDF nativo
        $pdf = $renderer->toPdf($document);

        // Retorna como download
        return $pdf->toResponse('documento.pdf');
    }
}

API completa

$renderer = app(PdfBlockRenderer::class);

// DSL → HTML (útil para debug, email, preview)
$html = $renderer->toHtml($document);

// DSL → PDF (blob)
$pdf = $renderer->toPdf($document);

// Download
return $pdf->toResponse('invoice.pdf');

// Preview inline no browser
return $pdf->toInlineResponse('preview.pdf');

// Salvar em disco
$pdf->save(storage_path('exports/invoice.pdf'));

// Tamanho em bytes
$pdf->size();

// Conteúdo binário raw
$pdf->content();

Configuração

// config/pdf-block.php

return [
    // Caminho para o binário do Node.js
    'node_binary' => env('PDF_BLOCK_NODE_BINARY', '/usr/bin/node'),

    // Caminho para o binário do npm
    'npm_binary'  => env('PDF_BLOCK_NPM_BINARY', '/usr/bin/npm'),

    // Timeout em segundos para a renderização
    'timeout' => (int) env('PDF_BLOCK_TIMEOUT', 30),

    // Flags do Chromium
    'chrome_args' => [
        '--no-sandbox',
        '--disable-gpu',
        '--disable-dev-shm-usage',
    ],
];

Variáveis de ambiente

PDF_BLOCK_NODE_BINARY=/usr/bin/node
PDF_BLOCK_NPM_BINARY=/usr/bin/npm
PDF_BLOCK_TIMEOUT=30

DSL — Formato do documento

O pacote espera receber a mesma DSL JSON produzida pelo editor @pdf-block/react:

{
  "id": "uuid",
  "version": "2.0.0",
  "meta": {
    "title": "Meu documento",
    "description": "",
    "locale": "pt-BR",
    "tags": []
  },
  "pageSettings": {
    "paperSize": { "preset": "a4", "width": 210, "height": 297 },
    "orientation": "portrait",
    "margins": { "top": 20, "right": 20, "bottom": 20, "left": 20 },
    "defaultFontFamily": "Inter, sans-serif"
  },
  "globalStyles": {
    "pageBackground": "#ffffff",
    "contentBackground": "#ffffff",
    "defaultFontColor": "#333333"
  },
  "blocks": [
    {
      "type": "stripe",
      "children": [
        {
          "type": "structure",
          "columns": [
            {
              "width": 100,
              "children": [
                { "type": "text", "content": { "type": "doc", "content": [...] }, ... },
                { "type": "image", "src": "https://...", ... },
                { "type": "button", "text": "Clique aqui", "url": "https://...", ... }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Arquitetura

DSL JSON (do editor React)
    │
    ▼
PdfBlockRenderer::toHtml()
    │  Blade templates renderizam cada bloco
    │  com inline styles idênticos ao editor
    ▼
HTML string standalone
    │
    ▼
PdfBlockRenderer::toPdf()
    │  Browsershot → Puppeteer → Chrome headless
    │  page.pdf() com margens e tamanho de papel
    ▼
PdfResult (blob PDF nativo)

Paridade visual

Os Blade templates replicam 1:1 os renderers React. Ambos usam 100% inline styles via as mesmas funções CSS helper (portadas de TypeScript para PHP em StyleHelpers.php).

Blocos suportados

Bloco Template Descrição
text blocks/text.blade.php Rich text via TipTap JSON → HTML (ueberdosis/tiptap-php)
image blocks/image.blade.php Imagem com alignment, objectFit, bordas
button blocks/button.blade.php Link estilizado como botão (clicável no PDF)
divider blocks/divider.blade.php Linha horizontal
spacer blocks/spacer.blade.php Espaço vertical
banner blocks/banner.blade.php Imagem de fundo com overlay e textos
table blocks/table.blade.php Tabela com header, zebra, bordas
qrcode blocks/qrcode.blade.php Placeholder de QR code
chart blocks/chart.blade.php Gráfico de barras (CSS puro)
pagebreak blocks/pagebreak.blade.php Quebra de página forçada

Customização de templates

Após publicar as views (--tag=pdf-block-views), edite os templates em resources/views/vendor/pdf-block/ para customizar a renderização:

resources/views/vendor/pdf-block/
├── document.blade.php     # Layout principal
├── stripe.blade.php       # Stripe (banda horizontal)
├── structure.blade.php    # Structure (row de colunas)
├── block.blade.php        # Router de blocos
└── blocks/
    ├── text.blade.php
    ├── image.blade.php
    ├── button.blade.php
    ├── divider.blade.php
    ├── spacer.blade.php
    ├── banner.blade.php
    ├── table.blade.php
    ├── qrcode.blade.php
    ├── chart.blade.php
    └── pagebreak.blade.php

Integração com o editor React

Endpoint típico

// routes/api.php
Route::post('/export/pdf', function (Request $request, PdfBlockRenderer $renderer) {
    $document = $request->validate([
        'document' => 'required|array',
        'document.blocks' => 'required|array',
        'document.pageSettings' => 'required|array',
        'document.globalStyles' => 'required|array',
    ])['document'];

    return $renderer->toPdf($document)->toResponse('document.pdf');
});

No React

import { useExport } from '@pdf-block/react';

function ExportButton() {
  const editorRef = useRef<PDFBuilderRef>(null);

  const handleExport = async () => {
    const doc = editorRef.current?.getDocument();
    if (!doc) return;

    const res = await fetch('/api/export/pdf', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ document: doc }),
    });

    const blob = await res.blob();
    const url = URL.createObjectURL(blob);
    window.open(url); // ou link de download
  };

  return <button onClick={handleExport}>Exportar PDF</button>;
}

Docker

Para ambientes Docker, use a imagem oficial do Puppeteer:

FROM node:20-slim

RUN apt-get update && apt-get install -y \
    chromium \
    --no-install-recommends && \
    rm -rf /var/lib/apt/lists/*

ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium

Ou use Gotenberg como alternativa ao Browsershot.

Desenvolvimento

# No monorepo
cd apps/laravel-sandbox
composer install
cp .env.example .env
php artisan serve

# Teste os endpoints:
curl -X POST http://localhost:8000/api/export/html \
  -H "Content-Type: application/json" \
  -d '{"document": {...}}'

Requisitos

  • PHP ≥ 8.2
  • Laravel ≥ 11.0
  • Node.js ≥ 18 (para Browsershot/Puppeteer)
  • Chromium ou Chrome instalado no servidor

Licença

MIT