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

Pro الإصدار

‏⁨Webview⁩

تُقدِّم ⁨Webview⁩ ملفّ ⁨PDF⁩ مُخطَّطًا خطّيًا (‏⁨Fast Web View⁩) عبر ⁨HTTP⁩ فيستطيع العميل بدءَ تصيير الصفحة 1 من بادئة أمامية صغيرة بينما بقيّة الملفّ ما زالت في الطريق. وهي تلفّ البايتات الخام بوصفها ⁨LinearizedDocument⁩، وتجيب على طلبات ⁨Range⁩ باستجابات محتوى جزئي وفق ⁨RFC 9110⁩ عبر ⁨ByteRangeResponder⁩ بنمط ⁨PSR-7⁩، وتستطيع البرهنة (عبر ⁨FirstPageProber⁩) على أنّ الصفحة الأولى مكتفية ذاتيًا داخل البادئة.

تُشحَن هذه الإمكانية في ‏⁨NextPDF Pro⁩ (⁨nextpdf/pro⁩) وتُفعَّل بمظروف ترخيص من فئة ⁨Pro⁩. ولا يُحمِّل أيُّ نشرٍ يفتقر إلى ذلك الاستحقاق أصنافَ هذه الإمكانية. قارِن الإصدارات واحصل على ترخيص.

ولا توجد علامة ترخيص منفصلة لكلّ ميزة. ويُوصَل المُجيب بمصانع ⁨PSR-17⁩ الخاصّة بك في وقت التشغيل — ⁨ResponseFactoryInterface⁩ و⁨StreamFactoryInterface⁩ — ونوعُ الوسيط يكون افتراضيًا ⁨application/pdf⁩ كوسيطٍ للبنّاء، لا مفتاحَ ترخيص.

Terminal window
composer require nextpdf/pro

تعيش الشيفرة ضمن النطاق الاسمي ⁨NextPDF\Pro\Webview⁩.

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

يحلّل ⁨LinearizedDocument::fromBytes()⁩ البايتات عبر ⁨LinearizationView⁩ القرائية في النواة (لا تعيد ⁨Pro⁩ أبدًا تنفيذ تحليل التخطيط الخطّي) ويرفض أيَّ شيء ليس مستندًا مُخطَّطًا خطّيًا قابلًا للاستخدام: غير مُخطَّط خطّيًا أصلًا، أو طول ⁨/L⁩ مُعلَن لا يطابق طول البايتات الحقيقي، أو إزاحة نهاية الصفحة الأولى ⁨/E⁩ ليست إزاحة موجبة داخل الملفّ. ولذلك يكون الإنشاء كلّيًّا — بمجرّد أن تمسك بـ⁨LinearizedDocument⁩، تكون كلُّ إزاحة يكشفها موثوقة.

ثمّ يجيب ⁨ByteRangeResponder⁩ على طلب ⁨HTTP⁩. وهو مُنفَّذ مقابل ⁨PSR-7⁩ / ⁨PSR-17⁩ فقط، من دون اقتران بأي إطار عمل. وهو يُعلِن دائمًا ⁨Accept-Ranges: bytes⁩ و⁨ETag⁩ قويًّا حتميًّا بصيغة ⁨SHA-256⁩، ويحلّل ترويسة ⁨Range⁩ من العميل وفق ⁨RFC 9110 §14⁩، ويُرجِع إمّا ⁨200 OK⁩ كاملًا، أو نطاقًا واحدًا ⁨206 Partial Content⁩، أو استجابة ⁨206⁩ بنوع ⁨multipart/byteranges⁩ لعدّة نطاقات، أو ⁨416 Range Not Satisfiable⁩.

و⁨FirstPageProber⁩ هو جانب البرهان البنيوي: فهو يُحدِّد كمّيًّا بادئة الصفحة الأولى، والكسر الذي تمثّله تلك البادئة من الملفّ كاملًا، وما إذا كان تدفّق التلميح الأساسي يقع بالكامل داخلها — وهي الخاصّية التي تتيح للقارئ تحديد موقع كائنات الصفحة 1 من البادئة وحدها.

لا تعيد ⁨Webview⁩ أبدًا تحليلَ التخطيط الخطّي بنفسها. بل تستعير ⁨LinearizationView⁩ القرائية من النواة، فترث طبقةُ التسليم محلِّلًا واحدًا مُدقَّقًا بدلًا من نسخة ثانية آخذة في الانحراف. والإنشاء كلّيّ عن قصد. ويرفض ⁨LinearizedDocument::fromBytes()⁩ التخطيطَ المشوَّه مقدَّمًا، فكلُّ إزاحة تثق بها استجابةُ ⁨Range⁩ قد جرى التحقّق منها أوّلًا. ولا يتكلّم المُجيب سوى ⁨PSR-7⁩ و⁨PSR-17⁩، فتُقدِّم الشيفرةُ نفسُها ملفَّ ⁨PDF⁩ مُخطَّطًا خطّيًا من أيّ مكدّس ⁨HTTP⁩. وهذا الانضباط هو ما يجعل التسليم التدريجي بالنطاقات آمنًا لعرضه على عملاء غير موثوقين بكمّيات كبيرة.

خلفية التصميم: توليد المستندات عالي الحجم.

كيف يعمل التقديم التدريجي بنطاق البايتات

قسم بعنوان «كيف يعمل التقديم التدريجي بنطاق البايتات»
  1. ابنِ ⁨LinearizedDocument⁩ من بايتات ⁨PDF⁩ المُصيَّرة. ويُثير الإدخال غير الصالح ⁨UnsupportedDocumentException⁩ مقدَّمًا.
  2. سلِّم المستندَ وطلبَ ⁨PSR-7⁩ الوارد ⁨ServerRequestInterface⁩ إلى ⁨ByteRangeResponder::respond()⁩. ويقرأ المُجيب ⁨Range⁩ (والشرطَ المسبق الاختياري ⁨If-Range⁩)، ويُنتِج ⁨ResponseInterface⁩ الصحيح بنمط ⁨PSR-7⁩.
  3. يطلب العميل البادئة الأمامية أوّلًا (أو تدفعها أنت بـ⁨firstPageResponse()⁩)، ويُصيِّر الصفحة 1، ثمّ يطلب النطاقات المتبقّية مع تمرير المستخدم.

يستخدم نموذج نطاق البايتات إزاحات شاملة وفق ⁨RFC 9110 §14.1.2⁩: فـ⁨ByteRange⁩ هو ⁨firstByte⁩–⁨lastByte⁩ على تمثيل لـ⁨contentLength⁩، وحقله ⁨Content-Range⁩ هو ⁨bytes first-last/length⁩.

  • LinearizedDocument::fromBytes()⁩ كلّيّ: مستندٌ غير مُخطَّط خطّيًا، أو عدمُ تطابق ⁨/L⁩، أو إزاحة ⁨/E⁩ غير موجبة / خارج الملفّ، كلُّ واحدة منها تُثير ⁨UnsupportedDocumentException⁩ بدلًا من إنتاج مستند غير آمن.
  • ETag⁩ هو وسم كيان ⁨SHA-256⁩ قويّ على البايتات بالضبط، يُحفَظ مرّةً واحدةً عند الإنشاء. ويُنتِج إدخالُ تصييرٍ متطابق بايتاتٍ متطابقة وبالتالي ⁨ETag⁩ متطابقًا، فتتصرّف الذواكر المؤقّتة و⁨If-Range⁩ على نحو متوقَّع.
  • طلبٌ بلا ⁨Range⁩ منطبق يُرجِع ⁨200 OK⁩ بالجسم الكامل. و⁨If-Range⁩ لا يطابق ⁨ETag⁩ القويّ الحالي يتسبّب بتجاهل ⁨Range⁩ وإرجاع ⁨200⁩ كامل (‏⁨RFC 9110 §13.1.5⁩). ولا يُكرَّم إلّا شكلُ ⁨If-Range⁩ بوسم الكيان القويّ؛ ويُعامَل ⁨If-Range⁩ بتاريخ ⁨HTTP⁩ كعدم تطابق.
  • وحدة نطاق غير معروفة أو ⁨Range⁩ غير صالح نحويًا يُتجاهَل ويُرجَع ⁨200⁩ كامل (‏⁨RFC 9110 §14.2⁩).
  • نطاق واحد قابل للإرضاء يُرجِع ⁨206 Partial Content⁩ مع ⁨Content-Range⁩؛ ونطاقات متعدّدة قابلة للإرضاء تُرجِع ⁨206⁩ بنوع ⁨multipart/byteranges⁩. ونطاقات بايتات صالحة لا يُرضى أيٌّ منها تُرجِع ⁨416⁩ مع ⁨Content-Range: bytes */length⁩ (‏⁨RFC 9110 §15.3.7⁩).
  • يُصدِر ⁨firstPageResponse()⁩ استجابة ⁨206⁩ تحمل بالضبط نطاق بايتات الصفحة الأولى ⁨[0, /E - 1]⁩ — وهو شكل الدفع من الخادم لـ«الصفحة الأولى قبل التنزيل الكامل».

ما يلي يعكس واجهة برمجة التطبيقات العامة المُوثَّقة. ولا يشحن المستودع مثالًا قابلًا للتشغيل لهذه الوحدة.

use NextPDF\Pro\Webview\LinearizedDocument;
use NextPDF\Pro\Webview\ByteRangeResponder;
$document = LinearizedDocument::fromBytes($pdfBytes);
$responder = new ByteRangeResponder($responseFactory, $streamFactory);
$response = $responder->respond($document, $request);

مثال شيفرة — دفع الصفحة الأولى والتحرّي

قسم بعنوان «مثال شيفرة — دفع الصفحة الأولى والتحرّي»
use NextPDF\Pro\Webview\LinearizedDocument;
use NextPDF\Pro\Webview\ByteRangeResponder;
use NextPDF\Pro\Webview\FirstPageProber;
use NextPDF\Pro\Webview\Exception\UnsupportedDocumentException;
try {
$document = LinearizedDocument::fromBytes($pdfBytes);
} catch (UnsupportedDocumentException $e) {
// Not a usable linearized document — fall back to plain full delivery.
// ...
return;
}
$prober = new FirstPageProber($document);
if ($prober->isFirstPageSelfContained()) {
// Push exactly the first page's bytes for an instant render.
$response = (new ByteRangeResponder($responseFactory, $streamFactory))
->firstPageResponse($document);
}
  • تتطلّب ⁨Webview⁩ ملفّ ⁨PDF⁩ مُخطَّطًا خطّيًا بالفعل. وإن لم يكن المستند المُصيَّر مُخطَّطًا خطّيًا، فعِّل التخطيط الخطّي وقت التصيير، أو قدِّمه بتسليم كامل بسيط — إذ يستطيع ⁨respondToBytes()⁩ ما زال تقديمَ نطاقات على بايتات اعتباطية (غير مُخطَّطة خطّيًا) حين تحتاج دعمَ النطاقات فقط، لا دلالات الصفحة الأولى.
  • التحديثات التزايدية مهمّة: مستندٌ أُلحِق به بما يتجاوز طول ⁨/L⁩ المُعلَن يُرفَض كعدم تطابق طول، لأنّ إزاحات نطاق البايتات لن تعود موثوقة.
  • يحدّ المُجيب عددَ النطاقات المتمايزة التي يكرّمها لكلّ طلب. وطلبٌ يطلب نطاقات مدموجة أكثر من الحدّ، أو بايتات إجمالية أكثر من التمثيل كاملًا، يُتجاهَل ⁨Range⁩ الخاصّ به ويُقدَّم ⁨200⁩ كامل.

بادئة الصفحة الأولى هي إزاحة نهاية الصفحة الأولى ⁨/E⁩ مُثبَّتة عند طول الملفّ، فيُبلِّغ ⁨FirstPageProber::prefixFraction()⁩ عن مدى صغر الجلب الأوّلي نسبةً إلى الملفّ كاملًا — ولمستند متعدّد الصفحات هذا هو المغزى كلُّه من ‏⁨Fast Web View⁩. ويقطّع بناءُ الاستجابة سلسلةَ البايتات في الذاكرة؛ والكلفة متناسبة مع البايتات المُنتقاة. ويُحسَب ⁨ETag⁩ مرّةً واحدةً لكلّ مستند. قِس بمستندات تمثيلية.

عامِل الإدخال على أنّه غير موثوق. ويتحقّق ⁨LinearizedDocument::fromBytes()⁩ من ثوابت التخطيط الخطّي قبل استخدام أي إزاحة. ويرفض المُجيب ⁨contentType⁩ يحتوي على محارف تحكُّم لمنع حقن الترويسات، ويشتقّ حدًّا فاصلًا متعدّد الأجزاء مضمونًا ألّا يظهر داخل الجسم، ويدمج النطاقات المتداخلة ويحدّ عددها وحجمها الإجمالي للدفاع عن صنف حرمان الخدمة بتضخيم النطاق متعدّد الأجزاء (‏⁨Apache HTTPD CVE-2011-3192⁩). ولا تُسجِّل هذه الوحدة أيَّ محتوى مستند.

يتبع تسليم نطاق البايتات ⁨RFC 9110⁩ (‏⁨HTTP Semantics⁩) — ⁨§14⁩ لطلبات النطاق، و⁨§13.1.5⁩ لـ⁨If-Range⁩، و⁨§15.3.7⁩ لـ⁨416⁩. ونموذج المستند المُخطَّط خطّيًا هو تخطيط ‏⁨Fast Web View⁩ الموصوف في ⁨ISO 32000-2 Annex F⁩. ولا تؤكّد الوحدة أي مُعرِّفات بنود خارجية إضافية تتجاوز السلوك الذي تتحقّق منه اختباراتها.

لا يُغيِّر ⁨Enterprise⁩ سلوك ⁨Webview⁩. ويضيف ⁨Enterprise⁩ ميزات مطابقة وأرشفة أعلى طبقةً موثَّقة بصورة منفصلة؛ وهي ليست مطلوبة لتقديم ملفّ ⁨PDF⁩ مُخطَّط خطّيًا عبر نطاقات بايتات.

توثِّق هذه الصفحة السلوكَ القابل للرصد خارجيًا وسطحَ واجهة برمجة التطبيقات العامة المدعوم فقط. أمّا مسارات النطاقات الاسمية الداخلية، والأصناف المساعِدة، وجداول الآليات، وأسماء ملفّات كتيّبات التشغيل، وبادئات التذاكر فخارجة عن النطاق.