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

الخطوط: موضع الصعوبة

Evidence: Mixed evidence

الخطوط هي الموضع الذي قد يبدو فيه ملف ⁨PDF⁩ صحيحًا تمامًا، بينما يحمل خللًا صامتًا. قد تعرض الصفحة المحارف الصحيحة، ومع ذلك يتعذر البحث فيها أو نسخها كنص، أو تكون غير مطابقة لمواصفة أرشيفية. قد يحدث ذلك كله في وقت واحد، من دون أي علامة مرئية تنبهك. تتناول هذه الصفحة الأمور الثلاثة التي يجب أن تكون صحيحة — التضمين والتجزئة والترميز — وما يفعله ⁨NextPDF⁩ لكل منها.

“يبدو سليمًا” هي أخطر عبارة في العمل على ملفات ⁨PDF⁩، والخطوط هي الموضع الذي تُحدث فيه أكبر ضرر. يجب أن تتحقق ثلاثة أمور مستقلة:

  1. التضمين — يسافر برنامج الخط داخل الملف، فيُعرَض على نحو متطابق على جهاز لا يحتوي على الخط مثبَّتًا.
  2. التجزئة — لا تُحمَل سوى المحارف المستخدمة فعليًا، فلا يجعل خط ⁨CJK⁩ بحجم 20 ميغابايت كل مستند ضخمًا.
  3. الترميز — يوجد تخطيط صحيح من رموز المحارف على الصفحة عودةً إلى ⁨Unicode⁩، فيمكن البحث في النص ونسخه وفهرسته وقراءته بواسطة التقنيات المساعِدة.

العرض المرئي لا يثبت سوى الأمر الأول جزئيًا. قد يُظهر المستند محارف مثالية ومع ذلك يفشل في الأمر الثالث تمامًا — النص عندئذٍ صورة لكلمات، لا كلمات. وهذا هو الإخفاق الذي يجتاز كل مراجعة بمعيار “يبدو سليمًا” ثم يفشل في تدقيق امتثال أو طلب استدلال قانوني.

  • الخط في ملف ⁨PDF⁩ هو قاموس، ومعه عادةً دفق برنامج خط مضمَّن.
  • التجزئة تعيد كتابة ذلك البرنامج ليحتوي على المحارف المستخدمة فقط. يحصل اسم الخط المجزَّأ على وسم من ست أحرف لاتينية كبيرة و+ كي يعامله القارئون كخط متمايز.
  • الترميز هو المشكلة المنفصلة: تخطيط رموز المحارف إلى ⁨Unicode.⁩ خريطة ⁨CMap⁩ في /ToUnicode هي ما يجعل النص قابلًا للبحث والنسخ — وهي مستقلة عمّا إذا كانت المحارف تبدو صحيحة.
  • النص الذي يبدو صحيحًا دون /ToUnicode (أو مع خريطة خاطئة) هو الإخفاق الصامت المعتاد: مثالي على الشاشة، لكنه غير قابل للبحث عمليًا.
  • يجزّئ ⁨NextPDF⁩ خطوط ⁨TrueType⁩، ويحافظ على هوية المحرف من أجل عرض صحيح، ويُصدر خريطة ⁨CMap⁩ في /ToUnicode كي يعمل الاستخراج — ويمكنه فرض قاعدة التضمين في ⁨PDF 2.0⁩ بدلًا من التحذير فقط.

التجزئة. يحلّل FontSubsetter (src/Typography/FontSubsetter.php) دليل جداول ⁨TrueType⁩ الأصلي، ويقرأ cmap لتخطيط نقاط ترميز ⁨Unicode⁩ إلى معرّفات المحارف. وهو يتعامل مع كل من التنسيق 4 لـ ⁨BMP⁩ والتنسيق 12 لـ ⁨Unicode⁩ الكامل، وهو ما تحتاجه ⁨CJK.⁩ ثم ينفّذ الخطوة التي تغفلها أدوات التجزئة الساذجة: يحلّ تبعيات المحارف المركَّبة عبر الإغلاق المتعدّي. فالمحرف المُشَكَّل المبني من حرف أساسي مضافًا إليه علامة تشكيل يشير إلى محارف أخرى بوصفها مكوّنات. فإذا أُسقطت تلك المكوّنات، عُرِض المحرف على نحو خاطئ. تجوب أداة التجزئة ذلك المخطط حتى لا يظهر أي مكوّن جديد، مع حارس دورات يمنع خطًا مشوَّهًا من الدوران إلى الأبد.

هناك خياران هندسيان في ذلك الملف يستحقان التنبيه. أولًا، معرّفات المحارف محفوظة لا مُعاد تخطيطها — تُملأ الخانات غير المستخدمة بأصفار في glyf/loca كي تظل فهارس المحارف الأصلية في دفق المحتوى صالحة تحت CIDToGIDMap /Identity. ستكون إعادة التخطيط أصغر حجمًا، لكنها ستستلزم إعادة كتابة كل إشارة إلى محرف. الحفاظ على الهوية صحيح بحكم البناء. ثانيًا، الاجتياز مرتَّب (تصاعديًا بحسب ⁨gid⁩) كي يكون الجزء حتميًا على مستوى البايت — فالخط نفسه والمحارف المستخدمة نفسها ينتجان بايتات الجزء نفسها، وهو ما تتطلبه عمليات البناء القابلة لإعادة الإنتاج. فإذا كانت التجزئة ستوفّر أقل من 10% تقريبًا من حجم الملف، يُعاد الأصل دون تغيير. الكلفة الإضافية لا تستحق مكسبًا هامشيًا.

التضمين. توجد سياسة صريحة تقرّر ما إذا كان برنامج الخط سيُحمَل أصلًا — من دون تخمين إطلاقًا. لدى Pdf20FontEmbeddingPolicy (src/Writer/Pdf20FontEmbeddingPolicy.php) وضعان. تحت مواصفة ⁨PDF 2.0⁩، يرفض Strict الإشارة إلى خط ⁨Type 1⁩ قياسي غير مضمَّن (“⁨Base14⁩”) باستثناء مُصنَّف — وهذا هو السلوك المطابق للمواصفة. يحافظ AllowBase14 على المسار الإرشادي التاريخي. خلال نافذة الترحيل، يُصدر واصف الخط الأدنى الذي ما زالت المواصفة تتطلبه، ويرسل تحذيرًا بدلًا من رمي استثناء. يتخذ المُستدعِي الخيار صراحةً على المستند؛ ولا يُستنتج أبدًا من الخط.

الترميز. بالنسبة إلى الخطوط المركَّبة (⁨Type 0⁩)، يُصدر EmbeddedTtfFontDictBuilder (src/Writer/EmbeddedTtfFontDictBuilder.php) السليل CIDFontType2، والأصل Type0، ودفق خريطة ⁨CMap⁩ في /ToUnicode كي تُحل رموز المحارف عودةً إلى ⁨Unicode.⁩ يغيب دفق /ToUnicode على نحو مشروع في حالة واحدة فقط: عندما تكون خريطة ⁨CMap⁩ مسبقة التعريف لـ ⁨CJK⁩ وذاتية الوصف قد منحت القارئ فعلًا تخطيط المحرف إلى ⁨Unicode.⁩ هناك تكون خريطة ⁨CMap⁩ هي الترميز، ولذلك تُسقط المواصفة العادية دفق /ToUnicode الزائد لتوفير البايتات. خارج تلك الحالة، يكون دفق /ToUnicode هو ما يُبقي النص نصًّا.

الشأنما يضمنهما لا يضمنهالإخفاق الصامت عند الخطأ
التضمينالعرض نفسه دون تثبيت الخطأن النص قابل للبحثخط بديل؛ قياسات خاطئة على جهاز آخر
التجزئةملف صغير؛ المحارف المستخدمة فقطأي شيء يخص الترميزمكوّنات مركَّبة مفقودة ← محارف مُشَكَّلة معطوبة
الترميز (/ToUnicode)نص قابل للبحث والنسخ والوصولأن المحارف تُعرَض على نحو صحيحصفحة تبدو مثالية، غير قابلة للبحث / مشوَّهة عند النسخ

الجوانب الثلاثة للخطوط مستقلة. يتعلق التضمين والتجزئة بـ المظهر والحجم؛ ويتعلق الترميز بـالمعنى. قد تجتاز الصفحة الأمرين الأولين وتفشل في الثالث من دون أي شيء مرئي يُظهر ذلك.

قاعدة تسمية الجزء معيارية ودقيقة. Spec: ISO 32000-2, §9.9.2 تتطلب أن يكون لخط الاسم ⁨PostScript⁩ للجزء — أي BaseFont وFontName في الواصف — بداية بوسم من ست أحرف لاتينية كبيرة بالضبط، ثم علامة زائد، ثم الاسم ⁨PostScript⁩ للخط الأصلي. كما تتطلب أن تستخدم الأجزاء المختلفة للخط نفسه في ملف واحد أوسامًا مختلفة. هذه القاعدة هي ما يتيح للقارئ تمييز جزأين عن بعضهما ودمج المستندات على نحو صحيح. Evidence: Standard-backed

الترميز بند منفصل عن العرض. Spec: ISO 32000-2, §9.10.3 يعرّف /ToUnicode بوصفه دفقًا يحتوي على خريطة ⁨CMap⁩ تُخطّط رموز المحارف إلى قيم ⁨Unicode⁩، وإجراء استخراج النص في Spec: ISO 32000-2, §9.10.2 يستخدم تلك الخريطة CMap كي يحوّل رموز المحارف إلى ⁨Unicode⁩ لأغراض البحث والفهرسة. لا شيء في آلية رسم المحارف يمسّ /ToUnicode — وهذا بالضبط سبب إمكان أن يبدو النص صحيحًا ثم يُستخرَج على نحو خاطئ.

فيما يخص التضمين، تنص المواصفة على أن معظم قواميس الخطوط تحمل واصف خط يكون دفق ملف الخط المضمَّن فيه اختياريًا لكن موصى به بشدة. تشدّد ⁨PDF 2.0⁩ هذا الأمر تحديدًا بالنسبة إلى خطوط ⁨Type 1⁩ القياسية الأربعة عشر. سياسة Strict في ⁨NextPDF⁩ هي القراءة المطابقة للمواصفة لهذا التشديد. أما AllowBase14 فهو مخرج التوافق العكسي الصريح والاختياري — فالمحرّك لا يخفّض المستوى في صمت أبدًا.

Strict PDF 2.0 font-embedding enforcement — edition availability
Edition Availability
Core

متاح. التجزئة، وإصدار /ToUnicode، وسياسة التضمين الصريحة Strict / AllowBase14 هي جميعًا سلوك أساسي للمحرّك.

Pro

يضيف فرض امتثال وإبلاغًا أعمق عن تضمين الخطوط على مستوى المواصفة.

Enterprise

يضيف فرض الامتثال ذاته ضمن الواجهة التشغيلية للمؤسسات .

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

% The Type 0 (composite) font dictionary
20 0 obj
<< /Type /Font /Subtype /Type0
/BaseFont /ABCDEF+NotoSans % six-letter subset tag + '+'
/Encoding /Identity-H
/DescendantFonts [21 0 R]
/ToUnicode 23 0 R >> % the map that makes text searchable
endobj
% The descendant CIDFontType2 (carries the subsetted program)
21 0 obj
<< /Type /Font /Subtype /CIDFontType2
/BaseFont /ABCDEF+NotoSans
/CIDToGIDMap /Identity % glyph IDs preserved, not remapped
/FontDescriptor 22 0 R >>
endobj

الحقل /ToUnicode 23 0 R في الكائن 20 هو الفرق بين مستند قابل للبحث وصورة عنه. احذفه (خارج حالة ⁨CMap⁩ مسبقة التعريف)، وسيظل كل محرف يُرسَم على نحو مثالي، ومع ذلك لن يجد البحث عن أي كلمة على الصفحة شيئًا.

الفخّ، بصريح العبارة: عرض المحارف على نحو صحيح لا يقول شيئًا عمّا إذا كان النص نصًّا. العرض يتبع مسار الترميز إلى المحرف. أما البحث والنسخ فيتبعان مسار الرمز إلى ⁨Unicode⁩ (/ToUnicode). هما آليتان مختلفتان تقرآن أجزاءً مختلفة من قاموس الخط. لذلك قد ينتج المستند مخرجات مرئية لا تشوبها شائبة مع غياب /ToUnicode أو خطئه. النتيجة صفحة تبدو موثوقة، لكنها غير قابلة للبحث وظيفيًا — وهو إخفاق ينجو من كل مراجعة بصرية، لأنه بحكم التعريف لا يوجد ما يُرى.

فخّ ذو صلة: افتراض “الخط مضمَّن، إذًا نحن بخير للأرشفة.” التضمين ضروري، لكنه ليس كافيًا. مواصفة مثل ⁨PDF/A⁩ تتوقع أيضًا أجزاءً مسمّاة وفق قاعدة الأحرف الستة وترميزًا صحيحًا. الخط المضمَّن لكنه غير قابل للبحث يظل يفشل.

أداة التجزئة في ⁨NextPDF⁩ هي تحديدًا أداة تجزئة ⁨TrueType⁩. تتطلب جداول ⁨TrueType⁩ الأساسية، وتُعيد الخط الأصلي دون تغيير عندما تكون مفقودة أو عندما يكون المكسب دون عتبة 10% تقريبًا. التجزئة وخريطة ⁨CMap⁩ في /ToUnicode تجعلان النص قابلًا للاستخراج، لكنهما لا تستطيعان إنقاذ خط مصدري يفتقر إلى المعلومات اللازمة لتخطيط محرف عودةً إلى محرف ذي معنى. عندما لا يمكن تحديد قيمة ⁨Unicode⁩، لا يخترع أي قدر من إصدار ⁨CMap⁩ قيمةً.

تتناول هذه الصفحة إنتاج بنية خط صحيحة في المستندات التي يكتبها ⁨NextPDF.⁩ وهي ليست أداة لإصلاح الخطوط في ملفات ⁨PDF⁩ الواردة الاعتباطية. كما أن إصدار جزء وترميز مطابقين لا يصادق بذاته على مستند مقابل مواصفة أرشيفية كاملة — فذلك فحص منفصل وأوسع.

لماذا الوسم من ستة أحرف — لماذا ليس اسم الخط؟ كي يستطيع القارئ تمييز جزأين مختلفين من الخط نفسه عن بعضهما ودمج المستندات دون تصادم مجموعتي المحارف. أجزاء مختلفة، أوسام مختلفة، بحكم القاعدة.

متى يكون مقبولًا عدم وجود /ToUnicode؟ عندما تقدّم خريطة ⁨CMap⁩ مسبقة التعريف لـ ⁨CJK⁩ وذاتية الوصف فعلًا تخطيط المحرف إلى ⁨Unicode.⁩ هناك تكون خريطة ⁨CMap⁩ هي الترميز. لذلك سيكون وجود /ToUnicode منفصل زائدًا. خارج ذلك، غيابه عيب.

هل تضرّ التجزئة يومًا؟ فقط إذا أُجريت على نحو خاطئ. إسقاط مكوّنات المحرف المركَّب يعطب المحارف المُشَكَّلة. وإعادة تخطيط معرّفات المحارف دون إعادة كتابة الإشارات تعطب العرض. يتجنب ⁨NextPDF⁩ الأمرين كليهما بحلّ إغلاق المكوّنات والحفاظ على هوية المحرف.

  • برنامج خط مضمَّن — ملف الخط الفعلي (⁨TrueType/CFF/Type 1⁩) المحمول داخل ملف ⁨PDF⁩ كدفق، كي لا يعتمد العرض على الخطوط المثبتة لدى القارئ.
  • التجزئة — إعادة كتابة برنامج خط ليحتوي على المحارف التي يستخدمها المستند فقط، بهدف تقليل الحجم.
  • وسم الجزء — البادئة الإلزامية من ست أحرف لاتينية كبيرة مضافًا إليها + قبل اسم الخط المجزَّأ (على سبيل المثال، ABCDEF+NotoSans).
  • /ToUnicode — دفق خريطة ⁨CMap⁩ يخطّط رموز المحارف إلى قيم ⁨Unicode⁩؛ وهو ما يجعل نص ⁨PDF⁩ قابلًا للبحث والنسخ والوصول.
  • المحرف المركَّب — محرف مبني بالإشارة إلى محارف أخرى بوصفها مكوّنات؛ ويجب الإبقاء على مكوّناته عند التجزئة.
  • CIDToGIDMap /Identity — الوضع الذي تكون فيه فهارس محارف دفق المحتوى هي معرّفات المحارف الخاصة بالخط دون تغيير؛ ويحافظ ⁨NextPDF⁩ على هوية المحرف كي يبقى هذا صالحًا.
  • ⁨Base14⁩ — خطوط ⁨Type 1⁩ القياسية الأربعة عشر؛ تتوقع ⁨PDF 2.0⁩ أن تكون الخطوط مضمَّنة بدلًا من الإشارة إليها بالاسم.