Texto: integração de shaping, CJK e tratamento de runs
Visão geral
Seção intitulada “Visão geral”O módulo de texto define a fronteira de shaping. Ele expõe uma interface enxuta que transforma um run em 8-bit Unicode Transformation Format (UTF-8) em glifos posicionados, seleciona um backend OpenType real quando há um disponível, recorre a um fallback determinístico quando não há nenhum e fornece um registro para shapers específicos de script.
Instalação
Seção intitulada “Instalação”composer require nextpdf/core:^3Visão conceitual
Seção intitulada “Visão conceitual”ShaperInterface conecta o pipeline de layout de texto a um motor de shaping OpenType. Ela é intencionalmente enxuta: um único método shape() consome um ShaperInput e retorna um ShapingResult. Esse tipo de retorno é a única saída visível para os consumidores. As implementações não devem vazar detalhes internos do motor de shaping, e o retorno tipado impõe essa fronteira. ShapingResult carrega a lista de registros GlyphRun, o texto de origem ecoado, o script e a direção, além de uma tag shaperImpl que identifica o backend que produziu o resultado.
A seleção de backend é explícita e informa a capacidade sem suposições. ShaperFactory executa uma única sondagem de capacidade. Se o host tiver um binding HarfBuzz funcional, create() retorna o shaper baseado no HarfBuzz. Caso contrário, retorna NullShaper. NullShaper é um fallback de passagem direta. Ele emite um glifo sintético por codepoint Unicode, com avanços zero e offsets zero. Ele marca o resultado para que a observabilidade possa detectar o fallback e deixa a resolução dos avanços para o módulo de métricas de fonte. Esse caminho é uma degradação documentada, não shaping completo. Substituição, ligaduras, posicionamento de marcas e formas contextuais exigem o backend real. wouldUseRealShaper() é um predicado de diagnóstico. Em código de produção, ramifique conforme a tag shaperImpl do resultado.
O shaping específico de script é uma interface de provedor de serviço (SPI), não uma implementação embutida. ScriptShaperRegistry é um registro no estilo PHP Standards Recommendation 11 (PSR-11) que resolve um MongolianShaperInterface ou TibetanShaperInterface pela tag de script International Organization for Standardization (ISO) 15924. O registro armazena as chaves sem distinguir maiúsculas de minúsculas e depende de uma única fonte de verdade para validar códigos de script. O registro e as interfaces de shapers por script formam um contrato congelado, de modo que uma extensão pode registrar um provedor da Phase-12 sem alterar os pontos de chamada. O motor entrega a fronteira. Os consumidores fornecem os provedores de scripts complexos.
O tratamento de runs de chinês, japonês e coreano (CJK) fica na fronteira de codificação da tipografia. Uma face TrueType CJK incorporada é emitida como uma fonte Type 0 com um CMap Identity-H e um descendente CIDFontType2, conforme tratado em ISO 32000-2 §9.7.4 (digest de retrieval-augmented generation (RAG) truncado pelo limite da licença; registrado em _downgraded-claims-o3.md). Quando o programa TrueType está incorporado, o CIDFont Type 2 mapeia identificadores de caractere para índices de glifo por meio da entrada CIDToGIDMap, conforme tratado em ISO 32000-2 §9 (o digest fixado pela página de contrato B1). O subsetter preserva a numeração original dos glifos, de modo que um /CIDToGIDMap /Identity continue válido para o subconjunto. CjkFontValidator verifica se uma fonte candidata cobre os blocos Unicode de que um script precisa antes de essa fonte ser escolhida.
Superfície da API
Seção intitulada “Superfície da API”| Tipo | Categoria | Membros principais | Estabilidade | Desde |
|---|---|---|---|---|
ShaperInterface | interface | shape(ShaperInput): ShapingResult | estável | 3.2.0 |
ShaperFactory | final class | default(), create(), wouldUseRealShaper() | estável | 3.2.0 |
NullShaper | final readonly class | shaper de fallback de passagem direta | estável | 3.2.0 |
ShapingResult | final readonly class | $glyphRuns, $originalText, $script, $direction, $shaperImpl | estável | 3.2.0 |
ScriptShaperRegistry | final class | registerMongolian(), getMongolian(), hasMongolian() e os equivalentes para tibetano | estável | 3.1.0 |
CjkFontValidator | final class | validateCoverage(), detectScript(), isCjkCodepoint() | estável | 1.0.0 |
O formato dos métodos register*, get* e has* de ScriptShaperRegistry e das interfaces de shapers por script é um contrato congelado. Por design, ShapingResult é a única saída do shaper visível para os consumidores.
Exemplo de código — Início rápido
Seção intitulada “Exemplo de código — Início rápido”<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Font\Shaper\ShaperFactory;use NextPDF\Font\Shaper\ShaperImpl;
$factory = ShaperFactory::default();$shaper = $factory->create();
// Branch on the result tag, not on the concrete class.$wouldShape = $factory->wouldUseRealShaper() ? 'HarfBuzz backend available' : 'NullShaper fallback (degraded — no substitution or positioning)';
echo $wouldShape, "\n";ShaperFactory::default() conecta a sondagem de capacidade de produção. create() memoiza o backend selecionado durante todo o ciclo de vida da factory. Use wouldUseRealShaper() e a tag shaperImpl em cada resultado para inspecionar a capacidade.
Exemplo de código — Produção
Seção intitulada “Exemplo de código — Produção”<?php
declare(strict_types=1);
require_once __DIR__ . '/../../vendor/autoload.php';
use NextPDF\Text\Shaping\MongolianShaperInterface;use NextPDF\Text\Shaping\ScriptShaperRegistry;
final readonly class ComplexScriptBootstrap{ public function __construct(private ScriptShaperRegistry $registry) {}
/** * Register a consumer-supplied Mongolian shaper provider at boot so * the layout pipeline can resolve it by ISO 15924 script tag. */ public function register(MongolianShaperInterface $mongolian): void { $this->registry->registerMongolian($mongolian); }
public function hasMongolian(): bool { return $this->registry->hasMongolian(); }}O registro é o ponto de integração para provedores de scripts complexos. O motor entrega a fronteira e o formato congelado dos acessadores. Os consumidores fornecem as implementações para mongol e tibetano.
Casos extremos e armadilhas
Seção intitulada “Casos extremos e armadilhas”- Um resultado de
NullShapertem avanços zero e offsets zero. Não alimente essas posições diretamente no layout de texto. Resolva os avanços a partir do módulo de métricas de fonte e detecte o fallback por meio da tagshaperImpl. - Uma entrada vazia produz uma lista
glyphRunsvazia, não um run vazio. O código de iteração do consumidor não precisa de um caso especial para um run de comprimento zero. ScriptShaperRegistrynão implementaPsr\Container\ContainerInterfacediretamente, de modo que os acessadores tipados mantêm o tipo de retorno restringido na análise estática. UsegetMongolian()egetTibetan(), não umget()genérico.- As tags de script são comparadas pelo valor canônico alpha-4 da ISO 15924 e armazenadas sem distinguir maiúsculas de minúsculas. Passe
MongouTibt. O uso de maiúsculas e minúsculas não afeta a busca. - Os caracteres da CJK Extension B ficam no plano 2 do Unicode e forçam uma subtabela cmap Format 12 no subconjunto. O caminho de codificação trata disso. Não presuma que o plano multilíngue básico cobre todo o texto CJK.
Desempenho
Seção intitulada “Desempenho”A sondagem de capacidade é executada uma única vez por instância de ShaperFactory, e o backend é memoizado, de modo que chamadas repetidas de create() não têm custo. NullShaper é linear em relação à contagem de codepoints do run de entrada e não realiza nenhuma operação de input/output (I/O). A resolução de ScriptShaperRegistry é uma busca por chave em tempo constante. CjkFontValidator amostra os codepoints em intervalos em vez de testar cada um, o que mantém as verificações de cobertura baratas mesmo com uma fonte CJK de 20,000 glifos. O performance_budget de 1500 ms de tempo de parede e 64 MB de pico cobre uma execução típica. No shaping real, o custo dominante é o backend OpenType. Esse custo fica fora do escopo deste módulo quando o fallback está ativo.
Notas de segurança
Seção intitulada “Notas de segurança”A fronteira do shaper consome uma string UTF-8. NullShaper tolera UTF-8 malformado dividindo da melhor forma possível em vez de lançar erro, porque o contrato de fallback documentado já é “nenhum shaping real”. O chamador está preparado para uma saída de baixa qualidade. O contrato de cluster por offset de byte usa comprimento orientado a bytes, o que é correto para entrada multibyte e evita um defeito de mapeamento de cluster com erro de um codepoint. Quando presente, o backend real é uma biblioteca nativa de terceiros. Trate a entrada dele como não confiável e limite o comprimento do run a montante. O registro de shapers por script armazena provedores fornecidos pelo consumidor. Essas implementações ficam dentro da fronteira de confiança do consumidor, não da fronteira do motor.
Conformidade
Seção intitulada “Conformidade”| Afirmação | Norma | Cláusula | Evidência |
|---|---|---|---|
Uma face TrueType CJK incorporada é emitida como uma fonte Type 0 com um CMap Identity-H e um descendente CIDFontType2. | ISO 32000-2 | §9.7.4 | Digest de retrieval-augmented generation (RAG) truncado pelo limite da licença; prefixo 7a5258772f508e3b, consulte _downgraded-claims-o3.md |
Um CIDFont Type 2 incorporado mapeia identificadores de caractere para índices de glifo por meio de CIDToGIDMap. | ISO 32000-2 | §9 |
Ambas as cláusulas são parafraseadas. A segunda é fixada por digest (reutilizada da página de contrato B1), e a primeira é corroborada pelo ADR-013 e pela visão geral do desenvolvedor do cmap-encoder. O NextPDF não reproduz texto normativo. O backend do shaper é independente da conformidade com o Portable Document Format (PDF). As afirmações de conformidade aqui dizem respeito à emissão do dicionário de fonte CJK produzido pela fronteira de codificação. O ADR-013 e a visão geral do desenvolvedor do cmap-encoder documentam esse caminho com mais detalhes.
Contexto comercial
Seção intitulada “Contexto comercial”Um pipeline avançado de pré-processamento de texto e serviços de extração se baseiam na fronteira do shaper do Core e nos tipos de valor de tratamento de runs. O módulo de texto do Core entrega a fronteira, o fallback e o registro de shapers por script sem licença. A ausência do link de conversão é intencional.
Veja também
Seção intitulada “Veja também”- Tipografia: registro, subsetting, CMap, codificação, BiDi — a fronteira de codificação e o motor de texto bidirecional.
- Fonte: tipos de valor, incorporação, fallback — o valor
FontInforeferenciado pela entrada do shaper. - Contracts / Typography — o contrato do pré-processador de texto a montante do shaping.