Pular para o conteúdo

Graphics: primitivas de caminho, sombreamento e transformação

O módulo Graphics transforma a intenção de desenho em operadores gráficos do Portable Document Format (PDF). Ele abrange caminhos, estilos de linha, espaços de cores, transformações, sombreamentos, padrões, meios-tons e carregamento de imagens.

Terminal window
composer require nextpdf/core:^3

Graphics é a camada de desenho vetorial e raster. Ele produz sequências de operadores que os módulos ContentStream e Writer serializam em um PDF. Um content stream (fluxo de conteúdo) codifica o conteúdo da página como uma sequência ordenada de operadores gráficos, conforme a International Organization for Standardization (ISO) 32000-2 §8. O módulo emite esses operadores; ele não grava o arquivo.

DrawingEngine é a principal interface de programação de aplicações (API). Ele é um construtor fluente e com estado. Cada setter retorna self, registra uma mudança de estado gráfico ou um operador de pintura de caminho e acrescenta essa saída a um buffer interno, que você lê com getStream(). O engine modela diretamente o estado gráfico do PDF: largura de linha, estilo de linha, cor de traço e de preenchimento, alfa e modo de mesclagem, limite de mitra, máscara suave, recorte, sobreimpressão, achatamento (flatness), suavidade (smoothness), intenção de renderização, geração de preto e remoção de subcor. Cada um desses elementos mapeia para um operador documentado. Os setters de cor aceitam um objeto de valor Color ou um ColorSpace explícito, de modo que os espaços de dispositivo e os baseados na Comissão Internacional de Iluminação (CIE) usem o mesmo formato de chamada.

O engine é acompanhado por três famílias de recursos. A primeira é a entrada de imagens. ImageLoader decodifica um arquivo ou um blob em memória para um ImageLoadResult. ImageRegistry remove duplicatas e rastreia as imagens decodificadas com um MemoryReport, de modo que documentos grandes permaneçam dentro de um orçamento de memória. Para importação vetorial, SvgParser e EpsParser traduzem entradas Scalable Vector Graphics (SVG) e Encapsulated PostScript (EPS) para o mesmo fluxo de operadores, com getBoundingBox() exposto para o layout. A terceira família é a fidelidade das cores de dispositivo: sombreamentos (ShadingManager, as famílias Type2/Type3 e de malha), padrões (PatternFill), meios-tons (Type1/Type5/Type6/Type10/Type16), funções de transferência e espaços de cores baseados no International Color Consortium (ICC).

TransformEngine é um componente complementar focado em transformações de coordenadas. Ele envolve uma transformação com startTransform() e stopTransform(), que emitem o par save/restore q e Q. Ele fornece auxiliares nomeados para transformações afins: scale, translate, rotate, skew, mirrorH e mirrorV. Cada auxiliar aceita um pivô opcional. A matriz de transformação mapeia um espaço de coordenadas interno para o espaço de coordenadas de destino. Esse é o mesmo modelo que a ISO 32000-2 aplica aos domínios de sombreamento — §8.7.4.

O gerenciamento de cores segue o Architectural Decision Record (ADR)-012: os espaços de cores ICCBased e baseados em CIE emitem operadores de content stream cs/CS explícitos em vez de depender do fallback de cor de dispositivo. Os perfis ICC são empacotados em um fluxo ICCBased com a contagem de componentes correta conforme a ISO 32000-2 §8.6.5.5.

ClasseMétodos principaisFunção
DrawingEnginegetStream(), reset(), setLineWidth(), setLineStyle(), setDrawColor(), setFillColor(), setAlpha(), setSoftMask(), clip(), setOverprint(), setRenderingIntent(), line(), rect(), circle(), ellipse(), polygon(), linearGradient()Construtor com estado para operadores de caminho + estado gráfico
TransformEnginestartTransform(), stopTransform(), scale(), translate(), rotate(), skew(), mirrorH(), mirrorV(), getStream()Transformações afins de coordenadas
ImageLoaderload(string $filePath), loadFromString(string $data, string $mimeType)Decodifica imagens para ImageLoadResult
ImageRegistryload(), loadFromString(), getMetadata(), memoryUsage(), reset()Cache de imagens com remoção de duplicatas e relatório de memória
SvgParserparse(), parseFile()Traduz SVG para o fluxo de operadores
EpsParserparse(), parseFile(), getBoundingBox()Traduz EPS para o fluxo de operadores
ShadingManagerregistro de sombreamento + emissão de dicionárioSombreamentos axiais, radiais e de malha
Halftone (abstrato)halftoneType(), toDict(), hasStream(), getStream()Type 1/5/6/10/16 — telas de meio-tom

Execute composer docs:generate-api-php -- --module=Graphics para gerar a tabela completa de PHPDoc.

Origem: examples/06-colors-and-drawing.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Graphics\Color;
use NextPDF\Graphics\DrawingEngine;
use NextPDF\Graphics\LineStyle;
$engine = new DrawingEngine();
$engine
->setLineWidth(1.5)
->setDrawColor(Color::rgb(0, 51, 102))
->setFillColor(Color::rgb(230, 240, 250))
->rect(20.0, 20.0, 160.0, 80.0)
->line(20.0, 110.0, 180.0, 110.0, new LineStyle(dash: [3.0, 2.0]));
$contentStreamBytes = $engine->getStream();

Este exemplo combina um registro de imagens com relatório de memória e um bloco de transformação. Ele espelha a estrutura usada em examples/07-images.php e examples/21-transforms.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Graphics\DrawingEngine;
use NextPDF\Graphics\ImageRegistry;
use NextPDF\Graphics\TransformEngine;
$registry = new ImageRegistry();
$image = $registry->load('/srv/assets/logo.png');
$report = $registry->memoryUsage();
if ($report->bytes > 32 * 1024 * 1024) {
// Decoded image cache exceeded the budget — reset before the next page.
$registry->reset();
}
$transform = new TransformEngine();
$transform
->startTransform()
->translate(40.0, 700.0)
->scale(0.5, 0.5)
->stopTransform();
$engine = new DrawingEngine();
$engine->reset();
$page = $transform->getStream() . $engine->getStream();
  • DrawingEngine tem estado. Chame reset() entre páginas independentes para que o estado gráfico anterior não vaze para o fluxo seguinte.
  • TransformEngine exige um par correspondente startTransform()/stopTransform(). Um bloco desbalanceado deixa um q pendente e corrompe a pilha save/restore mais adiante no Writer.
  • setSoftMask(), setOverprint(), setBlackGeneration() e setUnderColorRemoval() gravam marcadores de estado gráfico estendido. Em um perfil que rejeita o recurso, eles ficam inertes. Verifique a proteção do perfil antes de confiar no resultado visual.
  • ImageRegistry remove duplicatas por conteúdo. Dois caminhos com bytes idênticos compartilham um objeto. Não presuma que haverá uma imagem PDF por chamada de load().
  • EpsParser::getBoundingBox() retorna a caixa delimitadora (bounding box) analisada, não a caixa da página. Aplique seu próprio recorte se o EPS ultrapassar o retângulo de destino.
  • A compensação de ponto preto é orientativa e baseada em marcadores. Por si só, ela não transforma pixels.

Duas mudanças no lado do produtor são disruptivas (breaking). Ambas transformam uma corrupção antes silenciosa em uma falha explícita no local da chamada.

A validação de entrada agora lança exceção (nota de migração). A entrada de desenho é validada antes de chegar ao fluxo de operadores, e valores malformados são rejeitados com InvalidArgumentException. O código que passava NaN, Infinity ou valores fora do intervalo antes produzia operadores corrompidos silenciosamente; a mesma entrada agora gera uma exceção. As restrições validadas são:

  • O alfa da cor deve ser finito e estar dentro de [0, 1].
  • Os operandos da matriz de transformação atual (CTM), as dimensões do template, as coordenadas dos vértices de gradiente e as coordenadas das patches de malha devem ser finitos — sem NaN ou Infinity.
  • Uma flag de borda de patch de gradiente deve ser uma de {0, 1, 2, 3}.
  • Os parâmetros de função Type 2/3/4 e os parâmetros de meio-tom têm os limites verificados.
  • Os nomes de corantes são escapados.
  • O nome de uma camada de grupo de conteúdo opcional (OCG) não pode ser vazio.

Audite os locais de chamada que calculam coordenadas ou alfa a partir de dados upstream antes de atualizar: um valor que antes passava agora é um erro grave.

O /N ICCBased é fail-closed por padrão. A saída plain-PDF rejeita um espaço de cores ICCBased cuja contagem de componentes /N esteja fora de {1, 3, 4} e concilia o /N declarado com o perfil incorporado e o espaço /Alternate. Isso segue a regra da ISO 32000-2 §8.6.5.5 para o fluxo ICCBased, que carrega /N junto a um espaço /Alternate. Um perfil ICC de N canais, como um perfil hexachrome com N = 6, é retido somente quando um perfil PDF/A ou PDF/X está ativo, com adesão opt-in via IccConformancePolicy::ProfileGated. Isso é uma proteção estrutural sobre a contagem de componentes, não uma declaração de certificação PDF/A ou PDF/X.

A emissão de operadores é linear em relação ao número de chamadas de desenho: O(n) acréscimos a um buffer, sem reprocessamento. O custo de decodificação de imagem vem do codec e da contagem de pixels, não do registro. A remoção de duplicatas por hash de conteúdo do registro é a principal alavanca para documentos grandes: ativos reutilizados custam uma decodificação e um objeto PDF. O performance_budget para a carga de trabalho de referência deste módulo é de 1500 ms de tempo de relógio e 64 MB de pico. Use ImageRegistry::memoryUsage() para observar a pegada de memória das imagens decodificadas e reset() para liberá-la entre grupos de páginas.

SvgParser e EpsParser consomem entrada vetorial não confiável. Trate ambos como analisadores de dados hostis. Imponha limites de tamanho de entrada antes de chamar parse(). Execute a extração em um worker restrito quando a origem vier de quem usa o sistema. EPS é um dialeto de PostScript. O analisador traduz um subconjunto restrito e não executa um interpretador geral, mas você ainda deve limitar o tamanho da entrada e o tempo de análise. Os carregadores de imagem decodificam codecs de terceiros. Mantenha as extensões de imagem do runtime atualizadas e limite as dimensões decodificadas. Consulte o modelo de ameaças do engine em /modules/core/security/ para o limite de confiança e as orientações de isolamento de worker.

O módulo emite estruturas de operadores gráficos PDF consistentes com a ISO 32000-2 §8, dicionários de espaços de cores ICCBased conforme o §8.6.5.5 e dicionários de sombreamento cujos Domain, Function, Matrix e BBox seguem o §8.7.4. Estes são fatos de implementação: src/Graphics/ produz os formatos de operador e de dicionário, e tests/Unit/Graphics/ mais as baselines tests/Golden/PdfWriter/PdfWriterShadingGoldenBaselineSmokeTest e PdfWriterExtGStateGoldenSmokeTest os exercitam. Eles não constituem uma declaração de conformidade ponta a ponta com PDF 2.0 ou PDF/X. A conformidade do documento completo é validada separadamente pelo oracle e pelas suítes golden descritas em /modules/core/conformance/. O comportamento de perfil para ICC OutputIntents é decidido pelo ADR-011 e pelo ADR-012, não por este módulo isoladamente.