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

الكاتب: مُسلسِل PDF 2.0 ‏+‏ المرجع التقاطعي

تُسلسِل وحدة الكاتب المستند إلى بايتات بصيغة ⁨PDF⁩. تختار استراتيجية الإصدار، وتكتب بيان الكائنات، وتُصدِر بنية المرجع التقاطعي والمُذيَّل.

Terminal window
composer require nextpdf/core:^3

استخدم PdfWriter نقطة دخول. مرِّر كائن قيمة من نوع DocumentData إلى write(). تُعيد الطريقة ملف ⁨PDF⁩ الكامل كسلسلة بايتات. يجمع الكاتب بيان الكائنات، ويُسنِد أرقام الكائنات، ويُسجِّل إزاحات البايتات، ثم يكتب بنية المرجع التقاطعي في النهاية.

في كل استدعاء، يستخدم الكاتب استراتيجية تسلسل واحدة. تُعرِّف واجهة PdfSerializationStrategy أربع طرائق: writeHeader()، وgetCatalogVersion()، وwriteXrefAndTrailer()، وusesXrefStream(). وتُنفِّذها ثلاث استراتيجيات. يكتب Pdf20StreamStrategy ترويسة %PDF-2.0، ويضبط إصدار الفهرس على /2.0، ويُصدِر دفق مرجع تقاطعي. ويكتب Pdf17TableStrategy%PDF-1.7 وجدول مرجع تقاطعي تقليدي. أما Pdf14TableStrategy فيكتب %PDF-1.4 وجدول مرجع تقاطعي. يختار PdfWriter الاستراتيجية عبر match على DocumentData::$outputProfile. القيمة الافتراضية هي Pdf20StreamStrategy.

يحمل التعداد PdfOutputProfile الإصدارات المستهدفة الثلاثة: Pdf20، وPdf17، وPdf14. ويُتيح headerVersion()، وcatalogVersion()، وallowsObjectStreams()، وusesXrefStream(). يتجاوز وضع مطابقة الأرشفة الملف الشخصي المختار قبل تحديد الاستراتيجية. ويرفض Pdf14FeatureGuard ميزات ⁨PDF 2.0⁩ عندما يكون الملف الشخصي Pdf14.

يربط دفق المرجع التقاطعي كل رقم كائن بإزاحة البايت الخاصة به، كما هو محدَّد في ⁨ISO 32000-2⁩ §7. وتُلحِق التحديثات التزايدية كائنات جديدة بنهاية الملف، كما هو محدَّد في ⁨ISO 32000-2⁩ §7.5.6. يهرِّب الكاتب كل سلسلة حرفية عبر المسار القانوني PdfStringEscaper::escapeLiteral()، الذي يتبع جدول التهريب المعياري في ⁨ISO 32000-2⁩ §7.3.4.2 (⁨ADR-015⁩).

يدعم الكاتب الإخراج الحتمي. يثبِّت setDeterministicMode() مُعرِّفات الكائنات وترتيب مفاتيح القاموس. ويثبِّت setReproducibleClock() الطابع الزمني للمستند. عند ضبط كلا التثبيتين، يُنتِج الإدخال الثابت إخراجًا متطابقًا على مستوى البايت. تُعيد طريقة writeChunked() مولِّدًا يُنتِج ملف ⁨PDF⁩ في أجزاء ثابتة الحجم. يكتب Streaming/StreamingPdfWriter صفحة واحدة في كل مرة إلى دفق يوفِّره المُستدعي للمستندات التي تتجاوز ميزانية الذاكرة.

يعيد Linearizer كتابة ملف ⁨PDF⁩ مكتمل إلى تخطيط خطي. يضع الصفحة الأولى مبكرًا، بحيث يستطيع العارض عرضها قبل اكتمال التنزيل. ويتحقق shadowValidate() من إعادة الكتابة دون تغيير الإدخال.

تنبيه. يُعد PdfWriter.php وLinearizer.php حرجين لإزاحات البايتات وبيان الكائنات (مناطق خطر مُعلَنة في البيان). لا تُغيِّر ترقيم الكائنات أو حساب إزاحات المرجع التقاطعي دون مجموعة اختبارات الكاتب الذهبية.

الصنفالطرائق الرئيسيةالدور
PdfWriterwrite(DocumentData): string، writeChunked(DocumentData, int): Generator، setDeterministicMode()، setReproducibleClock()، setOutputColorProfile()، getLastXrefOffset()، getFileId()المُسلسِل الأساسي
PdfSerializationStrategy (واجهة)writeHeader()، getCatalogVersion()، writeXrefAndTrailer()، usesXrefStream()عقد استراتيجية الإصدار
Pdf20StreamStrategywriteHeader()%PDF-2.0، getCatalogVersion()/2.0، usesXrefStream()trueاستراتيجية دفق المرجع التقاطعي لـ ⁨PDF 2.0⁩
Pdf17TableStrategywriteHeader()%PDF-1.7، جدول مرجع تقاطعياستراتيجية جدول المرجع التقاطعي لـ ⁨PDF 1.7⁩
Pdf14TableStrategywriteHeader()%PDF-1.4، جدول مرجع تقاطعياستراتيجية جدول المرجع التقاطعي لـ ⁨PDF 1.4⁩
PdfOutputProfile (تعداد)Pdf20، Pdf17، Pdf14؛ headerVersion()، catalogVersion()، allowsObjectStreams()مُحدِّد الإصدار المستهدف
PdfXrefWritergenerateFileId()، finalizeTrailerAndXref()مُعرِّف الملف ‏+‏ إنهاء الـ ⁨trailer/xref⁩
Linearizerlinearize(string): string، shadowValidate(string): arrayإعادة كتابة للعرض السريع عبر الويب
Streaming\StreamingPdfWriteropen()، newPage()، close()كاتب دفق أحادي المرور

شغِّل composer docs:generate-api-php -- --module=Writer لتوليد جدول ⁨PHPDoc⁩ كامل.

عيِّنة شِفرة — بداية سريعة

قسم بعنوان «عيِّنة شِفرة — بداية سريعة»

المصدر: examples/02-pdf-factory.php.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Writer\PdfWriter;
$writer = new PdfWriter();
$pdfBytes = $writer->write($documentData);
file_put_contents('out.pdf', $pdfBytes);

الملف الشخصي الافتراضي هو ⁨PDF 2.0.⁩ يبدأ الإخراج بـ %PDF-2.0 وينتهي بدفق مرجع تقاطعي.

يثبِّت هذا المثال الحتمية وساعة ثابتة للحصول على إخراج متطابق على مستوى البايت، ثم يدفق النتيجة في أجزاء ثابتة.

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use DateTimeImmutable;
use NextPDF\Writer\PdfWriter;
use NextPDF\Writer\ReproducibleClock;
$pinned = new DateTimeImmutable('2026-01-01T00:00:00Z');
$writer = new PdfWriter();
$writer->setDeterministicMode($pinned, 'nextpdf-fixed-file-id');
$writer->setReproducibleClock(new ReproducibleClock($pinned));
$out = fopen('php://output', 'wb');
foreach ($writer->writeChunked($documentData, chunkSize: 65536) as $chunk) {
fwrite($out, $chunk);
}
fclose($out);

الحالات الحدِّية والمزالق

قسم بعنوان «الحالات الحدِّية والمزالق»
  • تعمل استراتيجية واحدة فقط لكل استدعاء write(). يُعيد الكاتب ضبط الاستراتيجية من الملف الشخصي في كل استدعاء. ولا يُسرِّب إصدار استدعاء سابق.
  • يتجاوز وضع مطابقة الأرشفة الملف الشخصي المطلوب. يفرض بناء ⁨PDF/A-3⁩ صيغة ⁨PDF 1.7.⁩ ويفرض بناء ⁨PDF/A-4⁩ صيغة ⁨PDF 2.0.⁩
  • يتطلب الإخراج المتطابق على مستوى البايت كلا التثبيتين. اضبط الوضع الحتمي و ساعة قابلة لإعادة الإنتاج. تثبيت واحد وحده لا يكفي.
  • يُنتِج writeChunked() مولِّدًا. استهلكه بالكامل. تُنتِج القراءة الجزئية ملف ⁨PDF⁩ مبتورًا وغير صالح.
  • يعيد Linearizer كتابة إزاحات المرجع التقاطعي. في خط أنابيب لا يحتمل فشل إعادة الكتابة، شغِّل shadowValidate() أولًا.
  • Pdf14TableStrategy هو final readonly. يرفض مسار ⁨PDF 1.4⁩ ميزات ⁨PDF 2.0⁩ عبر Pdf14FeatureGuard؛ ولا يُخفِّضها.

التسلسل خطي بالنسبة إلى عدد الكائنات وإجمالي حجم البايتات. يضيف دفق المرجع التقاطعي مرورًا واحدًا على جدول الكائنات. يحتفظ writeChunked() بالمستند المُجمَّع، لكنه يُنتِجه في شرائح محدودة، فتكون ذروة الذاكرة حجم المستند زائد جزء واحد. لا يحتفظ Streaming\StreamingPdfWriter بالمستند بأكمله؛ استخدمه للمدخلات الأكبر من ميزانية الذاكرة. ميزانية عبء العمل المرجعي هي 1500 ⁨ms⁩ للزمن الجداري و64 ⁨MB⁩ ذروة. تضيف الخطْيَنة مرورًا كاملًا ثانيًا ومرور قياس. خصِّص لها ميزانية صراحةً.

يُسلسِل الكاتب بيان كائنات موثوقًا في الذاكرة. وتمثّل مدخلاته حد التهديد الرئيسي. تمر كل سلسلة حرفية عبر المسار القانوني PdfStringEscaper::escapeLiteral() (⁨ADR-015⁩)، بحيث لا يمكن لبايتات التحكم المضمَّنة الخروج من رمز السلسلة. يُوصَّل التشفير عبر PdfEncryptionWriter ومدخل المُذيَّل /Encrypt. يُرفَض تشفير المفتاح العام باستثناء صريح بدلًا من تخفيضه بصمت. يزيل الوضع الحتمي والساعة القابلة لإعادة الإنتاج القنوات الجانبية للطابع الزمني والترتيب من الإخراج. راجع /modules/core/security/ للاطلاع على نموذج تهديد المستند وحد ثقة التشفير.

يُنتِج الكاتب بُنى ملفات ⁨PDF 2.0⁩: ترويسة %PDF-2.0، وإصدار فهرس /2.0، ودفق مرجع تقاطعي، وتهريب السلاسل الحرفية وفق جدول التهريب في ⁨ISO 32000-2⁩ §7.3.4.2. هذه حقائق تنفيذية. يوجد الدليل في src/Writer/Pdf20StreamStrategy.php، وsrc/Writer/PdfSerializationStrategy.php، واختيار الاستراتيجية في src/Writer/PdfWriter.php. يُختبَر السلوك عبر tests/Unit/Writer/ (192 اختبارًا، بما في ذلك مجموعات Pdf20StreamStrategy وPdfXrefWriter وLinearizer*) وخط الأساس tests/Golden/PdfWriter/PdfWriterGoldenBaselineSmokeTest.

هذا ليس ادعاءً بمطابقة كاملة لـ ⁨PDF 2.0.⁩ فالمطابقة الكاملة لـ ⁨ISO 32000-2⁩ خاصية لمستند كامل تتحقق منه أداة خارجية موثوقة، وليست خاصية للمُسلسِل وحده. لا تُؤكَّد المطابقة الشاملة إلا حيث تؤكِّدها أداة موثوقة: يتحقق tests/Integration/Accessibility/VeraPdfUa2GoldenTest من تجهيزة مُولَّدة مقابل ⁨veraPDF⁩ لـ ⁨PDF/UA-2⁩، ويغطي tests/Standards/Profile/PdfRConformanceTest الملف الشخصي ⁨PDF/R.⁩ يتخطى اختبار ⁨veraPDF⁩ الذهبي عندما يكون ثنائي ⁨veraPDF⁩ غائبًا من المُشغِّل؛ فهو بوابة أداة اختيارية لا غير مشروطة. اضبط VERAPDF_BINARY لتشغيله. يُقرَّر اختيار الملف الشخصي للأرشفة (⁨PDF/A-3⁩ → ⁨PDF 1.7⁩، ⁨PDF/A-4⁩ → ⁨PDF 2.0⁩) بواسطة ⁨ADR-011⁩ ووضع المطابقة، ويُتحقَّق منه بمجموعات المطابقة في /modules/core/conformance/. خارج تلك الملفات الشخصية المدعومة بأداة موثوقة، اذكر أن الكاتب «يُنتِج بُنى ⁨PDF 2.0⁩؛ والمطابقة يتحقق منها ⁨veraPDF⁩ للملف الشخصي ⁨PDF/UA-2⁩» بدلًا من تأكيد مطابقة غير مشروطة.