تعبئ PDFlibPas (losLab PDF Library) قوالب كتل PDF من Delphi: الكتلة مستطيل مسمى على صفحة قالب، نوعه نص أو صورة أو PDF، وترسم FillTextBlock و FillImageBlock و FillPDFBlock البيانات المتغيرة داخله. وتوجد الكتل بوصفها تعليقات صفحة من نوع PDFlibBlock:Text و PDFlibBlock:Image و PDFlibBlock:PDF، أو في قاموس page-piece تحت /PieceInfo
حالة الاستخدام هي النمط الممل عالي الحجم. يرسم المصمم شهادة أو تذكرة فعالية أو خطاب تحصيل مرة واحدة، ويختم البرنامج اسماً أو رقم مقعد أو صورة توقيع أو صفحة باركود معروضة مسبقاً في خانات ثابتة لعشرة آلاف سجل. تستطيع حقول النماذج إنجاز جزء من ذلك، لكن حقل AcroForm كائن تفاعلي له تدفق مظهر خاص وقواعده الخاصة، وغالباً ما تنتهي إلى تسطيحه على أي حال. الكتلة أبسط: مستطيل مع حفنة تلميحات تنسيق، تُستهلك مرة ثم تُرمى
ما هي كتلة PDF في PDFlibPas بالضبط؟
كتلة PDFlibPas تعليق صفحة يكون /Subtype فيه PDFlibBlock:Text أو PDFlibBlock:Image أو PDFlibBlock:PDF، ويكون /Rect فيه منطقة العنصر النائب، وتحمل سلسلة /Contents فيه name;key=value;key=value. المقطع الأول هو اسم الكتلة، وكل ما بعده قائمة خصائص. تكتب TPDFlib.AddBlock التعليق مع /F 4 (Print) ودون تدفق مظهر، فالكتلة غير المعبأة لا تشغل أي بكسل على الشاشة أو الورق
لجانب القراءة مصدر ثانٍ. بعد تعداد تعليقات الكتل، تلجأ PDFlibPas إلى مسار قاموس الصفحة /PieceInfo << /PDFlib << /Blocks ... >> >> وصيغته /PieceInfo << /PDFlib << /Private << /Blocks ... >> >> >>. قواميس page-piece هي PDF قياسية، معرفة في ISO 32000-1 §14.5 بوصفها المكان الذي يخزن فيه التطبيق بياناته الخاصة المرتبطة بصفحة، وهناك عادة تعريفات الكتل القادمة من أدوات تأليف القوالب الخارجية. وقد يكون حاوي /Blocks قاموساً مفهرساً باسم الكتلة أو مصفوفة عادية؛ وفي صيغة المصفوفة تسمي PDFlibPas المدخلات Block1 و Block2 وهكذا ما لم تحمل الكتلة /Name خاصاً بها
داخل كتلة page-piece، القيمة Image للـ /Subtype (أو /Type) تقابلها كتلة صورة، و PDF أو Page تقابلها كتلة PDF، وأي شيء آخر يعامل كنص. وكل مفتاح آخر عدا /Type و /Subtype و /Name و /Rect و /Custom يصبح خاصية، وتُسطّح مفاتيح قاموس /Custom المتداخل هي الأخرى، فمدخل /Custom << /department (Finance) >> يقرأ عبر GetBlockProperty(Page, Index, 'department'). ترقيم الكتل يبدأ من 1 في كل صفحة، كتل التعليقات أولاً ثم كتل الـ page-piece بعدها
تأليف قالب كتل بواسطة AddBlock
تضيف TPDFlib.AddBlock(BlockType, BlockName, Left, Top, Width, Height, Properties) كتلة إلى الصفحة المحددة حالياً وتعيد 1 عند النجاح. وتعيد 0 لاسم فارغ أو عرض أو ارتفاع صفر أو سالب أو BlockType غير 'text' أو 'image' أو 'pdf' (دون حساسية لحالة الأحرف). وتلحق Properties بالاسم بفاصلة منقوطة، فتستخدم صيغة key=value;key=value نفسها المخزنة في السلسلة
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
// رسومات ثابتة: إطار وشعار وصياغة مثبتة ...
Lib.DrawText(72, 200, 'This certifies that');
Lib.AddBlock('text', 'recipient', 72, 230, 450, 44,
'fontname=Times-Bold;fontsize=30;fillcolor={cmyk 0 0.55 1 0.1};alignment=center');
Lib.AddBlock('text', 'course', 72, 290, 450, 24,
'fontname=Helvetica;fontsize=14;alignment=center;fitmethod=word');
Lib.AddBlock('image', 'signature', 360, 600, 160, 60, '');
Lib.AddBlock('pdf', 'seal', 72, 580, 100, 100, '');
if Lib.GetBlockCount(1) <> 4 then
raise Exception.Create('Template blocks missing');
Lib.SaveToFile('certificate-template.pdf');
finally
Lib.Free;
end;
end;
محلل الخصائص متسامح عن قصد. تطابق المفاتيح دون حساسية لحالة الأحرف، وتقلّم القيم لكنها تعود كما هي حرفياً (القيمة المكتوبة {top left} تعود بأقواسها)، والمفتاح المفقود يعيد سلسلة فارغة. الحد الصارم الوحيد هو الفاصل: لا يمكن أن تتضمن القيمة فاصلة منقوطة، لأن الفاصلة المنقوطة تنهي المقطع
كيف تطبق FillTextBlock الخط واللون والمحاذاة؟
تقرأ TPDFlib.FillTextBlock(Page, BlockIndex, Text, Options) خصائص الكتلة وتطبقها على حالة الرسم، وتلائم النص في مستطيل الكتلة بواسطة FitTextBox، ثم تعيد خط المستدعي وحجم النص ولون النص والمحاذاة داخل كتلة finally. تلك الإعادة مهمة في حلقة دمج: كتلة عنوان حمراء بمقاس 30 pt لا يمكن أن تسرّب أسلوبها إلى استدعاء DrawText التالي تجريه على الصفحة نفسها
fontname (أوfont): يعيد أولاً استخدام خط محمّل مسبقاً في المستند بهذا الاسم، ثم يربط أسماء Standard 14 مثلHelvetica-BoldوTimes-RomanوCourier، وأخيراً يجربAddTrueTypeFontبالاسمfontsize (أوsize): حجم النص الممرر للملائم، الذي يصغّره إن لم يتسع النصfillcolor (أوtextcolor أوcolor):#RRGGBBأو{rgb r g b}أو{cmyk c m y k}أو{gray g}أو قوائم أرقام مجردة من 3 أو 4 عناصر، أو الأسماء black و white و red و green و blue و gray؛ ومكونات RGB فوق 1 تقرأ على أساس 0–255- تقرأ
alignmentوalignوpositionمعاً: left و right و center (أو centre) و justify تضبط المحاذاة الأفقية، و top و bottom تضبط الموضع الرأسي، والافتراضي هو التمركز الرأسي fitmethod:wordأوnofitsplitيمنع كسر الكلمات الطويلة، وscaleأوexpandيسمح للحجم أن يكبر لملء الصندوق
مسار CMYK هو سبب اهتمام مطابع الطباعة. القيمة {cmyk 0 0.55 1 0.1} تستدعي SetTextColorCMYK، فيُبث النص المتغير بصيغة DeviceCMYK ويصيب لون العلامة على المطبعة دون رحلة ذهاب وعودة عبر RGB. تعيد FillTextBlock القيمة 1 عندما يرسم النص؛ وتعيد 0 لنص Text فارغ أو كتلة مفقودة أو كتلة ليست كتلة نص، وفي كل تلك الحالات تبقى الكتلة في مكانها
تعبئة كتل الصور و PDF، وبتات Options
ترسم FillImageBlock(Page, BlockIndex, ImageID, Options) صورة أُضيفت سابقاً، وتستورد FillPDFBlock(Page, BlockIndex, SourceDocumentID, SourcePage, Options) صفحة من مستند آخر محمّل بوصفها Form XObject وترسمها، وهو ما يبقي المحتوى المتجهي كالختم أو صفحة رمز QR أو ترويسة الرسائل متجهياً بالكامل. وكلتاهما تحفظ نسبة أبعاد المصدر وتمركز النتيجة داخل مستطيل الكتلة. تتشارك دوال التعبئة الثلاث بتات Options نفسها: البت 1 (القيمة 1) يزيل الكتلة بعد تعبئة ناجحة، إما بحذف التعليق أو بإزالة المدخل من حاوي /Blocks، والبت 2 (القيمة 2) يمدد محتوى الصورة و PDF إلى المستطيل تماماً بدل ملاءمته
function FillCertificate(const TemplateFile, SealFile, SignatureFile,
OutFile: WideString; Values: TStrings): Boolean;
var
Lib: TPDFlib;
SealID, SignatureID, I: Integer;
BlockName, BlockType: WideString;
begin
Result := False;
Lib := TPDFlib.Create;
try
if Lib.LoadFromFile(SealFile, '') <> 1 then Exit;
SealID := Lib.SelectedDocument; // معرف المستند الحقيقي
if Lib.LoadFromFile(TemplateFile, '') <> 1 then Exit;
// القالب هو الآن المستند المحدد
SignatureID := Lib.AddImageFromFile(SignatureFile, 0);
// امش إلى الخلف: البت 1 يزيل كل كتلة ويعيد ترقيم ما بعدها
for I := Lib.GetBlockCount(1) downto 1 do
begin
BlockName := Lib.GetBlockName(1, I);
BlockType := Lib.GetBlockType(1, I);
if BlockType = 'text' then
Lib.FillTextBlock(1, I, Values.Values[BlockName], 1)
else if (BlockType = 'image') and (SignatureID <> 0) then
Lib.FillImageBlock(1, I, SignatureID, 1)
else if BlockType = 'pdf' then
Lib.FillPDFBlock(1, I, SealID, 1, 1);
end;
Result := Lib.SaveToFile(OutFile) = 1;
finally
Lib.Free;
end;
end;
تفصيلان في تلك الحلقة يسهل الخطأ فيهما. تدور الحلقة من أعلى فهرس نزولاً لأن إزالة الكتلة 2 تحول الكتلة 3 القديمة إلى كتلة 2؛ والعد صعوداً والبت 1 مفعّل يتخطى كل كتلة متبادلة. ويحمَّل المستند المصدر الخاص بـ FillPDFBlock أولاً ويؤخذ معرفه قبل تحميل القالب، لأن معرفات الصور تخص المستند المحدد، فيجب أن يجري AddImageFromFile والقالب هو المحدد. أما للتشغيل الدفعي الذي يتغير فيه الختم أو الترويسة بحسب السجل، فتستخدم خياطة صفحات Form XObject وقوالب العلامات المائية القابلة للتركيب في PDFlibPas الآلية نفسها من الالتقاط والرسم على صفحات كاملة
لماذا تخرج إحداثيات الكتل معكوسة؟
تخرج مستطيلات الكتل معكوسة عندما يمزج الكود بين نظامي الإحداثيات اللذين يتعامل معهما PDFlibPas. فالتعليق /Rect يخزن في فضاء مستخدم PDF ويقاس من أسفل الصفحة (ISO 32000-1 §12.5.2)، بينما تقيس واجهة الرسم في TPDFlib من الأعلى. تأخذ AddBlock مدخلات مبنية على الأعلى وتخزن الحافة السفلى PageHeight - Top - Height؛ وتقرأ GetBlockRect ودوال التعبئة الثلاث المستطيل مجدداً وتحوله بـ PageHeight - URy
عملياً ذلك يعني: استخدم GetBlockRect(Page, BlockIndex, Left, Top, Width, Height) عندما تحتاج هندسة الخانة، كرسم إطار تنقيح أو تموضع تسمية توضيحية بجوار كتلة، ولا تقرأ /Rect عبر استدعاء تعليق عام لتدفعها مباشرة إلى DrawText. كتلة page-piece ذات /Rect [40 700 240 730] على صفحة letter بمقاس 792 pt تبلّغ Top = 62 لا 700. والخطأ الذي يمنعه هو الخطأ الكلاسيكي الذي تهبط فيه كل قيمة معبأة معكوسة قرب الهامش السفلي دون أي رسالة خطأ
مزالق تستحق التعلم منها
أكثر خطأ مفيد في كود الكتل كان زلة حرف واحد. تُميز تعليقات الكتل باختبار بادئة على الـ subtype، و 'PDFlibBlock:' طوله 12 محرفاً. قارنت نسخة مبكرة Copy(Subtype, 1, 13) بتلك السلسلة الحرفية ذات 12 محرفاً، وهو ما لا يمكن أن يتساوى أبداً، فأعادت GetBlockCount القيمة 0 لكل قالب وبلغت كل تعبئة عن "no such block". لم ينهار شيء ولم يُسجل شيء. والدرس يُعمم على أي محرك قوالب: أثبت عدد كتل قالب معروف في اختبار، لأن التعداد الصامت ذا المطابقة الصفرية يبدو تماماً كصفحة فارغة
الفخ الثاني هو معرفات المستندات. تعيد TPDFlib.LoadFromFile رمز حالة، 1 للنجاح و0 للفشل، لا معرف المستند الذي حمّلته للتو. تمرير تلك الـ1 بوصفها SourceDocumentID إلى FillPDFBlock يفشل لأن 1 ليست معرف مستند صالحاً. استدعِ SelectedDocument مباشرة بعد تحميل ناجح لتحصل على المعرف الحقيقي، و SelectDocument للعودة إلى القالب إن حمّلت المصدر بعده
وأصغر منها: FillTextBlock بسلسلة فارغة لا تفعل شيئاً وتبقي الكتلة. في دمج المراسلات ذي الحقول الاختيارية، كسطر عنوان ثانٍ مفقود، هذا عادة ما تريده، لكن التعليق غير المعبأ يبقى عندئذ في ملف المخرجات. أزله صراحة بـ DeleteAnnotation أو املأ مسافة واحدة مع البت 1 إن كان لا بد أن تكون المخرجات نظيفة. وكل استدعاءات الكتل تحفظ الصفحة المحددة وتعيدها، فتعبئة في صفحة 3 لا تغير وجهة استدعاء الرسم التالي
ما هي حدود التعامل البيني لتعليقات الكتل؟
تعليقات الكتل اصطلاح خاص لا ميزة قياسية من PDF. لا PDFlibBlock:Text ولا أشقاؤه ضمن أنواع التعليقات المدرجة في ISO 32000-1 §12.5.6.1، فتعاملها العارضات بوصفها تعليقات مجهولة. ولأن علم Invisible غير مسرح ولا يوجد تدفق مظهر، لا ترسم لها العارضات المطابقة شيئاً، لكن بعض العارضات قد تسردها في لوحة التعليقات مع ذلك، وأدوات PDF الأخرى لن تفهم دلالتها ولن تحفظها
- املأ والبت 1 مفعّل لأي شيء يغادر نظامك، حتى لا يتضمن الملف النهائي سوى محتوى صفحة عادي
- لا يسمح PDF/A بأنواع تعليقات خارج مواصفة PDF (ISO 19005-1 §6.5.2)، فمخرجات الأرشفة يجب ألا تحمل تعليقات كتل غير معبأة
- كتل الـ page-piece أأمن إبقاءها في قالب، لأن
/PieceInfoقياسية والمستهلكون يتجاهلون البيانات التي لا يملكونها - الكتل ليست حقول نماذج: لا تفاعلية ولا قيمة حقل في الملف ولا تصدير إلى FDF أو XFDF؛ ولبيانات يجب أن تبقى قابلة للتحرير انظر تبادل بيانات النماذج FDF و XFDF و XFA في PDFlibPas
إن كان على القالب أن يعلن حقوله المتغيرة بطريقة قياسية قابلة للفحص من المدقق في مسار طباعة، فالكتل هي الأداة الخطأ. ذاك هو دور PDF/VCR-1، وتغطي بيان العناصر النائبة لقوالب PDF/VCR-1 في PDFlibPas كيف تعيش تلك العناصر النائبة في شجرة البنية بدل التعليقات
FAQ
هل تعبئ PDFlibPas قوالب كتل أنشأتها أدوات أخرى؟
نعم، عندما يخزن القالبُ الكتل تحت /PieceInfo /PDFlib /Blocks أو /PieceInfo /PDFlib /Private /Blocks، بصيغة قاموس أو مصفوفة، ومع /Rect من أربعة أرقام. تعدّ GetBlockCount تلك بعد أي تعليقات PDFlibBlock:*، وتكشف المفاتيح المخصصة عبر GetBlockProperty
هل تغير FillTextBlock الخط الذي حددته قبل الاستدعاء؟
لا. تحفظ FillTextBlock الخط المحدد وحجم النص ولون النص والمحاذاة، وتطبق خصائص الكتلة fontname و fontsize و fillcolor و alignment، ثم تعيد الحالة الأصلية بعد ذلك حتى إذا فشلت الملاءمة
كيف أضع شعاراً متجهياً أو صفحة باركود داخل كتلة؟
حمّل ملف PDF المصدر، وخزّن SelectedDocument بوصفه معرفه، وحدد مستند القالب مجدداً إن لزم، واستدعِ FillPDFBlock(Page, BlockIndex, SourceID, SourcePage, 1). تستورد الصفحة بوصفها Form XObject، فتبقى متجهية وتلائم الكتلة مع حفظ نسبة أبعادها ما لم تضف البت 2
قوالب الكتل واستيراد صفحات Form XObject وبقية أدوات البيانات المتغيرة متوفرة في PDFlibPas، مكتبة losLab لملفات PDF داخل Delphi، التي تغطي إنشاء PDF وتحريره والإنتاج الطباعي لمشاريع Delphi و C++Builder