Salta ai contenuti
getnextpdf.com

Creare PDF con CJK e arabo corretti per copia-incolla

Creare un file Portable Document Format (PDF) con testo cinese, giapponese, coreano (CJK) e arabo che venga copiato e incollato come i caratteri logici originali. Il motore mappa ogni glifo su Unicode tramite una CMap /ToUnicode, canonicalizza il risultato con Normalization Form Compatibility Composition (NFKC) e racchiude i run sagomati o con spaziatura tra le lettere in uno /Span dotato di /ActualText. Registrare i font e scrivere il contenuto: l’estrazione resta corretta. Verificare l’estrazione con pdftotext/Poppler, non con PyMuPDF; veraPDF convalida PDF/Universal Accessibility 2 (PDF/UA-2), non l’estrazione.

Terminal window
composer require nextpdf/core

Registrare un font CJK, per esempio Noto Sans CJK, e un font per l’arabo la cui mappa dei caratteri copra il blocco Arabic Presentation Forms-B, per esempio Noto Naskh Arabic. Incorporare solo i font che si è autorizzati a incorporare.

I codici dei glifi in un flusso di contenuto non sono Unicode. Una CMap /ToUnicode rimappa ciascun codice su Unicode, così che un lettore possa estrarre il testo (ISO 32000-2 §9.10). Il motore canonicalizza questi valori con Normalization Form Compatibility Composition (NFKC), come definito da Unicode UAX #15. Un ideogramma di compatibilità CJK e una forma di presentazione araba vengono mappati sul rispettivo carattere di base, così ricerca e copia restituiscono testo canonico anziché un punto di codice di compatibilità.

Due casi richiedono più di /ToUnicode: il latino con spaziatura tra le lettere e l’arabo sagomato da destra a sinistra. Il motore li disegna come glifi spaziati o riordinati, quindi il solo ordine dei glifi non consente di ricostruire la stringa logica. Racchiude ogni run in una sequenza di contenuto marcato /Span dotata di /ActualText, una sostituzione esatta del contenuto racchiuso (ISO 32000-2 §14.9). Gli estrattori che rispettano /ActualText restituiscono la stringa logica.

SimboloPosizioneRuolo
FontRegistry::register(string $fontFile, string $alias = ''): FontInfoNextPDF\Typography\FontRegistryRegistra i tipi di carattere CJK e arabo.
DocumentFactory::create(): DocumentNextPDF\Core\DocumentFactoryCrea un documento che usa il registro.
Document::writeHtml(string $html): staticNextPDF\Core\Concerns\HasTextOutputEsegue il rendering del contenuto multilingue.
<?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.

Questo esempio autonomo tagga il documento, aggiunge un’intestazione con spaziatura tra le lettere e scrive nel percorso dell’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";

Eseguire pdftotext sull’output. L’intestazione con spaziatura tra le lettere si estrae come CODE WORD senza spazi inseriti, la riga CJK si estrae nei suoi caratteri di base e la riga araba si estrae come stringa logica.

  • Verificare l’estrazione con pdftotext, non con PyMuPDF. La modalità di testo grezzo di PyMuPDF ignora /ActualText inline e restituisce i glifi visivi, quindi può far apparire meno corretta l’estrazione. Poppler (pdftotext) rispetta /ActualText; veraPDF convalida PDF/UA-2, non l’estrazione.
  • L’estrazione richiede /ToUnicode. Registrare e incorporare i font in modo che il writer emetta la CMap /ToUnicode. Un font non incorporato e non standard non può garantire una mappatura Unicode.
  • /ActualText copre i run sagomati e con spaziatura tra le lettere. Per il testo semplice, non sagomato e senza spaziatura, il solo /ToUnicode consente un’estrazione corretta; il wrapper /Span preserva i run spaziati o riordinati.
  • Le tabelle HTML taggate superano i controlli PDF/UA-2. L’estrazione è corretta e una <table> HTML taggata ora supera veraPDF --flavour ua2 senza alcun fallimento — vedere Accessibilità.

La costruzione della CMap /ToUnicode e dei wrapper /Span cresce linearmente con il numero di glifi. Questa ricetta prevede un budget di wall_ms: 1500, peak_mb: 96.

Convalidare la lunghezza delle stringhe multilingue fornite dall’utente affinché la dimensione dell’output resti limitata. Il costruttore di /ToUnicode rifiuta le metà surrogate e i codici fuori dallo spazio dei codici, quindi una mappa malformata non può creare una risorsa di estrazione corrotta. Il motore non esegue alcuno script e non recupera risorse remote per i font locali.

DichiarazioneSpecificaClausola
Una CMap /ToUnicode mappa i codici dei caratteri su Unicode per l’estrazione.ISO 32000-2§9.10
/ActualText è una sostituzione esatta del contenuto racchiuso.ISO 32000-2§14.9
NFKC è la decomposizione di compatibilità seguita dalla composizione canonica.Unicode UAX #15§1.2

Non applicabile.