Garanta cópia e colagem corretas em PDFs com CJK e árabe
Em resumo
Seção intitulada “Em resumo”Crie um arquivo Portable Document Format (PDF) com texto em chinês, japonês, coreano (CJK) e árabe que seja copiado e colado como os caracteres lógicos originais. O motor mapeia cada glifo para Unicode por meio de um CMap /ToUnicode, canonicaliza o resultado com Normalization Form Compatibility Composition (NFKC) e envolve as sequências moldadas ou com espaçamento entre letras em um /Span que carrega /ActualText. Registre as fontes e escreva o conteúdo; a extração continuará correta. Verifique a extração com pdftotext/Poppler, não com PyMuPDF; o veraPDF valida PDF/Universal Accessibility 2 (PDF/UA-2), não a extração.
Instalação
Seção intitulada “Instalação”composer require nextpdf/coreRegistre uma fonte CJK, como a Noto Sans CJK, e uma fonte com suporte a árabe cujo mapa de caracteres cubra o bloco Arabic Presentation Forms-B, como a Noto Naskh Arabic. Incorpore apenas fontes que você tenha licença para incorporar.
Visão geral conceitual
Seção intitulada “Visão geral conceitual”Os códigos de glifo em um stream de conteúdo não são Unicode. Um CMap /ToUnicode mapeia cada código de volta para Unicode para que um leitor consiga extrair o texto (ISO 32000-2 §9.10). O motor canonicaliza esses valores com Normalization Form Compatibility Composition (NFKC), conforme definido pelo Unicode UAX #15. Tanto um ideograma de compatibilidade CJK quanto uma forma de apresentação árabe são mapeados para seu caractere base; assim, a busca e a cópia retornam o texto canônico em vez de um ponto de código de compatibilidade.
Dois casos precisam de mais do que /ToUnicode: latim com espaçamento entre letras e árabe moldado da direita para a esquerda. O motor os desenha como glifos espaçados ou reordenados; por isso, só a ordem dos glifos não basta para recuperar a string lógica. Ele envolve cada uma dessas sequências em conteúdo marcado /Span que carrega /ActualText, uma substituição exata para o conteúdo contido (ISO 32000-2 §14.9). Os extratores que honram /ActualText retornam a string lógica.
Superfície da API
Seção intitulada “Superfície da API”| Símbolo | Localização | Função |
|---|---|---|
FontRegistry::register(string $fontFile, string $alias = ''): FontInfo | NextPDF\Typography\FontRegistry | Registra as faces CJK e árabe. |
DocumentFactory::create(): Document | NextPDF\Core\DocumentFactory | Constrói um documento que usa o seu registro. |
Document::writeHtml(string $html): static | NextPDF\Core\Concerns\HasTextOutput | Renderiza conteúdo multilíngue. |
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\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\FontRegistry;
$fonts = new FontRegistry();$fonts->register(__DIR__ . '/NotoSansCJK-Regular.ttf', alias: 'CJK');$fonts->register(__DIR__ . '/NotoNaskhArabic-Regular.ttf', alias: 'Arabic');
$doc = (new DocumentFactory($fonts, new ImageRegistry(maxCacheBytes: 0)))->create();$doc->addPage();$doc->writeHtml( '<p style="font-family: \'CJK\';">PDF 2.0 引擎 — 量子</p>' . '<p style="direction: rtl; font-family: \'Arabic\';">فاتورة</p>');$doc->save(__DIR__ . '/multilingual.pdf');pdftotext multilingual.pdf - | head# Extracts the logical text: "PDF 2.0 引擎 — 量子" and the logical Arabic "فاتورة",# not compatibility code points or reversed presentation forms.Exemplo de código — Produção
Seção intitulada “Exemplo de código — Produção”Este exemplo autossuficiente marca o documento, adiciona um título com espaçamento entre letras e grava no caminho usado pelo harness.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;use NextPDF\Graphics\ImageRegistry;use NextPDF\Typography\FontRegistry;
$fonts = new FontRegistry();$fonts->register(__DIR__ . '/NotoSansCJK-Regular.ttf', alias: 'CJK');$fonts->register(__DIR__ . '/NotoNaskhArabic-Regular.ttf', alias: 'Arabic');
$doc = (new DocumentFactory($fonts, new ImageRegistry(maxCacheBytes: 0)))->create();$doc->setTitle('Multilingual extraction');$doc->enableTaggedPdf('en');$doc->addPage();
$html = <<<'HTML'<h1 style="font-family: 'CJK'; letter-spacing: 3px;">CODE WORD</h1><p style="font-family: 'CJK';">中文 · 日本語 · 한국어 · 量子 (compatibility ideograph)</p><p style="direction: rtl; font-family: 'Arabic';">المبلغ الإجمالي 380.00</p>HTML;
$doc->writeHtml($html);
$out = getenv('NEXTPDF_OUT');$doc->save($out !== false ? $out : __DIR__ . '/multilingual-copy-paste-extraction.pdf');
echo "Wrote the multilingual PDF\n";Execute pdftotext sobre a saída. O título com espaçamento entre letras é extraído como CODE WORD sem espaços inseridos, a linha CJK é extraída como os respectivos caracteres base e a linha árabe é extraída como a string lógica.
Casos extremos e pegadinhas
Seção intitulada “Casos extremos e pegadinhas”- Verifique a extração com pdftotext, não com PyMuPDF. O modo de texto bruto do PyMuPDF ignora o
/ActualTextinline e retorna os glifos visuais, então ele pode indicar uma falha onde não há uma. O Poppler (pdftotext) honra/ActualText; o veraPDF valida PDF/UA-2, não a extração. - A extração precisa de
/ToUnicode. Registre e incorpore as fontes para que o escritor emita o CMap/ToUnicode. Uma fonte não incorporada e não padrão não consegue garantir um mapeamento Unicode. /ActualTextcobre sequências moldadas e com espaçamento entre letras. Para texto simples, não moldado e sem espaçamento,/ToUnicodesozinho é extraído corretamente; o invólucro/Spanpreserva as sequências espaçadas ou reordenadas.- As tabelas HTML marcadas passam nas verificações de PDF/UA-2. A extração é correta, e uma
<table>HTML marcada agora passa noveraPDF --flavour ua2com zero falhas — consulte Acessibilidade.
Desempenho
Seção intitulada “Desempenho”Construir o CMap /ToUnicode e os invólucros /Span escala linearmente com a contagem de glifos. Esta receita tem orçamento de wall_ms: 1500, peak_mb: 96.
Notas de segurança
Seção intitulada “Notas de segurança”Valide o comprimento das strings multilíngues fornecidas pelo usuário para que o tamanho da saída permaneça limitado. O construtor de /ToUnicode rejeita metades de substitutos (surrogate halves) e códigos fora do espaço de código; assim, um mapa malformado não consegue criar um recurso de extração corrompido. O motor não executa scripts e não busca recursos remotos para fontes locais.
Conformidade
Seção intitulada “Conformidade”| Declaração | Especificação | Cláusula |
|---|---|---|
Um CMap /ToUnicode mapeia códigos de caractere para Unicode na extração. | ISO 32000-2 | §9.10 |
/ActualText é uma substituição exata para o conteúdo contido. | ISO 32000-2 | §14.9 |
| NFKC é a decomposição de compatibilidade seguida da composição canônica. | Unicode UAX #15 | §1.2 |
Contexto comercial
Seção intitulada “Contexto comercial”Não aplicável.
Veja também
Seção intitulada “Veja também”- Extrair conteúdo textual — a base da extração de conteúdo marcado.
- Renderizar HTML árabe da direita para a esquerda — moldagem do árabe e texto da direita para a esquerda.
- Tipografia —
/ToUnicodee canonicalização NFKC. - Acessibilidade —
/ActualTexte o suporte a tabelas marcadas.