Hacer que los PDF con CJK y árabe se copien y peguen correctamente
De un vistazo
Sección titulada «De un vistazo»Crear un archivo en formato de documento portátil (PDF) con texto en chino, japonés, coreano (CJK) y árabe que se copie y pegue como los caracteres lógicos originales. El motor asigna cada glifo a Unicode mediante un CMap /ToUnicode, canonicaliza el resultado con la forma de normalización de composición de compatibilidad (NFKC) y envuelve las secuencias modeladas o con espaciado entre letras en un /Span que lleva /ActualText. Al registrar las fuentes y escribir el contenido, la extracción se mantiene correcta. Verificar la extracción con pdftotext/Poppler, no con PyMuPDF; veraPDF valida la accesibilidad universal de PDF 2 (PDF/UA-2), no la extracción.
Instalación
Sección titulada «Instalación»composer require nextpdf/coreRegistrar una fuente CJK, como Noto Sans CJK, y una fuente compatible con el árabe cuyo mapa de caracteres cubra el bloque Arabic Presentation Forms-B, como Noto Naskh Arabic. Incrustar únicamente fuentes con licencia para ello.
Descripción conceptual
Sección titulada «Descripción conceptual»Los códigos de glifo de un flujo de contenido no son Unicode. Un CMap /ToUnicode vuelve a asignar cada código a Unicode para que un lector pueda extraer el texto (ISO 32000-2 §9.10). El motor canonicaliza esos valores con la forma de normalización de composición de compatibilidad (NFKC), tal como la define Unicode UAX #15. Un ideograma de compatibilidad CJK y una forma de presentación árabe se asignan a su carácter base, de modo que la búsqueda y la copia devuelven texto canónico en lugar de un punto de código de compatibilidad.
Dos casos necesitan algo más que /ToUnicode: el latín con espaciado entre letras y el árabe modelado de derecha a izquierda. El motor los dibuja como glifos espaciados o reordenados, por lo que el orden de los glifos por sí solo no puede recuperar la cadena lógica. Envuelve cada secuencia en contenido marcado /Span con /ActualText, un reemplazo exacto del contenido que encierra (ISO 32000-2 §14.9). Los extractores que respetan /ActualText devuelven la cadena lógica.
Superficie de la API
Sección titulada «Superficie de la API»| Símbolo | Ubicación | Función |
|---|---|---|
FontRegistry::register(string $fontFile, string $alias = ''): FontInfo | NextPDF\Typography\FontRegistry | Registra los tipos de letra CJK y árabe. |
DocumentFactory::create(): Document | NextPDF\Core\DocumentFactory | Construye un documento que usa tu registro. |
Document::writeHtml(string $html): static | NextPDF\Core\Concerns\HasTextOutput | Representa el contenido multilingüe. |
Muestra de código — Inicio rápido
Sección titulada «Muestra de código — Inicio 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.Muestra de código — Producción
Sección titulada «Muestra de código — Producción»Este ejemplo independiente etiqueta el documento, añade un encabezado con espaciado entre letras y escribe en la ruta indicada por el arnés.
<?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";Ejecutar pdftotext sobre la salida. El encabezado con espaciado entre letras se extrae como CODE WORD sin espacios insertados; la línea CJK se extrae como sus caracteres base y la línea árabe se extrae como la cadena lógica.
Casos límite y trampas
Sección titulada «Casos límite y trampas»- Verificar la extracción con pdftotext, no con PyMuPDF. El modo de texto sin procesar de PyMuPDF ignora el
/ActualTexten línea y devuelve glifos visuales, por lo que puede infravalorar la corrección. Poppler (pdftotext) respeta/ActualText; veraPDF valida PDF/UA-2, no la extracción. - La extracción necesita
/ToUnicode. Registrar e incrustar las fuentes para que el escritor emita el CMap/ToUnicode. Una fuente no estándar que no se incrusta no puede garantizar una asignación a Unicode. /ActualTextcubre las secuencias modeladas y con espaciado entre letras. El texto simple, sin modelar y sin espaciado, se extrae correctamente solo con/ToUnicode; el envoltorio/Spanpreserva las secuencias espaciadas o reordenadas.- Las tablas HTML etiquetadas pasan las comprobaciones de PDF/UA-2. La extracción es correcta, y una
<table>HTML etiquetada ahora pasaveraPDF --flavour ua2sin ningún fallo — véase Accesibilidad.
Rendimiento
Sección titulada «Rendimiento»La construcción del CMap /ToUnicode y de los envoltorios /Span escala linealmente con el número de glifos. Esta receta usa como presupuesto wall_ms: 1500, peak_mb: 96.
Notas de seguridad
Sección titulada «Notas de seguridad»Validar la longitud de las cadenas multilingües proporcionadas por el usuario para que el tamaño de la salida se mantenga acotado. El constructor de /ToUnicode rechaza las mitades de pares suplentes y los códigos fuera del espacio de códigos, de modo que un mapa malformado no pueda crear un recurso de extracción corrupto. El motor no ejecuta scripts ni obtiene recursos remotos para las fuentes locales.
Conformidad
Sección titulada «Conformidad»| Afirmación | Especificación | Cláusula |
|---|---|---|
Un CMap /ToUnicode asigna códigos de carácter a Unicode para la extracción. | ISO 32000-2 | §9.10 |
/ActualText es un reemplazo exacto del contenido que encierra. | ISO 32000-2 | §14.9 |
| NFKC es la descomposición de compatibilidad seguida de la composición canónica. | Unicode UAX #15 | §1.2 |
Contexto comercial
Sección titulada «Contexto comercial»No aplicable.
Véase también
Sección titulada «Véase también»- Extraer contenido de texto — la base de la extracción etiquetada.
- Representar HTML en árabe de derecha a izquierda — el modelado del árabe y el texto de derecha a izquierda.
- Tipografía —
/ToUnicodey la canonicalización NFKC. - Accesibilidad —
/ActualTexty el soporte de las tablas etiquetadas.