定制 pdf-block/laravel 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

pdf-block/laravel

Composer 安装命令:

composer require pdf-block/laravel

包简介

Server-side PDF renderer for pdf-block DSL documents. Converts JSON DSL to native PDF via a local Chromium binary or a remote Browserless (v2) service.

README 文档

README

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.

Suporta dois drivers de renderização intercambiáveis:

Driver Como funciona Quando usar
local Chromium CLI no mesmo container que o PHP, via symfony/process Desenvolvimento e ambientes controlados
browserless HTTP POST a um serviço Browserless v2 (auto-hospedado ou gerenciado) via libcurl nativo Produção, escalabilidade horizontal, separação de responsabilidades

Instalação

composer require pdf-block/laravel

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

Publicar config (opcional)

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

Cria config/pdf-block.php no projeto com todas as opções disponíveis.

Publicar views (opcional)

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

Permite customizar os Blade templates 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');

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

API pública

$renderer = app(PdfBlockRenderer::class);

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

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

// Download (Content-Disposition: attachment)
return $pdf->toResponse('fatura.pdf');

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

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

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

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

Configuração

// config/pdf-block.php

return [
    // Driver ativo: 'local' ou 'browserless'
    'driver' => env('PDF_BLOCK_DRIVER', 'local'),

    'drivers' => [
        'local' => [
            'chrome_path' => env('PDF_BLOCK_CHROME_PATH', '/usr/bin/chromium'),
            'timeout'     => (int) env('PDF_BLOCK_TIMEOUT', 30),
            'chrome_args' => [ /* defaults otimizados — ver config/pdf-block.php */ ],
        ],

        'browserless' => [
            'url'        => env('PDF_BLOCK_BROWSERLESS_URL', 'http://browserless:3000'),
            'token'      => env('PDF_BLOCK_BROWSERLESS_TOKEN'),
            'timeout'    => (int) env('PDF_BLOCK_BROWSERLESS_TIMEOUT', 30),
            'wait_until' => env('PDF_BLOCK_BROWSERLESS_WAIT_UNTIL', 'load'),

            // Bloqueio de recursos no nível do Puppeteer — cancela requests
            // ANTES de ir pra rede. Ganho de 200–800ms por PDF.
            'reject_resource_types'  => ['media', 'websocket', 'eventsource', 'manifest', 'texttrack', 'other'],
            'reject_request_pattern' => ['google-analytics\\.com', 'googletagmanager\\.com', /* ... */],
        ],
    ],

    'fonts' => [
        'local_fonts' => [ /* 20 famílias corporativas — ver config/pdf-block.php */ ],
    ],
];

Variáveis de ambiente

Driver local:

PDF_BLOCK_DRIVER=local
PDF_BLOCK_CHROME_PATH=/usr/bin/chromium
PDF_BLOCK_TIMEOUT=30

Driver browserless:

PDF_BLOCK_DRIVER=browserless
PDF_BLOCK_BROWSERLESS_URL=http://browserless:3000
PDF_BLOCK_BROWSERLESS_TOKEN=
PDF_BLOCK_BROWSERLESS_TIMEOUT=30
PDF_BLOCK_BROWSERLESS_WAIT_UNTIL=load

Driver local

O driver padrão. Invoca o binário do Chromium diretamente via symfony/process com a flag --print-to-pdf. Não requer Node.js, Puppeteer ou Browsershot.

Pré-requisitos

Chromium instalado no servidor ou container:

# Debian/Ubuntu
sudo apt install chromium fonts-noto-color-emoji

# Alpine (Docker)
apk add chromium font-noto-emoji

fonts-noto-color-emoji: necessário para renderizar emojis. Sem ela, emojis aparecem como quadrados vazios no PDF.

Dockerfile (desenvolvimento)

FROM php:8.2-cli-bookworm

RUN apt-get update && apt-get install -y --no-install-recommends \
        chromium \
        fonts-noto-color-emoji \
        libzip-dev unzip git \
    && docker-php-ext-install zip \
    && rm -rf /var/lib/apt/lists/*

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

Produção com www-data (PHP-FPM)

O chrome_crashpad_handler usa ptrace(PTRACE_ATTACH) no processo pai do Chromium. Quando o PHP roda como www-data sem CAP_SYS_PTRACE, o kernel nega essa chamada e o Chromium recebe SIGTRAP.

Os flags --disable-crash-reporter e --disable-breakpad (já incluídos nos defaults) reduzem a frequência desse problema, mas não o eliminam em todos os builds do Chromium no Debian/Ubuntu.

Solução definitiva via setcap (requer libcap2-bin):

RUN apt-get install -y libcap2-bin \
    && setcap cap_sys_ptrace+ep /usr/lib/chromium/chrome_crashpad_handler

Alternativa recomendada para produção: use o driver browserless, onde o Chromium roda isolado em um container próprio — o problema de permissão não existe nesse contexto.

Driver browserless

Envia o HTML via POST /pdf para qualquer serviço compatível com a API Browserless v2. Elimina o Chromium do container PHP por completo.

O driver é implementado sobre libcurl nativo (ext-curl) — sem Guzzle. Motivos:

  • Roteamento confiável. libcurl emite sempre request-line em origin-form (POST /pdf HTTP/1.1), que é o único formato aceito pelo router do Browserless v2. Clientes de alto nível podem serializar em absolute-form para hosts sem TLD (ex.: browserless em rede Docker), resultando em HTTP 404 "No matching HTTP route handler".
  • Menor overhead. Sem wrappers PSR-7 / streams / middlewares — ~5 ms a menos por request e pegada de memória menor (útil em containers com mem_limit baixo).
  • Menos superfície de erro. Zero dependência transitiva.

O HTML gerado pelo Blade já carrega toda a lógica de dimensionamento: um script injeta @page { size: Wmm Hmm; margin: 0 } via beforeprint, medindo o scrollHeight real após fontes e imagens carregadas. O driver envia preferCSSPageSize: true para que o Puppeteer obedeça esse @page — tamanho, margens e orientação vêm direto da DSL via CSS, sem duplicação no JSON da requisição.

Otimizações ativas por padrão

Otimização Efeito
waitUntil: 'load' Fontes são 100% locais (fontconfig), imagens já são aguardadas pelo load. Usar networkidle0 só é necessário se o template dispara XHR/fetch após o load.
preferCSSPageSize: true Tamanho do papel lido do @page do HTML — evita duplicar config na request.
rejectResourceTypes Cancela media, websocket, eventsource, manifest, texttrack, other antes da rede.
rejectRequestPattern Bloqueia trackers conhecidos (Google Analytics, GTM, DoubleClick, Facebook Pixel, Hotjar, Mixpanel, Segment, FullStory, Clarity).
CURLOPT_TCP_NODELAY Desliga Nagle — request chega ao Browserless sem o delay de 40 ms.
Expect: header removido Evita handshake 100-continue que alguns proxies travam.
HTTP/1.1 forçado Garante request-line em origin-form compatível com o router do Browserless.

Docker Compose (desenvolvimento)

services:
  api:
    build: .
    env_file: .env
    depends_on:
      - browserless
    mem_limit: 64m

  browserless:
    image: ghcr.io/uotz/pdf-block-browserless:latest
    ports:
      - "3001:3000"
    environment:
      TOKEN: ""
      TIMEOUT: 30000
      CONCURRENT: "1"
      PREBOOT_CHROME: "false"
      ENABLE_DEBUGGER: "false"
      ENABLE_API_GET: "false"
      DEFAULT_BLOCK_ADS: "false"
      DEFAULT_STEALTH: "false"
      DEBUG: ""
      NODE_OPTIONS: "--max-old-space-size=96"
    mem_limit: 336m
# .env
PDF_BLOCK_DRIVER=browserless
PDF_BLOCK_BROWSERLESS_URL=http://browserless:3000

Budget de memória: com esse stack o pico observado é api ~50 MB + browserless ~150 MB por PDF de 1–5 páginas. Cabe confortavelmente em 400 MB totais.

DEFAULT_BLOCK_ADS=false é intencional. O ad-blocker embutido do Browserless baixa listas de filtros em background e pode travar o setContent. O bloqueio de trackers é feito no nível do driver via reject_request_pattern — mais previsível e sem dependência de DNS externo.

Serviço gerenciado (browserless.io)

PDF_BLOCK_DRIVER=browserless
PDF_BLOCK_BROWSERLESS_URL=https://production-sfo.browserless.io
PDF_BLOCK_BROWSERLESS_TOKEN=seu-token
PDF_BLOCK_BROWSERLESS_TIMEOUT=30

Driver customizado

Implemente a interface PdfBlock\Laravel\Contracts\PdfDriver e registre o binding no seu service provider:

use PdfBlock\Laravel\Contracts\PdfDriver;

// AppServiceProvider::register()
$this->app->bind(PdfDriver::class, MyCustomDriver::class);

Fontes locais

O pacote opera com fontes 100% locais — zero tráfego para fonts.googleapis.com em runtime. A imagem Docker do Browserless (ghcr.io/uotz/pdf-block-browserless:latest) já inclui as 20 famílias curadas abaixo, instaladas no fontconfig do sistema:

Sans-serif: Inter, Roboto, Open Sans, Lato, Source Sans 3, Noto Sans, Work Sans, DM Sans, Montserrat Serif: Spectral (padrão), Merriweather, Lora, Source Serif 4, PT Serif, EB Garamond, Libre Baskerville, Playfair Display, Noto Serif Display: Oswald Mono: Roboto Mono

A lista vive em config/pdf-block.php → fonts.local_fonts e está sincronizada com o FontPicker do pacote React.

Para adicionar uma família: atualize docker/install-fonts.sh (baixa os arquivos no build), config/pdf-block.php → local_fonts e o FontPicker do React. O workflow publish-browserless.yml detecta mudanças em install-fonts.sh e rebuilda automaticamente a imagem.

Imagem Docker pronta

A imagem ghcr.io/uotz/pdf-block-browserless é a maneira recomendada de rodar o driver browserless. Ela estende ghcr.io/browserless/chromium:latest com:

  • Noto Color Emoji como família de emoji (mesmo visual do driver local).
  • As 20 fontes corporativas instaladas no fontconfig.
  • Sem alteração de entrypoint — todas as variáveis de ambiente do Browserless v2 continuam funcionando.

Tags disponíveis:

Tag Uso
latest Última build da branch main
vX.Y.Z Vinculada ao release correspondente do pacote Composer
vX.Y Major.minor (atualiza em novos patches)

Veja docs/image-build.md para detalhes do pipeline de build e como gerar uma nova versão da imagem.

Diagnóstico

O sandbox inclui dois endpoints para validar a configuração de cada driver sem gerar um PDF completo:

Verificar driver local

GET /api/diagnostics/local

Executa chromium --version e retorna:

{
  "driver": "local",
  "status": "ok",
  "chrome_path": "/usr/bin/chromium",
  "version": "Chromium 124.0.6367.82 built on Debian 12.5, running on Debian 12.5"
}

Verificar driver browserless

GET /api/diagnostics/browserless

Faz um request de health check ao serviço e retorna:

{
  "driver": "browserless",
  "status": "ok",
  "url": "http://browserless:3000",
  "response_ms": 42,
  "service": { "Browser": "Chrome/124...", "Protocol": "1.3" }
}

Em caso de erro, ambos os endpoints retornam HTTP 500/502 com "status": "error" e uma "message" descritiva.

Arquitetura

DSL JSON (do editor React)
    │
    ▼
PdfBlockRenderer::toHtml()
    │  Blade templates renderizam cada bloco
    │  com inline styles idênticos ao editor
    ▼
HTML string standalone
    │
    ├─── driver: local ──────────────────────┐
    │    LocalDriver::render()               │
    │    Chromium CLI --print-to-pdf         │
    │    (symfony/process)                   │
    │                                        │
    └─── driver: browserless ────────────────┤
         BrowserlessDriver::render()         │
         POST {url}/pdf (libcurl nativo)     │
         HTTP/1.1 origin-form · TCP_NODELAY  │
                                             ▼
                                      PdfResult (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).

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", "locale": "pt-BR", "tags": [] },
  "pageSettings": {
    "paperSize": { "preset": "a4", "width": 210, "height": 297 },
    "orientation": "portrait",
    "margins": { "top": 20, "right": 20, "bottom": 20, "left": 20 },
    "defaultFontFamily": "Spectral, serif"
  },
  "globalStyles": {
    "pageBackground": "#ffffff",
    "contentBackground": "#ffffff",
    "defaultFontColor": "#333333"
  },
  "blocks": [...]
}

Blocos suportados

Bloco Descrição
text Rich text via TipTap JSON → HTML (ueberdosis/tiptap-php)
image Imagem com alignment, objectFit, bordas
button Link estilizado como botão (clicável no PDF)
divider Linha horizontal
spacer Espaço vertical
banner Imagem de fundo com overlay e textos
table Tabela com header, zebra, bordas
qrcode QR code
chart Gráfico de barras (CSS puro)
pagebreak 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/:

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 Laravel

// 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('documento.pdf');
});

No React

const editorRef = useRef<PDFBuilderRef>(null);

const exportPdf = 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 a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'documento.pdf';
  a.click();
};

Requisitos

  • PHP ≥ 8.2
  • Extensões PHP: ext-curl, ext-json
  • Laravel 11.x ou 12.x
  • Driver local: Chromium instalado no servidor
  • Driver browserless: Serviço Browserless v2 acessível via HTTP

Licença

MIT

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.

Suporta dois drivers de renderização intercambiáveis:

Driver Como funciona Quando usar
local Chromium CLI no mesmo container que o PHP Desenvolvimento e ambientes controlados
browserless HTTP POST a um serviço Browserless v1 (auto-hospedado ou gerenciado) Produção, servidores com www-data sem CAP_SYS_PTRACE

Instalação

composer require pdf-block/laravel

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

Publicar config (opcional)

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

Cria config/pdf-block.php no projeto com todas as opções disponíveis.

Publicar views (opcional)

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

Permite customizar os Blade templates 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');

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

API pública

$renderer = app(PdfBlockRenderer::class);

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

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

// Download (Content-Disposition: attachment)
return $pdf->toResponse('fatura.pdf');

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

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

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

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

Configuração

// config/pdf-block.php

return [
    // Driver ativo: 'local' ou 'browserless'
    'driver' => env('PDF_BLOCK_DRIVER', 'local'),

    'drivers' => [
        'local' => [
            'chrome_path' => env('PDF_BLOCK_CHROME_PATH', '/usr/bin/chromium'),
            'timeout'     => (int) env('PDF_BLOCK_TIMEOUT', 30),
            'chrome_args' => [
                '--headless',
                '--ozone-platform=headless',
                '--no-sandbox',
                '--disable-gpu',
                '--disable-dev-shm-usage',
                '--run-all-compositor-stages-before-draw',
                '--disable-crash-reporter',
                '--disable-breakpad',
            ],
        ],

        'browserless' => [
            'url'     => env('PDF_BLOCK_BROWSERLESS_URL', 'http://browserless:3000'),
            'timeout' => (int) env('PDF_BLOCK_BROWSERLESS_TIMEOUT', 30),
        ],
    ],
];

Variáveis de ambiente

Driver local:

PDF_BLOCK_DRIVER=local
PDF_BLOCK_CHROME_PATH=/usr/bin/chromium
PDF_BLOCK_TIMEOUT=30

Driver browserless:

PDF_BLOCK_DRIVER=browserless
PDF_BLOCK_BROWSERLESS_URL=http://browserless:3000
PDF_BLOCK_BROWSERLESS_TIMEOUT=30

Driver local

O driver padrão. Invoca o binário do Chromium diretamente via symfony/process com a flag --print-to-pdf. Não requer Node.js, Puppeteer ou Browsershot.

Pré-requisitos

Chromium instalado no servidor ou container:

# Debian/Ubuntu
sudo apt install chromium fonts-noto-color-emoji

# Alpine (Docker)
apk add chromium font-noto-emoji

fonts-noto-color-emoji: necessário para renderizar emojis. Sem ela, emojis aparecem como quadrados vazios no PDF.

Dockerfile (desenvolvimento)

FROM php:8.2-cli-bookworm

RUN apt-get update && apt-get install -y --no-install-recommends \
        chromium \
        fonts-noto-color-emoji \
        libzip-dev unzip git \
    && docker-php-ext-install zip \
    && rm -rf /var/lib/apt/lists/*

COPY --from=composer:2 /usr/bin/composer /usr/bin/composer

Produção com www-data (PHP-FPM)

O chrome_crashpad_handler usa ptrace(PTRACE_ATTACH) no processo pai do Chromium. Quando o PHP roda como www-data sem CAP_SYS_PTRACE, o kernel nega essa chamada e o Chromium recebe SIGTRAP.

Os flags --disable-crash-reporter e --disable-breakpad (já incluídos nos defaults) reduzem a frequência desse problema, mas não o eliminam em todos os builds do Chromium no Debian/Ubuntu.

Solução definitiva via setcap (requer libcap2-bin):

RUN apt-get install -y libcap2-bin \
    && setcap cap_sys_ptrace+ep /usr/lib/chromium/chrome_crashpad_handler

Alternativa recomendada para produção: use o driver browserless, onde o Chromium roda como root em container isolado — o problema de permissão não existe nesse contexto.

Driver browserless

Envia o HTML via POST /pdf para qualquer serviço compatível com a API Browserless v1. Elimina o Chromium do container PHP por completo.

O HTML gerado pelo Blade já carrega toda a lógica de dimensionamento: um script injeta @page { size: Wmm Hmm; margin: 0 } via beforeprint, medindo o scrollHeight real após fontes e imagens carregadas. O driver envia preferCSSPageSize: true para que o Puppeteer obedeça esse @page — tamanho, margens e orientação vêm diretamente da DSL via CSS, sem duplicação no JSON da requisição.

Docker Compose (desenvolvimento)

# docker-compose.yml
services:
  api:
    build: .
    env_file: .env
    depends_on:
      - browserless

  browserless:
    image: ghcr.io/browserless/chromium:latest
    ports:
      - "3000:3000"
    environment:
      TOKEN: ""         # sem autenticação (desenvolvimento)
      TIMEOUT: 30000
      CONCURRENT: 5
# .env
PDF_BLOCK_DRIVER=browserless
PDF_BLOCK_BROWSERLESS_URL=http://browserless:3000

Serviço gerenciado (browserless.io)

PDF_BLOCK_DRIVER=browserless
PDF_BLOCK_BROWSERLESS_URL=https://chrome.browserless.io
PDF_BLOCK_BROWSERLESS_TIMEOUT=30

Driver customizado

Implemente a interface PdfBlock\Laravel\Contracts\PdfDriver e registre o binding no seu service provider:

use PdfBlock\Laravel\Contracts\PdfDriver;

// AppServiceProvider::register()
$this->app->bind(PdfDriver::class, MyCustomDriver::class);

Diagnóstico

O sandbox inclui dois endpoints para validar a configuração de cada driver sem gerar um PDF completo:

Verificar driver local

GET /api/diagnostics/local

Executa chromium --version e retorna:

{
  "driver": "local",
  "status": "ok",
  "chrome_path": "/usr/bin/chromium",
  "version": "Chromium 124.0.6367.82 built on Debian 12.5, running on Debian 12.5"
}

Verificar driver browserless

GET /api/diagnostics/browserless

Faz um request de health check ao serviço e retorna:

{
  "driver": "browserless",
  "status": "ok",
  "url": "http://browserless:3000",
  "response_ms": 42,
  "service": { "Browser": "Chrome/124...", "Protocol": "1.3" }
}

Em caso de erro, ambos os endpoints retornam HTTP 500/502 com "status": "error" e uma "message" descritiva.

Arquitetura

DSL JSON (do editor React)
    │
    ▼
PdfBlockRenderer::toHtml()
    │  Blade templates renderizam cada bloco
    │  com inline styles idênticos ao editor
    ▼
HTML string standalone
    │
    ├─── driver: local ──────────────────────┐
    │    LocalDriver::render()               │
    │    Chromium CLI --print-to-pdf         │
    │    (symfony/process)                   │
    │                                        │
    └─── driver: browserless ────────────────┤
         BrowserlessDriver::render()         │
         POST {url}/pdf com opções Puppeteer │
         (illuminate/http)                   │
                                             ▼
                                      PdfResult (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).

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", "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": [...]
}

Blocos suportados

Bloco Descrição
text Rich text via TipTap JSON → HTML (ueberdosis/tiptap-php)
image Imagem com alignment, objectFit, bordas
button Link estilizado como botão (clicável no PDF)
divider Linha horizontal
spacer Espaço vertical
banner Imagem de fundo com overlay e textos
table Tabela com header, zebra, bordas
qrcode QR code
chart Gráfico de barras (CSS puro)
pagebreak 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/:

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 Laravel

// 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('documento.pdf');
});

No React

const editorRef = useRef<PDFBuilderRef>(null);

const exportPdf = 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 a = document.createElement('a');
  a.href = URL.createObjectURL(blob);
  a.download = 'documento.pdf';
  a.click();
};

Migração da config anterior

Se você usava uma versão anterior do pacote, a estrutura de configuração foi alterada. Re-publique o arquivo de config e ajuste suas variáveis de ambiente:

Antes (configuração plana):

return [
    'chrome_path' => env('PDF_BLOCK_CHROME_PATH', '/usr/bin/chromium'),
    'timeout'     => (int) env('PDF_BLOCK_TIMEOUT', 30),
    'chrome_args' => [...],
];

Depois (configuração por driver):

return [
    'driver' => env('PDF_BLOCK_DRIVER', 'local'),
    'drivers' => [
        'local' => [
            'chrome_path' => env('PDF_BLOCK_CHROME_PATH', '/usr/bin/chromium'),
            'timeout'     => (int) env('PDF_BLOCK_TIMEOUT', 30),
            'chrome_args' => [...],
        ],
        'browserless' => [...],
    ],
];

As variáveis PDF_BLOCK_CHROME_PATH e PDF_BLOCK_TIMEOUT continuam funcionando — apenas foram movidas para dentro de drivers.local.

# Após atualizar o pacote:
php artisan vendor:publish --tag=pdf-block-config --force

Requisitos

  • PHP ≥ 8.2
  • Laravel ≥ 11.0
  • Driver local: Chromium instalado no servidor
  • Driver browserless: Serviço Browserless v1 acessível via HTTP

Licença

MIT

pdf-block/laravel 适用场景与选型建议

pdf-block/laravel 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 86 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 05 月 14 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「DSL」 「pdf」 「document」 「renderer」 「headless」 「laravel」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 pdf-block/laravel 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 pdf-block/laravel 我们能提供哪些服务?
定制开发 / 二次开发

基于 pdf-block/laravel 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 86
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 54
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-05-14