ينشئ مكون PDFium تعليقات ترميز النصوص، مثل التمييز (highlight)، والتسطير (underline)، والتشطيب (strikeout)، والتعرج (squiggly)، من خلال TPdf.CreateAnnotation: حيث تقوم بضبط HasAttachmentPoints := True في سجل TPdfAnnotation وملء نقاط AttachmentPoints الرباعية الخاصة به، ويكتب المكون إدخال QuadPoints المحدد في معيار ISO 32000-1 §12.5.6.10. وهذه هي واجهة برمجة التطبيقات بالكامل. والسبب في وجود هذا المقال هو ما يحدث تحته، لأن سلسلة استدعاءات PDFium الخام بها وضع فشل ينتج عنه العرض الأقل فائدة في مجموعة الأدوات: ترجع FPDFAnnot_SetAttachmentPoints القيمة false على تعليق تم إنشاؤه حديثًا، في كل مرة، وبدون رمز خطأ أو تلميح. وهذا هو المقال المصاحب لجانب الإنشاء لـ مقالنا حول قراءة ومراجعة التعليقات الحالية، والذي يسير في الاتجاه الآخر عبر نفس الهياكل
مشهد تصحيح الأخطاء هو نفسه دائمًا. تقوم بإنشاء تعليق تمييز، وتستدعي واضع نقاط الارتباط بالفهرس 0، وترجع الدالة القيمة false، وتبدأ في التشكيك في إحداثياتك. وتقوم بنقل النقاط، وقلب محور Y، وتبديل مساحة الصفحة بمساحة الجهاز. ولا شيء من ذلك يساعد، لأن الإحداثيات لم تكن هي المشكلة أبدًا. بل تكمن المشكلة في دلالات الفهرس لواجهة برمجة تطبيقات C، وبمجرد رؤيتها، يكون الإصلاح في سطرين
ماذا تعني QuadPoints في معيار ISO 32000-1؟
نقاط QuadPoints هي مصفوفة من أرقام 8×n تصف n من الأشكال الرباعية، ويتطلبها معيار ISO 32000-1 §12.5.6.10 في كل تعليق لترميز النص: يحدد كل شكل رباعي كلمة أو مجموعة من الكلمات المتجاورة التي ينطبق عليها التمييز أو التسطير أو التشطيب. ولا يزال إدخال Rect الخاص بالتعليق موجودًا، ولكنه بالنسبة للأنواع الفرعية للترميز يحد المنطقة فقط؛ والأشكال الرباعية هي ما يرسمه المصير (renderer) فعليًا. والشكل الرباعي بدلاً من المستطيل لأن النص يمكن تدويره أو قصه، لذا يتم تخزين الزوايا الأربع كأربع نقاط مستقلة: x1 y1 x2 y2 x3 y3 x4 y4
ترتيب هذه النقاط الأربع هو المكان الذي تتباعد فيه المواصفات والتطبيقات العملية. فالمواصفات تصف النقاط بأنها تتتبع الشكل الرباعي عكس اتجاه عقارب الساعة، ولكن مصير أدوبي الخاص كان يفسرها دائمًا بنمط Z بدلاً من ذلك: أولاً الحافة العلوية من اليسار إلى اليمين، ثم الحافة السفلية من اليسار إلى اليمين. ونظرًا لأن كل مؤلف تم اختباره ضد Acrobat، فإن كل مصير تقريبًا، بما في ذلك PDFium، يتبع نمط Z، والملفات التي تتبع الصياغة الحرفية للمواصفات تظهر كتمييز منهار أو ملتوٍ في بعض برامج العرض. وهيكل FS_QUADPOINTSF لـ PDFium يرمز بالضبط لهذه الاتفاقية: (x1,y1) هي الزاوية العلوية اليسرى، و (x2,y2) العلوية اليمنى، و (x3,y3) السفلية اليسرى، و (x4,y4) السفلية اليمنى، في إحداثيات الصفحة حيث ينمو Y لأعلى. اتبع هذا الترتيب؛ فالمصيرات متساهلة بشأن أشياء كثيرة، لكن الشكل الرباعي المشوش ليس أحدها
لماذا ترجع FPDFAnnot_SetAttachmentPoints القيمة false؟
تفشل FPDFAnnot_SetAttachmentPoints في التعليقات الجديدة لأن عقدها هو استبدال الشكل الرباعي عند فهرس معين، والتعليق المنشأ حديثًا يحتوي على صفر من الأشكال الرباعية لاستبدالها. ويأخذ التوقيع مقبض التعليق، و quad_index، والنقاط؛ والفهرس 0 لا يعني "الفتحة الأولى، وإنشائها إذا لزم الأمر"، بل يعني "الشكل الرباعي الحالي رقم 0"، وعندما تبلغ FPDFAnnot_CountAttachmentPoints عن القيمة 0، فلا يوجد مثل هذا الشكل الرباعي ويرجع الاستدعاء false. والدالة التي تنشئ فتحة هي FPDFAnnot_AppendAttachmentPoints. ويبدأ كل تعليق يتم إنشاؤه عبر FPDFPage_CreateAnnot بعدد صفر، لذا يجب أن يستدعي مسار الإنشاء Append أولاً، ولا يجوز إلا للتحديثات اللاحقة استدعاء Set
// Inside the component's annotation writer (v1.79.1+):
// a new annotation has no quad slots yet, so Append creates
// the first one; Set only replaces a slot that already exists
if FPDFAnnot_CountAttachmentPoints(Annotation) = 0 then
Check(FPDFAnnot_AppendAttachmentPoints(Annotation, QuadPoints) <> 0,
'Cannot set attachment points')
else
Check(FPDFAnnot_SetAttachmentPoints(Annotation, 0, QuadPoints) <> 0,
'Cannot set attachment points');
النمط نفسه ينطبق إذا كنت تستدعي دوال C المصدرة مباشرة، وهو ما يتيح لك المكون القيام به لأن جميع نقاط الدخول FPDFAnnot_* تظهر في PDFium.pas. وكلما كنت تحمل مقبض FPDF_ANNOTATION وتريد كتابة أشكال رباعية، اسأل FPDFAnnot_CountAttachmentPoints أولاً ووجه استدعاءاتك وفقًا لذلك. وإذا كنت تبحث عن "ترجع FPDFAnnot_SetAttachmentPoints القيمة false"، فإن تفريع count-then-append هذا هو إجابتك بالتأكيد
إنشاء تمييز باستخدام TPdf.CreateAnnotation
مع قيام المكون بتوجيه Append-versus-Set نيابة عنك، يقتصر إنشاء التمييز على ملء سجل. وينشئ المثال أدناه صفحة A4 ويضع تمييزًا أصفر شبه شفاف فوق منطقة 200×20 نقطة؛ ولاحظ أن الشكل الرباعي يتبع ترتيب Z الموصوف أعلاه، وأن Rectangle تم ضبطه لتطويق الشكل الرباعي، مما يحافظ على سلوك منطقي لبرامج العرض التي تجري اختبار الضرب (hit-test) ضد Rect
var
Pdf: TPdf;
A: TPdfAnnotation;
begin
Pdf := TPdf.Create(nil);
try
Pdf.CreateDocument;
Pdf.AddPage(0, 595, 842);
FillChar(A, SizeOf(A), 0);
A.Subtype := anHighlight;
A.HasColor := True;
A.Color := clYellow;
A.ColorAlpha := $80; // 50% opacity
A.HasAttachmentPoints := True;
A.AttachmentPoints[1].X := 50; A.AttachmentPoints[1].Y := 700; // top-left
A.AttachmentPoints[2].X := 250; A.AttachmentPoints[2].Y := 700; // top-right
A.AttachmentPoints[3].X := 50; A.AttachmentPoints[3].Y := 680; // bottom-left
A.AttachmentPoints[4].X := 250; A.AttachmentPoints[4].Y := 680; // bottom-right
A.Rectangle.Left := 50; A.Rectangle.Top := 700;
A.Rectangle.Right := 250; A.Rectangle.Bottom := 680;
A.ContentsText := 'Highlighted region';
Pdf.CreateAnnotation(A);
Pdf.SaveAs('highlighted.pdf');
finally
Pdf.Free;
end;
end;
تبديل الأنواع الفرعية يكلف سطرًا واحدًا. فتأخذ anUnderline، و anStrikeout، و anSquiggly نفس شكل السجل، والأشكال الرباعية وكل شيء، لأن معيار ISO 32000-1 يعامل الأربعة كعائلة تعليقات واحدة تتميز فقط بكيفية تزيين منطقة الشكل الرباعي. والأنواع الفرعية التي ليست ترميزًا للنص، مثل anSquare، و anCircle، و anText، تحدد موضعها من Rectangle وحده؛ اترك HasAttachmentPoints عند القيمة False لهذه الأنواع، ولن تعمل آلية الأشكال الرباعية أبدًا
لماذا يترجم AttachmentPoints[0] في دلفي ولكنه يفشل في FPC؟
يتم الإعلان عن TQuadrilateralPoint كـ array [1..4] of TPdfPoint، وهي مصفوفة تبدأ من 1، وهذا يعيق أي شخص تتدرب أصابعه افتراضيًا على الفهرسة الصفرية. وإذا كتبت A.AttachmentPoints[0]، فسيقوم dcc32 الخاص بدلفي بترجمته دون شكوى، لأن فحص النطاق معطل افتراضيًا؛ وفي وقت التشغيل يقرأ التعبير أو يكتب بصمت في الذاكرة الموجودة قبل المصفوفة مباشرة، وهو في سجل TPdfAnnotation حقل مجاور. ويحصل التمييز الخاص بك على زاوية تالفة واحدة، أو يتلف حقل مجاور، ولا يثار أي شيء. وقد اصطاد برنامج Free Pascal هذا الخطأ بالضبط في مصادر العرض التوضيحي الخاصة بنا أثناء منفذ Lazarus: حيث يقوم fpc بإجراء فحص نطاق في وقت الترجمة على الفهارس الثابتة ورفض AttachmentPoints[0..3] تمامًا، وهو ما كشف عن خطأ الإزاحة بواحد وخطأ مكتبة Set-versus-Append معًا
يتبع ذلك عادتان. فهرسة الشكل الرباعي من 1 إلى 4، بمطابقة ترتيب الزوايا في الكود أعلاه، وبناء كود التعليق التوضيحي الخاص بك مرة واحدة على الأقل مع تمكين فحص النطاق، إما {$R+} في دلفي أو أي بناء fpc، قبل الوثوق به. فمرور بناء dcc32 الافتراضي ليس دليلاً على صحة الفهارس؛ بل هو دليل فقط على عدم حدوث انهيار في الذاكرة التي صدف وجودها هناك
الحصول على إحداثيات الشكل الرباعي من نص حقيقي
المستطيلات المحددة بشكل ثابت جيدة للعروض التوضيحية، لكن التمييزات الفعلية تتتبع الحروف الرسومية (glyphs) الحقيقية، ويجب أن تأتي الإحداثيات من هندسة صفحة نص PDFium بدلاً من التخمين. وتمنحك البرامج الفرعية التي يغطيها دليلنا لاستخراج النصوص باستخدام مكون PDFium مربعات إحاطة لكل حرف في نفس مساحة إحداثيات الصفحة التي تستخدمها الأشكال الرباعية، بحيث تتحول نتيجة البحث مباشرة إلى نقاط زاوية: يسار الحرف الأول، ويمين الأخير، والأعلى والأسفل من امتدادات الخط. وإذا كنت تقوم بإنشاء النص بنفسك وبحاجة إلى معرفة أين ستقع الخطوط قبل وجودها، فإن مقال قياس النص والتفاف الكلمات يغطي حساب تلك الامتدادات مقدمًا
حد صادق واحد: يحمل سجل TPdfAnnotation نقطة TQuadrilateralPoint واحدة، لذا فإن استدعاء CreateAnnotation واحد يكتب شكلاً رباعيًا واحدًا. ويحتاج التحديد الذي يمتد لثلاثة أسطر إلى ثلاثة أشكال رباعية، واحد لكل سطر، وفقًا للفقرة §12.5.6.10، ولديك طريقتان للوصول إلى ذلك. الطريقة البسيطة هي تعليق واحد لكل سطر، والذي يظهر بشكل صحيح في كل مكان ويحافظ على واجهة برمجة تطبيقات المكون. والطريقة المدمجة، تعليق واحد يحمل ثلاثة أشكال رباعية، تعني إنشاء التعليق من خلال المكون ثم استدعاء FPDFAnnot_AppendAttachmentPoints المصدرة بنفسك للشكلين الرباعيين الثاني والثالث، وهو ما يعمل بدقة لأن Append ينشئ فتحات بدلاً من استبدالها. ولا تحاول الوصول إلى أشكال رباعية متعددة من خلال استدعاءات SetAttachmentPoints المتكررة؛ فكل فهرس يتجاوز العدد الحالي يرجع false فقط، لنفس السبب الذي جعل الفهرس 0 يفعل ذلك في التعليق الجديد
بعد الكتابة، تحقق في برنامج عرض حقيقي بدلاً من الوثوق برموز الإرجاع: افتح الملف في Acrobat أو أي برنامج عرض يعتمد على PDFium وتأكد من هبوط الترميز على النص، وقراءته بالتعتيم المطلوب، ونجاته في جولة ذهاب وعودة للحفظ وإعادة التحميل. وأنواع التعليقات، ومعالجة الأشكال الرباعية، والكاتب المدرك للعدد الموضح هنا كلها أجزاء من مكون PDFium القياسي لدلفي و C++Builder و Lazarus؛ وتحمل صفحة المنتج مرجع واجهة برمجة التطبيقات الكامل للتعليقات إلى جانب بقية المكتبة