Перейти к содержимому
getnextpdf.com

Создание PDF-файлов с CJK и арабским текстом для корректного копирования и вставки

Создайте файл Portable Document Format (PDF) с китайским, японским, корейским (CJK) и арабским текстом, который при копировании и вставке сохраняет исходные логические символы. Движок сопоставляет каждый глиф с Unicode через CMap /ToUnicode, канонизирует результат в форме нормализации совместимости с композицией (NFKC) и оборачивает сформированные прогоны или прогоны с разрядкой в /Span, который несёт /ActualText. Зарегистрируйте шрифты и выведите содержимое — извлечение останется корректным. Проверяйте извлечение через pdftotext/Poppler, а не PyMuPDF; veraPDF валидирует соответствие PDF/Universal Accessibility 2 (PDF/UA-2), а не извлечение.

Окно терминала
composer require nextpdf/core

Зарегистрируйте CJK-шрифт, например Noto Sans CJK, и шрифт с поддержкой арабского, например Noto Naskh Arabic, с таблицей символов, покрывающей блок Arabic Presentation Forms-B. Встраивайте только шрифты, на встраивание которых у вас есть права.

Коды глифов в потоке содержимого — это не Unicode. CMap /ToUnicode сопоставляет каждый код обратно с Unicode, чтобы средство чтения могло извлечь текст (ISO 32000-2 §9.10). Движок канонизирует эти значения в форме нормализации совместимости с композицией (NFKC), как определено в Unicode UAX #15. Идеографы совместимости CJK и арабские формы представления сопоставляются со своими базовыми символами, поэтому поиск и копирование возвращают канонический текст вместо символов совместимости или форм представления.

В двух случаях одного /ToUnicode недостаточно: для латиницы с разрядкой и сформированного арабского с направлением справа налево. Движок отрисовывает их как разнесённые или переупорядоченные глифы, поэтому по одному порядку глифов нельзя восстановить логическую строку. Движок оборачивает каждый прогон в последовательность размеченного содержимого /Span с /ActualText — точной заменой вложенного содержимого (ISO 32000-2 §14.9). Средства извлечения, которые учитывают /ActualText, возвращают логическую строку.

СимволРасположениеРоль
FontRegistry::register(string $fontFile, string $alias = ''): FontInfoNextPDF\Typography\FontRegistryРегистрирует CJK- и арабские начертания.
DocumentFactory::create(): DocumentNextPDF\Core\DocumentFactoryСоздаёт документ с вашим реестром.
Document::writeHtml(string $html): staticNextPDF\Core\Concerns\HasTextOutputОтрисовывает многоязычное содержимое.
<?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.

Этот самодостаточный пример тегирует документ, добавляет заголовок с разрядкой и записывает файл по пути, заданному тестовой средой.

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

Запустите pdftotext на полученном файле. Заголовок с разрядкой извлекается как CODE WORD без вставленных пробелов, строка CJK — как её базовые символы, а арабская строка — как логическая строка.

  • Проверяйте извлечение через pdftotext, а не PyMuPDF. Режим необработанного текста в PyMuPDF игнорирует встроенный /ActualText и возвращает визуальные глифы, поэтому может показывать результат хуже фактического. Poppler (pdftotext) учитывает /ActualText; veraPDF валидирует PDF/UA-2, а не извлечение.
  • Для извлечения нужен /ToUnicode. Регистрируйте и встраивайте шрифты, чтобы компонент записи выводил CMap /ToUnicode. Для невстроенного нестандартного шрифта нельзя гарантировать сопоставление с Unicode.
  • /ActualText охватывает сформированные прогоны и прогоны с разрядкой. Для обычного несформированного текста без разрядки одного /ToUnicode достаточно для корректного извлечения; обёртка /Span сохраняет прогоны с разрядкой или переупорядочением.
  • Тегированные HTML-таблицы проходят проверки PDF/UA-2. Извлечение корректно, и тегированная HTML-<table> теперь проходит veraPDF --flavour ua2 без единой ошибки — см. Доступность.

Построение CMap /ToUnicode и обёрток /Span линейно зависит от числа глифов. Бюджет этого рецепта — wall_ms: 1500, peak_mb: 96.

Проверяйте длину многоязычных строк от пользователя, чтобы ограничивать размер вывода. Построитель /ToUnicode отклоняет половины суррогатных пар и коды вне кодового пространства, поэтому некорректное сопоставление не может создать повреждённый ресурс извлечения. Движок не выполняет сценарии и не загружает удалённые ресурсы при работе с локальными шрифтами.

УтверждениеСпецификацияПункт
CMap /ToUnicode сопоставляет коды символов с Unicode для извлечения.ISO 32000-2§9.10
/ActualText — точная замена вложенного содержимого.ISO 32000-2§14.9
NFKC — это декомпозиция совместимости с последующей канонической композицией.Unicode UAX #15§1.2

Не применимо.