تضمين الصور داخل مستند
لمحة سريعة
قسم بعنوان «لمحة سريعة»ضع صورة نقطية في موضع مطلق وبعرض صريح. احذف الارتفاع، فيستنتجه NextPDF من نسبة العرض إلى الارتفاع في المصدر. يتّبع هذا المثال examples/07-images.php. يمكنك استخدام ملفات JPEG وPNG وGIF وBMP وWebP وAVIF.
يضمّن NextPDF الصورة بوصفها كائن XObject للصورة وفق ISO 32000-2. ويحدّد قاموس الصورة العرض والارتفاع وعدد البتات لكل مكوّن بصورة صريحة.
التثبيت
قسم بعنوان «التثبيت»composer require nextpdf/core:^3واجهة image() البرمجية جزء من Core. ولا تحتاج إلى أي شيء آخر لتضمين ملف موجود. يولّد المثال المرافق صورة اختبارية باستخدام GD، لذا يتطلّب ext-gd. ظلّت الواجهة البرمجية مستقرة منذ 1.0.0 وتعمل على مصفوفة النقل الخلفي 8.1–8.4.
نظرة عامة مفاهيمية
قسم بعنوان «نظرة عامة مفاهيمية»تُحمِّل image($file, x, y, width, height) الملف عبر سجلّ الصور، وتفكّ ترميزه، ثم تضعه. يتحقّق السجلّ من صحة المسار قبل فكّ الترميز، ويرفض أي مسار يحتوي على بايت NUL أو يبدو كمخطّط مُحدِّد موارد موحَّد (URL) (scheme://…). تقرأ image() الملفات المحلية فقط، ولا تقرأ أبدًا عنوان URL بعيدًا. ويرفض السجلّ كذلك أي قيمة غير موجبة لـ width أو height.
يستخدم الوضع مصفوفة التحويل الحالية لتعيين مربّع الوحدة الخاص بالصورة داخل المستطيل الهدف. يحدّد ISO 32000-2 §8.8 المُعامِل cm داخل زوج q/Q لهذا الوضع، بحيث يبقى التحويل معزولًا. تصبح الصورة النقطية بعد فكّ ترميزها كائن XObject صورة. قيمة BitsPerComponent مطلوبة ويجب أن تكون إحدى القيم 1 أو 2 أو 4 أو 8 أو 16 (§8.9.5).
سطح الواجهة البرمجية
قسم بعنوان «سطح الواجهة البرمجية»يُولَّد سطح الواجهة البرمجية من PHPDoc. ويستخدم هذا المثال طريقة واحدة:
image(string $file, ?float $x = null, ?float $y = null, ?float $width = null, ?float $height = null): static— تضمّن صورة نقطية محلية وتضعها. احذفheightللحفاظ على نسبة العرض إلى الارتفاع في المصدر. وترفض مسارات مخطّطات URL ومسارات بايت NUL والأبعاد غير الموجبة مع إطلاقPageLayoutException.
نموذج الشيفرة — البدء السريع
قسم بعنوان «نموذج الشيفرة — البدء السريع»<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
$doc = Document::createStandalone();$doc->addPage();
// Absolute position; height inferred from the source aspect ratio.$doc->image(__DIR__ . '/logo.png', x: 15, y: 30, width: 80);
$doc->save(getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/images.pdf');نموذج الشيفرة — الإنتاج
قسم بعنوان «نموذج الشيفرة — الإنتاج»يولّد هذا المثال الكامل والجاهز للتشغيل ضمن منظومة الاختبار صورة اختبارية حتمية باستخدام GD، ليبقى المثال مكتفيًا ذاتيًا. ويراعي NEXTPDF_COOKBOOK_OUTPUT ولا يثبّت أي عشوائية خاصة به.
<?php
declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use NextPDF\Core\Document;
if (!\extension_loaded('gd')) { fwrite(STDERR, "ext-gd required for the self-contained test image\n"); exit(1);}
// Build a deterministic test image (fixed pixels — no entropy).$imgPath = \tempnam(\sys_get_temp_dir(), 'npf') . '.png';$img = \imagecreatetruecolor(200, 100);if ($img === false) { fwrite(STDERR, "GD imagecreatetruecolor failed\n"); exit(1);}$bg = (int) \imagecolorallocate($img, 30, 58, 138);$fg = (int) \imagecolorallocate($img, 255, 255, 255);\imagefilledrectangle($img, 0, 0, 199, 99, $bg);\imagestring($img, 5, 40, 40, 'NextPDF Image', $fg);\imagepng($img, $imgPath);\imagedestroy($img);
try { $doc = Document::createStandalone(); $doc->setTitle('Image Embedding'); $doc->addPage();
$doc->setFont('helvetica', 'B', 18); $doc->cell(0, 12, 'Image Embedding', newLine: true); $doc->ln(5);
// Absolute placement; width fixed, height from aspect ratio. $doc->image($imgPath, x: 15, y: 50, width: 80); // Same image, narrower — scaled down by the CTM, not re-decoded. $doc->image($imgPath, x: 15, y: 105, width: 40);
$out = getenv('NEXTPDF_COOKBOOK_OUTPUT') ?: __DIR__ . '/images.pdf'; $doc->save($out); echo "Created images.pdf\n";} finally { @\unlink($imgPath);}الحالات الحدّية والمزالق
قسم بعنوان «الحالات الحدّية والمزالق»- لا توجد عناوين URL بعيدة. يُرفض أي مسار يطابق
scheme://…مع إطلاقPageLayoutException. نزّل الأصل إلى ملف محلي أولًا، ثم مرّر ذلك المسار. لا يجلب المُحمِّل أي شيء عبر الشبكة نيابةً عنك. هذه حماية متعمَّدة ضد تزوير الطلب من جانب الخادم (SSRF). - الأبعاد غير الموجبة تُطلِق استثناءً. تؤدّي قيمة
width <= 0أوheight <= 0إلى إطلاقPageLayoutException. لاستخدام الحجم الطبيعي، احذف الوسيطة بدلًا من تمرير0. - رفض المسار الذي يحتوي على بايت NUL. يُرفض أي مسار يحتوي على
\0. وهذا يقي من مسارات التسميم بالبايت الصفري. تحقّق من صحة أسماء الملفات المقدَّمة من المستخدم. - نسبة العرض إلى الارتفاع. مرّر
widthوحده، أوheightوحده، للتحجيم المتناسب. قد يؤدّي تمرير كلتا القيمتين إلى تشويه الصورة. - الملف نفسه مُضمَّن مرّتين. أعد استخدام مسار واحد للتحجيم عبر مصفوفة التحويل. لا يعيد المحرّك فكّ ترميز البايتات المتطابقة. وفضّل إعادة استخدام المسار على تكرار الملفات.
الأداء
قسم بعنوان «الأداء»تتناسب تكلفة فكّ الترميز مع عدد بكسلات المصدر، لذا تكفي ميزانية 96 MB للسماح بصورة فوتوغرافية متوسطة. وضع المسار نفسه مرّة أخرى تكلفته منخفضة: مُعامِل Do إضافي واحد مع cm، وليس فكّ ترميز ثانٍ. لا يقلّل التصغير بحسب العرض من البكسلات المخزَّنة. أعد ضبط حجم صور المصدر الكبيرة مسبقًا إذا كان حجم المستند مهمًّا.
ملاحظات أمنية
قسم بعنوان «ملاحظات أمنية»يقي رفض مخطّط URL وبايت NUL من SSRF وحقن المسارات. لا يمكن خداع image() لجلب http://… أو التجاوز إلى ما بعد البايت الصفري. ومع ذلك، عامِل أسماء الملفات المقدَّمة من المستخدم باعتبارها غير موثوقة. حُلّها بالنسبة إلى دليل أساس مسموح به قبل استدعاء image(). عندما يكون الملف مرفوعًا من المستخدم، يجري فكّ ترميز الصورة من بايتات يتحكّم فيها المهاجم. ضع سقفًا لحجم المُدخَل وفكّر في فكّ الترميز ضمن عامل معزول.
المطابقة
قسم بعنوان «المطابقة»| البيان | المواصفة | البند | reference_id |
|---|---|---|---|
| يحدّد قاموس الصورة العرض والارتفاع وعدد البتات لكل مكوّن بصورة صريحة. | ISO 32000-2 | §8.9.5 | |
BitsPerComponent مطلوبة وقيمتها 1 أو 2 أو 4 أو 8 أو 16. | ISO 32000-2 | §8.9.5 | |
تُوضَع الصورة بواسطة المُعامِل cm داخل q/Q. | ISO 32000-2 | §8.8 |
ملف قابلية إعادة الإنتاج — بنيوي. بايتات الصورة حتمية لمصدر ثابت. يحمل كل مستند محفوظ /ID في المقطورة وذرّات تاريخ، لذا تجرّدها منظومة الاختبار وتقارن البنية المُسوّاة بـ qpdf. يصف هذا المثال كيفية إنتاج NextPDF للبنية، ولا يقدّم ادعاء مطابقة شاملًا لـ ISO 32000-2.
السياق التجاري
قسم بعنوان «السياق التجاري»لا ينطبق. تضمين الصور النقطية قدرة في Core، دون بوابة Premium.