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

ترميز نص CJK مع مراعاة cmap

تسجّل هذه الوصفة وجه خط ⁨TrueType⁩ صيني وياباني وكوري (⁨CJK⁩)، ثم ترمّز النص الصيني التقليدي عبر الواجهة التي تراعي ⁨cmap⁩ FontInfo::encodeText(). تُعيد الواجهة دفق بايتات ⁨CID⁩ ثنائية البايت بترميز ⁨Identity-H.⁩ تتبع الوصفة examples/35-cjk-cmap-demo.php. اقرأ ملاحظة النطاق قبل الاعتماد على هذا المسار.

النطاق والحالة (اقرأ أولًا)

قسم بعنوان «النطاق والحالة (اقرأ أولًا)»

تُطرح بنية ترميز النص التي تراعي ⁨cmap⁩ على مراحل (⁨ADR-013⁩). اكتملت المرحلة 1: وُصلت الواجهة FontInfo::encodeText() واستراتيجية الترميز التي تراعي ⁨cmap⁩، وأصبحتا متاحتين من مساحة المستخدم. المرحلة 2 قيد التنفيذ: تمرير أداة العرض والكاتب عبر الواجهة. المرحلتان 3 و4 معلّقتان: إصدار /ToUnicode و/CIDSystemInfo و/Encoding و/CIDToGIDMap لكل خط، ومُحلِّل الخط البديل، لم يُوصَّلا بعد إلى الكاتب.

خطّط مع مراعاة هذه التبعات:

  • توضّح هذه الوصفة واجهة الترميز، وليست وضع كتابة عمودية مكتملًا. لا تملك واجهة المستند اليوم أي واجهة برمجة عامة لوضع الكتابة؛ فلا يوجد استدعاء setWritingMode ولا أداة ضبط vertical-rl.
  • المثال الداعم، وفق ترويسته نفسها، هو اختبار دخان تكاملي، وليس تجهيزة مطابقة. سيفشل التحقق من ⁨PDF/UA-2⁩ و⁨PDF/A-4⁩ فشلًا سلبيًا للمخرجات المنتَجة بهذه الطريقة حتى تكتمل المرحلتان 3 و4. لا تذكر أن المخرجات من هذا المسار مطابقة. المدقّق هو من يقرّر المطابقة، وهذه المخرجات لن تجتازه بعد.
  • توجد بنية مقاييس الكتابة العمودية لكنها داخلية. تشمل كائن القيمة CjkVerticalMetrics ومُصدِرَي /W2 و/DW2. لا يكشف ⁨NextPDF⁩ عنها كاستدعاء “اكتب عموديًا” في مساحة المستخدم، ولا يُصدِر الكاتب قواميسها بعد.
Terminal window
composer require nextpdf/core:^3

يطابق القيد حزمة nextpdf/core. يعمل المثال على ⁨PHP 8.4.⁩ تجعل تجهيزة اختبار ⁨Noto Sans TC⁩ المرفقة هذه الوصفة مكتفية بذاتها.

يصوغ ⁨ISO 32000-2⁩ إخراج النص في ثلاث طبقات: نقطة ترميز ⁨Unicode⁩، ورمز الحرف، ومعرّف المحرف الرسومي. بالنسبة إلى وجه خط ⁨TrueType⁩ من نوع ⁨CJK⁩، يستخدم المحرّك خطًا مركّبًا من نوع ⁨Type 0⁩ بترميز Identity-H. مع هذا الترميز، تستخدم السلسلة المعروضة أزواج بايتات تُفهرس ⁨CIDFont⁩ ‏(⁨ISO 32000-2⁩).

يحلّل FontRegistry::register() وجه الخط. ثم يحدّد FontInfo::encodeText($unicodeText) استراتيجية الترميز عبر FontEncodingStrategyResolver. بالنسبة إلى وجه خط ⁨TrueType⁩ مسجّل من نوع ⁨CJK⁩، يوجّه الطلب إلى TrueTypeCmapStrategy. يحمل EncodedGlyphRun المُعاد دفق بايتات ⁨Identity-H⁩، ومُعامل سلسلة ⁨PDF⁩، وعروض التقدّم لكل محرف رسومي، ونقاط الترميز المستخدَمة، وخريطة ⁨GID⁩←⁨Unicode.⁩ يستخدم تجزيء خطوط ⁨CJK⁩ نقاط الترميز وفق ⁨ADR-008.⁩ سيستخدم دفق /ToUnicode مستقبلًا خريطة ⁨GID⁩←⁨Unicode.⁩ الوضع المحدَّد هو EncodingMode::TwoByteCid.

يحدّد بناءان من بنى ⁨CIDFont⁩ الكتابة العمودية في ⁨PDF.⁩ الأول هو مصفوفة المقاييس العمودية لكل محرف رسومي /W2 ‏(⁨ISO 32000-2⁩). والثاني هو المقاييس العمودية الافتراضية /DW2 ‏(⁨ISO 32000-2⁩). يوفّر ⁨NextPDF⁩ كائن القيمة والمُصدِرات لكليهما عبر CjkVerticalMetrics::toW2Array() وtoW2RangeArray() وtoDw2Array(). هذه البنية داخلية، ولا يُصدِرها الكاتب بعد. راجع ملاحظة النطاق.

  • FontRegistry::register(string $fontFile, string $alias = '', int $fontIndex = 0): FontInfoNextPDF\Typography\FontRegistry.
  • FontInfo::encodeText(string $unicodeText): EncodedGlyphRunNextPDF\Typography\FontInfo. واجهة المرحلة 1.
  • EncodedGlyphRunNextPDF\Typography\Encoding\EncodedGlyphRun (byteStream, pdfStringOperand, mode, advanceWidths, toUnicodeMap, usedCodepoints, glyphCount()).
  • EncodingModeNextPDF\Typography\Encoding\EncodingMode (SingleByte, TwoByteCid).
  • CjkVerticalMetricsNextPDF\Typography\CjkVerticalMetrics. كائن قيمة داخلي للمقاييس العمودية. يُوثَّق للشفافية، وليس كمسار كتابة في مساحة المستخدم.

يُولَّد جدول ⁨PHPDoc⁩ الكامل من المصدر.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Typography\Encoding\EncodingMode;
use NextPDF\Typography\FontRegistry;
$registry = new FontRegistry();
$font = $registry->register('/path/to/NotoSansTC-Regular.ttf', alias: 'NotoSansTC');
$encoded = $font->encodeText('PDF 2.0 引擎');
assert($encoded->mode === EncodingMode::TwoByteCid); // cmap-aware branch fired
echo $encoded->glyphCount() . " glyph run entries\n";

هذه العيّنة قائمة بذاتها وقابلة للتشغيل ضمن إطار الاختبار. وهي تعكس examples/35-cjk-cmap-demo.php. أولًا، سجّل تجهيزة ⁨Noto Sans TC⁩ المرفقة. بعد ذلك، تأكّد من أن الواجهة التي تراعي ⁨cmap⁩ قابلة للوصول. ثم اعرض عبر DocumentFactory حتى يستخدم المستند السجل الذي ملأته.

<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\DocumentFactory;
use NextPDF\Graphics\ImageRegistry;
use NextPDF\Typography\Encoding\EncodingMode;
use NextPDF\Typography\FontRegistry;
$cjkFontPath = dirname(__DIR__, 2)
. '/fonts/test-fixtures/Noto Sans TC/NotoSansTC-Regular.ttf';
if (!is_file($cjkFontPath)) {
fwrite(STDERR, "Missing CJK font fixture: {$cjkFontPath}\n");
exit(1);
}
$fontRegistry = new FontRegistry();
$cjkFont = $fontRegistry->register($cjkFontPath, alias: 'NotoSansTC');
// Phase 1 facade: prove the cmap-aware path is reachable from userland.
$cjkSample = 'PDF 2.0 引擎 — 使用 CMap 編碼';
$encoded = $cjkFont->encodeText($cjkSample);
if ($encoded->mode !== EncodingMode::TwoByteCid) {
fwrite(STDERR, "Expected TwoByteCid (TrueTypeCmapStrategy branch)\n");
exit(2);
}
$imageRegistry = new ImageRegistry(maxCacheBytes: 0);
$documentFactory = new DocumentFactory($fontRegistry, $imageRegistry);
$doc = $documentFactory->create();
$doc->setTitle('NextPDF CJK CMap-Aware Encoding Demo');
$doc->setLanguage('zh-Hant');
$doc->addPage();
$doc->setFont('helvetica', 'B', 16);
$doc->cell(0, 12, 'CJK cmap-aware encoding (Phase 1 facade)', newLine: true);
$doc->setFont('helvetica', '', 10);
$doc->cell(0, 6, 'Mode: ' . $encoded->mode->name . ' (Identity-H, 2-byte CIDs)', newLine: true);
$doc->cell(0, 6, 'Glyphs: ' . $encoded->glyphCount() . ' run entries', newLine: true);
$doc->cell(0, 6, 'Bytes: ' . strlen($encoded->byteStream) . ' encoded bytes', newLine: true);
$doc->ln(4);
$doc->setFont('NotoSansTC', '', 18);
$doc->cell(0, 12, $cjkSample, newLine: true);
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT');
$doc->save($out !== false ? $out : __DIR__ . '/cjk-vertical-writing.pdf');
echo "Wrote cjk-vertical-writing.pdf (Phase 1+2 dry-run; not a conformance fixture)\n";

‏⁨STDOUT⁩ المتوقّع:

Wrote cjk-vertical-writing.pdf (Phase 1+2 dry-run; not a conformance fixture)
  • ليست تجهيزة مطابقة. وفق ترويسة المثال الداعم نفسها، هذه المخرجات اختبار دخان تكاملي. ستفشل فحوص ⁨PDF/UA-2⁩ و⁨PDF/A-4⁩ لها حتى تكتمل المرحلتان 3 و4. لا تسجّلها كمرجع مطابقة ذهبي.
  • لا توجد واجهة برمجة لوضع الكتابة. لا يوجد استدعاء عام يبدّل إلى الكتابة العمودية، بما يشمل vertical-rl وvertical-lr. يوجد مُصدِرا /W2 و/DW2 داخليًا. وهما غير مكشوفين ولم يُكتبا بعد في قاموس الخط.
  • ملكية السجل. يبني Document::createStandalone() سجله الخاص. استخدم DocumentFactory حتى يقرأ المستند السجل الذي ملأته بوجه خط ⁨CJK.⁩
  • مسار دفق البايتات النهائي. حتى تُغلق المرحلة 2، لا يزال دفق المحتوى المرئي يمرّ عبر مسار النص القديم. الجزء المثبَت والمتاح اليوم هو خطوة الترميز الأولية: البحث الأمامي في ⁨cmap⁩ إضافة إلى دفق بايتات ⁨Identity-H.⁩
  • تكلفة تجزيء خطوط ⁨CJK.⁩ تُجزَّأ أوجه خطوط ⁨CJK⁩ الكبيرة عبر عملية فرعية معزولة. لدى تلك العملية الفرعية بديل أصلي بلغة ⁨PHP⁩ ومهلة من ثانيتين (⁨ADR-008⁩).

يُجري encodeText() تمريرة بحث أمامي واحدة في ⁨cmap⁩ على المُدخل. وهو خطّي في عدد نقاط الترميز، ⁨O⁩(⁨n⁩). الميزانية هي wall_ms: 2000, peak_mb: 128. هذه الميزانية هي الأعلى في هذه المجموعة لأن أوجه خطوط ⁨CJK⁩ كبيرة، والتجزيء هو التكلفة الغالبة. يعزل ⁨ADR-008⁩ ذلك العمل بحيث لا يستطيع حجب المستدعي.

ملف خط ⁨CJK⁩ مُدخل ثنائي غير موثوق. يرفض المحلّل مسارات أغلفة الدفق وبايتات ⁨null.⁩ يعمل تجزيء خطوط ⁨CJK⁩ في عملية فرعية معزولة دون حالة موروثة (⁨ADR-008⁩). تحقّق من مصدر أوجه الخطوط التي يوفّرها المستخدم النهائي. يُعرَض محتوى نص ⁨CJK⁩ ولا يُفسَّر.

العبارةالمواصفةالبند⁨reference_id⁩
بالنسبة إلى خط ⁨Type 0⁩ بترميز ⁨Identity-H/Identity-V⁩، تكون السلسلة المعروضة أزواج بايتات تُفهرس ⁨CIDFont.⁩⁨ISO 32000-2⁩⁨iso32000_2_sec9⁩#⁨x1.x49.p90⁩
تعطي مصفوفة ⁨W2⁩ مقاييس الكتابة العمودية لكل محرف رسومي وتنطبق فقط على ⁨CIDFonts⁩ المستخدَمة للكتابة العمودية.⁨ISO 32000-2⁩⁨iso32000_2_sec9⁩#⁨x1.x44.p23⁩
تعطي مصفوفة ⁨DW2⁩ مقاييس الكتابة العمودية الافتراضية لـ ⁨CIDFont.⁩⁨ISO 32000-2⁩⁨iso32000_2_sec9⁩#⁨x1.x44.p22⁩

تُظهر هذه الوصفة أن واجهة ترميز ⁨CJK⁩ التي تراعي ⁨cmap⁩ قابلة للوصول من مساحة المستخدم (المرحلة 1). وهي لا تدّعي إخراج كتابة عمودية ولا مطابقة ⁨PDF/UA-2⁩ ‏/ ⁨PDF/A-4⁩ للملف المنتَج. إصدار /ToUnicode والمقاييس العمودية من جانب الكاتب (المرحلتان 3 و4) لا يزال معلّقًا، ولن تجتاز هذه المخرجات المدقّق اليوم.

لا ينطبق.