تخطَّ إلى المحتوى
getnextpdf.com

اجعل ملفات PDF التي تحتوي على CJK والعربية تُنسخ وتُلصق بصورة صحيحة

أنشئ ملف ⁨PDF⁩ يتضمن نصًا صينيًا ويابانيًا وكوريًا (CJK) وعربيًا يُنسخ ويُلصق بوصفه المحارف المنطقية الأصلية. يربط المحرك كل محرف رسومي بـ Unicode عبر خريطة /ToUnicode CMap، ويُقنِّن النتيجة بصيغة التركيب التوافقي للتطبيع (NFKC)، ويغلّف المقاطع المُشكَّلة أو ذات التباعد الحرفي بعنصر /Span يحمل /ActualText. سجّل الخطوط واكتب المحتوى؛ سيظل الاستخراج صحيحًا. تحقّق من الاستخراج باستخدام pdftotext/Poppler، لا PyMu⁨PDF⁩؛ يتحقق vera⁨PDF⁩ من صحة توافق ⁨PDF/UA-2⁩ لإمكانية الوصول، لا من الاستخراج.

Terminal window
composer require nextpdf/core

سجّل خط CJK، مثل Noto Sans CJK، وخطًا يدعم العربية ويغطي نطاق Arabic Presentation Forms-B في جدول رموزه، مثل Noto Naskh Arabic. لا تضمّن إلا الخطوط التي يحق لك تضمينها.

رموز المحارف الرسومية في دفق المحتوى ليست Unicode. تربط خريطة /ToUnicode CMap كل رمز بما يقابله في 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');
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.

يوسم هذا المثال القائم بذاته المستند، ويضيف عنوانًا ذا تباعد حرفي، ويكتب إلى مسار منظومة الاختبار.

<?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، لا PyMu⁨PDF⁩. يتجاهل وضع النص الخام في PyMu⁨PDF⁩ قيمة /ActualText السطرية، ويُعيد المحارف الرسومية البصرية، لذلك قد يعطي انطباعًا خاطئًا بعدم صحة الاستخراج. أما Poppler (pdftotext) فيحترم /ActualText؛ ويتحقق vera⁨PDF⁩ من صحة ⁨PDF/UA-2⁩، لا من الاستخراج.
  • يحتاج الاستخراج إلى /ToUnicode. سجّل الخطوط وضمّنها حتى يُصدر الكاتب خريطة /ToUnicode CMap. فالخط غير المُضمَّن وغير القياسي لا يمكنه ضمان ربط بـ Unicode.
  • يغطي /ActualText المقاطع المُشكَّلة وذات التباعد الحرفي. بالنسبة إلى النص العادي غير المُشكَّل وغير المُتباعد، يستخرج /ToUnicode وحده بصورة صحيحة؛ ويحافظ غلاف /Span على المقاطع المتباعدة أو المُعاد ترتيبها.
  • جداول ⁨HTML⁩ الموسومة تجتاز فحوص ⁨PDF/UA-2⁩. الاستخراج صحيح، وأصبح جدول ⁨HTML⁩ الموسوم <table> الآن يجتاز veraPDF --flavour ua2 بصفر إخفاقات — راجع إمكانية الوصول.

يتناسب بناء خريطة /ToUnicode CMap وأغلفة /Span تناسبًا خطيًا مع عدد المحارف الرسومية. تحدّد هذه الوصفة ميزانية wall_ms: 1500, peak_mb: 96.

تحقّق من طول السلاسل النصية متعددة اللغات المُقدَّمة من المستخدم لإبقاء حجم ملف الإخراج محدودًا. يرفض باني /ToUnicode أنصاف البدائل والرموز الخارجة عن فضاء الترميز، وبذلك لا تستطيع خريطة مشوَّهة إنشاء مورد استخراج معطوب. لا يشغّل المحرك أي سكربت، ولا يجلب أي موارد بعيدة للخطوط المحلية.

العبارةالمواصفةالبند
تربط خريطة /ToUnicode CMap رموز المحارف بـ Unicode من أجل الاستخراج.ISO 32000-2§9.10
/ActualText بديل دقيق للمحتوى المُحاط.ISO 32000-2§14.9
NFKC هو التفكيك التوافقي الذي يتبعه التركيب القانوني.Unicode UAX #15§1.2

غير منطبق.