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

ContentStream: مُصدِر دفق محتوى PDF

تُصدِر وحدة ⁨ContentStream⁩ معاملات المحتوى المُعلَّم في تنسيق ⁨Portable Document Format⁩ (⁨PDF⁩). فهي تفتح وسوم البنية والأثريات وتغلقها، وتتعقّب عمق التداخل، وتُرجع المخزن المؤقت للمعاملات.

Terminal window
composer require nextpdf/core:^3

ContentStreamBuilder هو الصنف الوحيد في الوحدة. يبني هذا الصنف طبقة المحتوى المُعلَّم من دفق محتوى الصفحة. يُرمِّز دفق المحتوى محتوى الصفحة على هيئة سلسلة من المعاملات — ⁨ISO 32000-2⁩ §8. ثم يُصدِر الباني معاملات المحتوى المُعلَّم حول ذلك المحتوى.

append() يُضيف بايتات المعاملات الخام حرفيًّا. ولا يهرّب الباني هذا المُدخَل؛ فأنت المسؤول عن صحّته. استخدم هذا الحدّ عندما يحتاج خط معالجة ⁨HTML⁩ ووحدة ⁨Graphics⁩ إلى تداخل معاملاتهما الخاصة.

beginTag() يفتح تسلسلًا مُعلَّمًا بوسم بنية. وهو يُصدِر معامل BDC مع قائمة خصائص MCID، وفقًا لـ ⁨ISO 32000-2⁩ §14.6. ويُصدِر endTag() معامل EMC المُطابِق. كما يَعدّ الباني عمق التداخل. إذا استدعيتَ endTag() دون تسلسل مفتوح، فإنه يطرح PageLayoutException بدلًا من كتابة EMC غير متوازن.

beginArtifact() يفتح تسلسلًا أثريًّا. استخدم الأثريات لعناصر الترقيم الزخرفية — الترويسات والتذييلات وأرقام الصفحات والخطوط — التي يجب أن تبقى خارج شجرة البنية، وفقًا لـ ⁨ISO 32000-2⁩ §14.8.2.2. النوع الفرعي إحدى أربع قيم في ⁨ISO⁩: Pagination، أو Layout، أو Page، أو Background. فضِّل التعداد ArtifactSubtype المُحدَّد النوع. ويُتحقَّق من الحِمل الزائد النصّي مقابل التعداد، لذلك تفشل أي قيمة غير معيارية فورًا.

relabelTag() يُعيد كتابة وسم سبق إصداره في موضعه. finish() يُرجع المخزن المؤقت الكامل ويطرح استثناءً إذا كان المحتوى المُعلَّم غير متوازن. drain() يُرجع المخزن المؤقت حتى الآن دون فحص التوازن، من أجل البث التدريجي. peek() يُرجع المخزن المؤقت دون استهلاكه. reset() يمسح الحالة.

الطريقةالتوقيعالدور
append()append(string $raw): voidيُضيف بايتات المعاملات الخام حرفيًّا (دون تهريب)
beginTag()beginTag(string $structType, int $mcid): voidيفتح تسلسل بنية BDC
endTag()endTag(): voidيُغلق التسلسل الأعمق بـ EMC
beginArtifact()beginArtifact(ArtifactSubtype|string $type): voidيفتح تسلسلًا أثريًّا
endArtifact()endArtifact(): voidيُغلق الأثري الأعمق
getMarkedContentDepth()getMarkedContentDepth(): intيُرجع عمق التداخل الحالي
relabelTag()relabelTag(string $old, string $new, int $mcid): voidيُعيد كتابة وسم مُصدَر في موضعه
finish()finish(): stringيُرجع المخزن المؤقت الكامل؛ ويطرح استثناءً إذا كان غير متوازن
drain()drain(): stringيُرجع المخزن المؤقت دون فحص التوازن
peek()peek(): stringيُرجع المخزن المؤقت دون استهلاكه
reset()reset(): voidيمسح كل الحالة

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

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\ContentStream\ContentStreamBuilder;
$builder = new ContentStreamBuilder();
$builder->beginTag('P', mcid: 0);
$builder->append("BT /F1 12 Tf 72 720 Td (Hello) Tj ET\n");
$builder->endTag();
$pageContent = $builder->finish();

استخدم هذا النمط لتغليف فقرة بوسم بنية وتذييل بأثري. يبثّ النمط المخزن المؤقت تدريجيًّا عبر drain().

<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use NextPDF\Accessibility\ArtifactSubtype;
use NextPDF\ContentStream\ContentStreamBuilder;
$builder = new ContentStreamBuilder();
$builder->beginTag('H1', mcid: 0);
$builder->append($titleOperators);
$builder->endTag();
$builder->beginArtifact(ArtifactSubtype::Pagination);
$builder->append($footerOperators);
$builder->endArtifact();
if ($builder->getMarkedContentDepth() !== 0) {
throw new RuntimeException('Unbalanced marked content before flush.');
}
$chunk = $builder->drain();
  • append() لا يهرّب المُدخَل. مرّر بايتات معاملات صالحة فقط؛ فالباني يثق بالمستدعي.
  • endTag() وendArtifact() يطرحان استثناءً عند الجريان السفلي. لا تُغلق أبدًا تسلسلًا غير مفتوح.
  • finish() يفحص التوازن ويطرح استثناءً عندما لا يكون العمق صفرًا. drain() لا يفحص. استخدم drain() للبث التدريجي فقط.
  • عدّاد العمق لا يميّز الوسوم عن الأثريات. EMC يُغلق التسلسل الأعمق من أيّ من النوعين. أدخِل التسلسلات بترتيب صارم.
  • يُتحقَّق من الحِمل الزائد النصّي لـ beginArtifact() مقابل التعداد. النوع الفرعي غير المعياري يفشل عند الاستدعاء، لا في المُخرَج.
  • relabelTag() يُعيد كتابة وسم مُصدَر. استخدم نفس mcid الذي استخدمته لإصداره.

كل عملية هي إلحاق سلسلة بكلفة ⁨O⁩(1)، باستثناء relabelTag()، الذي يُجري إعادة كتابة بكلفة ⁨O⁩(⁨buffer⁩). تحتفظ الوحدة بمخزن سلاسل مؤقت واحد وعدّاد عمق صحيح واحد. وهي لا تُجري أي تحليل، ولا تُخصّص سوى المخزن المؤقت. ميزانية حِمل العمل المرجعي هي 1500 ⁨ms⁩ زمنًا و64 ⁨MB⁩ ذروةً. وتبقى هذه الوحدة أدنى من هذه الميزانية بكثير.

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

تُصدِر الوحدة ⁨emits⁩ بُنى معاملات محتوى مُعلَّم متوافقة مع ⁨ISO 32000-2⁩: أزواج BDC/EMC مع قائمة خصائص MCID وفقًا لـ §14.6، وتسلسلات أثرية وفقًا لـ §14.8.2.2. هذه حقائق تنفيذية. الدليل هو src/ContentStream/ContentStreamBuilder.php، والتعداد src/Accessibility/ArtifactSubtype.php، وtests/Unit/ContentStream/ContentStreamBuilderMarkedContentBalanceCoverageTest إضافةً إلى ContentStreamBuilderRelabelTagInvariantTest. وهي ليست ادّعاءً بالمطابقة الشاملة من طرف إلى طرف لـ ⁨PDF/UA-2⁩ أو ⁨PDF 2.0.⁩ يتحقق وسيط خارجي من بنية ⁨PDF⁩ الموسومة التي تشارك فيها هذه المعاملات: tests/Integration/Accessibility/VeraPdfUa2GoldenTest يفحص مُلحَقًا مُولَّدًا مقابل ⁨veraPDF⁩ لمواصفة ⁨PDF/UA-2.⁩ يتخطّى اختبار الوسيط ذاك ⁨skips when the veraPDF binary is absent⁩، لذا فهو بوابة اختيارية. اذكر أن هذه الوحدة “⁨produces marked-content structures⁩; ⁨PDF/UA-2 conformance is validated by veraPDF⁩” بدلًا من تأكيد مطابقة غير مشروطة.