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

تشريح ملف PDF

افتح أيّ ملف ⁨PDF⁩ في محرّر نصوص عادي، فيكون أول ما تراه مطمئنًا: ترويسة %PDF-1.x أو %PDF-2.0. وآخر ما تراه هو %%EOF. وكل ما بين هذين السطرين آلةٌ صغيرة أنيقة للعثور على الكائنات برقمها. هذه الصفحة تشريح. نفتح الملف، ونسمّي كل عضو، ونبيّن كيف تتّصل.

إنها الرفيق البنيوي لجارَتيها. تتناول ما حقيقة ملف ⁨PDF⁩ الملفَّ بوصفه رسمًا بيانيًّا للكائنات؛ وتغطّي التحديثات التزايدية كيف ينمو مع الزمن. أما هذه الصفحة فتبقى قريبة من البايتات — المناطق الفيزيائية التي يسير المحلّل عبرها، بالترتيب الذي تقع به على القرص.

نادرًا ما تحتاج هذا كي تستخدم ملف ⁨PDF⁩. بل تحتاجه يوم يسير أحدها على نحو خاطئ. ملف يُفتَح في عارض دون آخر؛ ومدقّق يُبلّغ عن “جدول مراجع متقاطعة تالف”؛ ومستند موقَّع يُخفِق فجأة في التحقّق. ليس أيّ من تلك ألغازًا متى استطعت قراءة التشريح. إنها رقم لم يعد يطابق موضعًا، أو منطقة في المكان الخطأ، أو ذيل يشير إلى لا شيء.

معرفة التخطيط تحوّل “ملف ⁨PDF⁩ تالف” إلى تشخيص تستطيع التصرّف بناءً عليه. إنه الفرق بين تجاهل صندوق مغلق والإشارة إلى البايت الذي يكذب بالضبط.

لملف ⁨PDF⁩ مطابق أربعة أجزاء فيزيائية، بهذا الترتيب في الملف (Spec: ISO 32000-2, §7.5.1):

  1. ترويسة — سطر واحد، %PDF-2.0، يسمّي الإصدار.
  2. جسم — جُلّ الملف: تتابع من الكائنات غير المباشرة المرقّمة.
  3. قسم مراجع متقاطعة — فهرس من رقم الكائن إلى إزاحة البايت حيث يقيم ذلك الكائن. تستخدم ملفات ⁨PDF⁩ الكلاسيكية جدولًا نصيًّا؛ ويستخدم ⁨PDF 2.0⁩ دفق xref مضغوطًا.
  4. ذيل — قاموس صغير يسمّي نقطة الدخول، يتبعه startxref، وإزاحة، و%%EOF.

والمفارقة: لا يبدأ القارئ من الأعلى. بل يبدأ من الأسفل، ويقرأ startxref ليجد الفهرس، ويستخدم ذلك الفهرس للوصول إلى أيّ كائن مباشرةً. يُكتَب الملف من المقدّمة إلى المؤخّرة لكنه يُقرأ من المؤخّرة إلى المقدّمة.

لنسر في المناطق الأربع بالترتيب، والبايتات أمامنا.

الترويسة سطر واحد. يكتب ⁨NextPDF⁩ %PDF-2.0، وعادةً سطر تعليق ثانيًا من بايتات عالية البِت كي تعامل أدوات النقل الساذجة الملف بوصفه ثنائيًّا لا نصيًّا. وذلك السطر الثاني هو سبب ظهور ⁨PDF⁩ مفتوحًا كنصّ عادي بقليل من التشويش مباشرةً بعد الإصدار.

الجسم هو حيث يقيم المستند. كل كائن غير مباشر هو رقم، وجيل، والكلمة المفتاحية obj، وقيمة، وendobj (Spec: ISO 32000-2, §7.3.10). والقيمة واحدة من بضعة أشكال — لكن شكلين يحملان معظم الحِمل:

  • قاموس، << /Key value … >>، هو خريطة من الأسماء إلى القيم. شجرة الصفحات، والفهرس، وواصِفات الخطوط: كلّها قواميس.
  • الدفق قاموس يتبعه stream، وكتلة بايتات اعتباطية، وendstream. محتوى الصفحة، والخطوط المُضمَّنة، والصور دفقات، مضغوطة في الغالب دائمًا. (مرشّحاتها قصّة قائمة بذاتها، مرويّة في الدفقات والمرشّحات.)

تشير الكائنات بعضها إلى بعض بـإشارة غير مباشرة2 0 R تعني “الكائن 2، الجيل 0”. وذلك هو التوصيل الذي يحوّل قائمة كائنات مسطّحة إلى رسم بياني.

قسم المراجع المتقاطعة هو الجزء الذي لا يتصوّره معظم الناس تصوّرًا صحيحًا. في الصورة الكلاسيكية يكون نصًّا عاديًّا: الكلمة المفتاحية xref، ثم أقسام فرعية من أسطر بحجم ثابت قدره 20 بايتًا (Spec: ISO 32000-2, §7.5.4). كل سطر هو إزاحة بايت من عشرة أرقام، وجيل من خمسة أرقام، وراية مفردة — n للمستخدَم، f للحرّ — مُبطَّنة إلى عشرين بايتًا بالضبط كي يستطيع القارئ القفز إلى أيّ مدخل بالحساب وحده. ويستبدل ⁨PDF 2.0⁩ بهذا دفق مراجع متقاطعة: الفهرس نفسه، لكنه ثنائي ومضغوط داخل كائن دفق موسوم بـ/Type /XRef (Spec: ISO 32000-2, §7.5.8). أصغر، وقادر على وصف الكائنات المحزومة داخل دفقات الكائنات.

الذيل هو فهرس محتويات الملف (Spec: ISO 32000-2, §7.5.5). يسمّي /Root — فهرس المستند، الكائن الوحيد الذي يتدلّى منه كل شيء آخر — و/Size، عدد الكائنات. ثم تأتي المصافحة التي تجعل القراءة العكسية ممكنة: startxref، وإزاحة بايت في سطرها الخاصّ، و%%EOF. يقفز القارئ إلى النهاية، ويقرأ تلك الإزاحة، ويثب مباشرةً إلى قسم المراجع المتقاطعة، وينطلق.

  1. Headerسطر واحد، %PDF-2.0، يسمّي الإصدار. وعادةً يتبعه تعليق علامة ثنائية.
  2. Bodyكائنات غير مباشرة مرقّمة — قواميس ودفقات — مُشار إليها بـN G R.
  3. Cross-reference sectionجدول نصي من مداخل بحجم 20 بايتًا، أو دفق /Type /XRef مضغوط في PDF 2.0.
  4. Trailerيسمّي /Root و/Size، ثم startxref + إزاحة + %%EOF.
  5. Read orderيبدأ القارئ من %%EOF، ويتبع startxref إلى الفهرس، ثم يصل إلى كل كائن مباشرةً.
المناطق الفيزيائية الأربع لملف PDF بترتيب الملف، والمسار الذي يسلكه القارئ فعلًا عبرها — بدءًا من الذيل ثم باتجاه الداخل عبر قسم المراجع المتقاطعة.

ثمّة منطقة خامسة ينمّيها ملف طويل العمر: تحديث تزايدي. لا يُكتَب التغيير في موضعه. بل تُلحَق الكائنات المتغيّرة، وقسم مراجع متقاطعة جديد، وذيل جديد بعد أول %%EOF، ويحمل ذلك الذيل الجديد /Prev — إزاحة قسم المراجع المتقاطعة السابق (Spec: ISO 32000-2, §7.5.6). تشكّل الأقسام سلسلة عكسية؛ ولأيّ رقم كائن، يفوز أحدث مدخل. ولأن البايتات الأصلية لا تتحرّك قطّ، يبقى الفحص التشفيري على نطاق البايتات الذي يغطّيه التوقيع فعلًا قائمًا بعد التحديث. ويمكن لتحديث تزايدي لاحق أن يغيّر ما يُبلّغ عنه المدقّق بشأن المستند ككلّ — هل التغييرات اللاحقة للتوقيع مسموح بها، وما الذي يُعتبَر التوقيع مصدّقًا عليه — لكنه لا يستطيع تبديل البايتات الموقَّعة نفسها. وتلك الخاصّة هي موضوع التحديثات التزايدية بأكمله.

هذا هو التشريح كلّه في ملف أصغري واحد. الأرقام تحت xref هي إزاحات بايت، وعليها أن تكون دقيقة — أشِر إلى حرف واحد بعد موضع بدء كائن وييأس قارئ صارم.

%PDF-2.0
1 0 obj
<< /Type /Catalog /Pages 2 0 R >>
endobj
2 0 obj
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
endobj
3 0 obj
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 612 792] >>
endobj
xref
0 4
0000000000 65535 f
0000000009 00000 n
0000000058 00000 n
0000000115 00000 n
trailer
<< /Size 4 /Root 1 0 R >>
startxref
186
%%EOF

اقرأه بالطريقة التي يقرأ بها المحلّل. السطر الأخير: %%EOF. وفوقه: startxref 186، فاقفز إلى البايت 186، حيث يبدأ xref. يقول الجدول إن الكائن 1 يقيم عند البايت 9. ويشير /Root 1 0 R في الذيل إلى هناك — الفهرس — ومن الفهرس تسير في /Pages إلى شجرة الصفحات فتجد الصفحة الوحيدة. الكائن 0 هو دائمًا رأس قائمة الكائنات الحرة بالجيل 65535، أحفورة من أول تصميم للتنسيق ما زال كل قارئ يتوقّع رؤيتها.

الفخّ هو قراءة ملف ⁨PDF⁩ كقصّة — من الأعلى إلى الأسفل، بالترتيب. إنه ليس قصّة؛ بل فهرس بمؤشّر عكسي. لا يلزم أن تكون أرقام الكائنات متسلسلة في الملف، ويمكن أن تظهر الكائنات بأيّ ترتيب فيزيائي، ولا يعتمد قارئ مطلقًا على موضعها. الخريطة الموثوقة الوحيدة هي قسم المراجع المتقاطعة، والطريق الوحيد لإيجاد تلك الخريطة هو إزاحة startxref في النهاية تمامًا.

والنتيجة تفاجئ الناس. ملف ⁨PDF⁩ بجسم لا تشوبه شائبة ورقم واحد خاطئ في startxref غير قابل للقراءة — لا يستطيع القارئ إيجاد الفهرس. وملف ⁨PDF⁩ بكائناته في ترتيب مشوّش لكن بقسم مراجع متقاطعة صحيح سليم تمامًا. الموضع الفيزيائي لا يحمل معنى. الموضع المسجَّل يحمله كلّه.

تصف هذه الصفحة البنية الفيزيائية، لا محتوى الصفحة. أما كيفية وصول العلامات إلى صفحة — مُعامِلات دفق المحتوى، وعرض النص، وحالة الرسومات — فموضوع منفصل. كما تصف ملفًا حسن التكوين. فملفات ⁨PDF⁩ الواقعية كثيرًا ما تكون معطوبة قليلًا وتنجو فقط لأن العارضات المتسامحة تعيد بناء جدول المراجع المتقاطعة بالمسح بحثًا عن الكلمات المفتاحية obj. وذلك الإنقاذ سلوك عارض، لا شيء يضمنه التنسيق.

Reading and repairing arbitrary third-party PDFs — edition availability
EditionAvailability
Core⁨NextPDF⁩ كاتب. يسجّل كل إزاحة من مخزن الإخراج لحظة إصدار كل كائن، فتكون للملفات التي ينتجها قسم مراجع متقاطعة يطابق الجسم بالبناء.
Proتحليل جدول مراجع متقاطعة تالف أو إعادة بنائه أو إصلاحه في ملف لم يكتبه NextPDF خارج النطاق عبر كل إصدار.
Enterpriseلفحص بنية ملف قائم، استخدم محلّلًا مخصّصًا أو مدقّقًا؛ يضمن NextPDF الصحّة لما يكتبه، لا لما يقرؤه.

لماذا لملف ⁨PDF⁩ سطر علامة ثنائية بعد الترويسة؟ كانت بعض أدوات النقل الأقدم تشوّه ملفًا تظنّه نصًّا عاديًّا. ويجعل تعليق البِت العالي الملفَّ يبدو ثنائيًّا بلا لبس، كي ينجو من الرحلة دون تغيير.

أهو دفق xref مجرّد جدول أصغر؟ في معظمه، مع قوّة إضافية واحدة. فبعد كونه مضغوطًا، يستطيع دفق xref وصف كائنات مخزّنة داخل دفقات الكائنات — مداخل لا تملك طريقة للتعبير عنها في الجدول النصي الكلاسيكي بحجم 20 بايتًا.

أيمكن أن يكون لديّ جدول ودفق معًا في ملف واحد؟ تستخدم المراجعة الواحدة هذا أو ذاك. لكن ملفًا هجينًا يمكن أن يقرن جدولًا كلاسيكيًّا للقرّاء القدامى بدفق مراجع متقاطعة للجدد، كي يجد كل نوع من القرّاء فهرسًا يفهمه.

  • الترويسة — السطر الأول، %PDF-2.0، يسمّي الإصدار؛ ويتبعه عادةً تعليق علامة ثنائية.
  • الكائن غير المباشر — كائن مرقّم في الجسم، يُكتَب N G obj … endobj، حيث N رقم الكائن وG الجيل.
  • القاموس — خريطة << /Key value … >> من الأسماء إلى القيم؛ أكثر أشكال الكائنات شيوعًا.
  • الدفق — قاموس إضافةً إلى كتلة بايتات بين stream و endstream، يُستخدَم للمحتوى والخطوط والصور.
  • جدول المراجع المتقاطعة (xref) — الفهرس من رقم الكائن إلى إزاحة البايت؛ جدول نصي بحجم 20 بايتًا لكل مدخل كلاسيكيًّا، ودفق /Type /XRef في ⁨PDF 2.0⁩.
  • الذيل — القاموس الذي يسمّي /Root و/Size، يُعثَر عليه عبر إزاحة startxref في نهاية الملف.
  • التحديث التزايدي — كائنات متغيّرة، وقسم مراجع متقاطعة جديد، و ذيل جديد مُلحَق بعد %%EOF، مع /Prev يتسلسل إلى الوراء إلى القسم السابق.