Ga naar inhoud
getnextpdf.com

CJK- en Arabische PDF's correct kopieer- en plakbaar maken

Maak een Portable Document Format (PDF)-bestand met Chinese, Japanse, Koreaanse (CJK) en Arabische tekst die bij kopiëren en plakken als de oorspronkelijke logische tekens meekomt. De engine mapt elke glyph naar Unicode via een /ToUnicode-CMap, canonicaliseert het resultaat met Normalization Form Compatibility Composition (NFKC) en wikkelt gevormde runs of runs met letter-spacing in een /Span met /ActualText. Registreer de lettertypen en schrijf de inhoud weg; de extractie blijft correct. Controleer de extractie met pdftotext/Poppler, niet PyMuPDF; veraPDF valideert PDF/Universal Accessibility 2 (PDF/UA-2), niet de extractie.

Terminal window
composer require nextpdf/core

Registreer een CJK-lettertype, zoals Noto Sans CJK, en een lettertype met Arabische ondersteuning waarvan de tekentabel het blok Arabic Presentation Forms-B bevat, zoals Noto Naskh Arabic. Sluit alleen lettertypen in waarvoor je een embedding-licentie hebt.

Glyphcodes in een content stream zijn geen Unicode. Een /ToUnicode-CMap mapt elke code terug naar Unicode, zodat een reader tekst kan extraheren (ISO 32000-2 §9.10). De engine canonicaliseert die waarden met Normalization Form Compatibility Composition (NFKC), zoals gedefinieerd door Unicode UAX #15. Een CJK Compatibility Ideograph en een Arabische presentatievorm worden naar hun basisteken gemapt, zodat zoeken en kopiëren canonieke tekst opleveren in plaats van een compatibiliteitscodepunt.

In twee gevallen is /ToUnicode alleen niet genoeg: Latijnse tekst met letter-spacing en gevormd rechts-naar-links-Arabisch. De engine rendert ze als gespatieerde of herordende glyphs, waardoor de glyphvolgorde op zichzelf de logische string niet kan herstellen. De engine wikkelt elke run in een /Span-marked-content-sequentie met /ActualText, een exacte vervanging voor de omsloten inhoud (ISO 32000-2 §14.9). Extractietools die /ActualText respecteren, leveren de logische string op.

SymboolLocatieRol
FontRegistry::register(string $fontFile, string $alias = ''): FontInfoNextPDF\Typography\FontRegistryRegistreer de CJK- en Arabische lettertypen.
DocumentFactory::create(): DocumentNextPDF\Core\DocumentFactoryBouw een document dat je register gebruikt.
Document::writeHtml(string $html): staticNextPDF\Core\Concerns\HasTextOutputRender meertalige inhoud.
<?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');
Terminal window
pdftotext multilingual.pdf - | head
# Extracts the logical text: "PDF 2.0 引擎 — 量子" and the logical Arabic "فاتورة",
# not compatibility code points or reversed presentation forms.

Dit zelfstandige voorbeeld tagt het document, voegt een kop met letter-spacing toe en schrijft naar het harness-pad.

<?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";

Voer pdftotext uit op de uitvoer. De kop met letter-spacing wordt geëxtraheerd als CODE WORD zonder ingevoegde spaties, de CJK-regel wordt geëxtraheerd als basistekens, en de Arabische regel wordt geëxtraheerd als de logische string.

  • Verifieer de extractie met pdftotext, niet PyMuPDF. De ruwe-tekstmodus van PyMuPDF negeert inline /ActualText en levert visuele glyphs op, waardoor deze de correctheid kan onderrapporteren. Poppler (pdftotext) respecteert /ActualText; veraPDF valideert PDF/UA-2, niet de extractie.
  • Extractie heeft /ToUnicode nodig. Registreer en sluit lettertypen in zodat de writer de /ToUnicode-CMap wegschrijft. Een niet-ingesloten, niet-standaard lettertype kan geen Unicode-mapping garanderen.
  • /ActualText dekt gevormde runs en runs met letter-spacing. Voor gewone, niet-gevormde, niet-gespatieerde tekst extraheert /ToUnicode op zichzelf al correct; de /Span-wrapper behoudt gespatieerde of herordende runs.
  • Getagde HTML-tabellen slagen voor PDF/UA-2-controles. De extractie is correct, en een getagde HTML-<table> slaagt nu voor veraPDF --flavour ua2 met nul fouten — zie Toegankelijkheid.

Het bouwen van de /ToUnicode-CMap en de /Span-wrappers schaalt lineair met het aantal glyphs. Dit recipe reserveert wall_ms: 1500, peak_mb: 96.

Valideer de lengte van meertalige strings die gebruikers aanleveren, zodat de uitvoergrootte begrensd blijft. De /ToUnicode-builder weigert surrogate-helften en codes buiten de codespace, zodat een misvormde map geen corrupte extractieresource kan aanmaken. De engine voert geen scripts uit en haalt geen externe resources op voor lokale lettertypen.

VerklaringSpecClause
Een /ToUnicode-CMap mapt tekencodes naar Unicode voor extractie.ISO 32000-2§9.10
/ActualText is een exacte vervanging voor de omsloten inhoud.ISO 32000-2§14.9
NFKC is compatibiliteitsdecompositie gevolgd door canonieke compositie.Unicode UAX #15§1.2

Niet van toepassing.