التعليق التوضيحي (annotation) لـ PDF هو قاموس مرفق بصفحة، وليس علامة مرسومة عليها. يحدد ISO 32000-1 §12.5 ما يقرب من دزينتين من الأنواع الفرعية (subtypes)، ويحمل كل منها /Subtype، ومستطيل في إحداثيات الصفحة، ومجموعة من العلامات (flags)، وعادة تيار مظهر (appearance stream) يقرر ما يرسمه العارض بالفعل. لا تعني جميع الأنواع الفرعية نفس الشيء للشخص الذي يراجع المستند. التمييز (Highlight) وضربة الحبر (Ink) هي تعليقات؛ الارتباط (Link) هو انتقال؛ النافذة المنبثقة (Popup) هي النافذة الصغيرة التي تفتح عند النقر فوق ملاحظة لاصقة، ويتم تخزينها ككائن خاص بها ويشير إليها أحد الوالدين. الردود هي تعليقات توضيحية نصية (Text) كاملة تشير إلى التعليق الذي ترد عليه من خلال إدخال in-reply-to. لذا فإن مصفوفة التعليقات التوضيحية على مستوى الصفحة ليست قائمة تعليقات المراجع. إنها حقيبة مسطحة تحتوي على تعليقات، والسباكة التي تربطهم، والعديد من الأشياء التي لن يطلق عليها أي مراجع تعليقًا على الإطلاق. اللوحة التي تتعامل مع المصفوفة كقائمة تعليقات ستختلف مع كل عارض آخر يقوم العميل بتشغيله
بناء سير عمل مراجعة التعليقات التوضيحية على مكون PDFium، وهو مكون VCL/LCL المستند إلى PDFium لـ Delphi و C++Builder و Lazarus، يعني التركيز على النقاط التي تسبب فيها هذه الفجوة بين المصفوفة الخام والمنظور البشري المتاعب: العد، والفهرسة، وإعادة تلوين العلامات التي جمدها المحرك بالفعل، والحذف دون ترك أشباح، وإضافة علاماتك الخاصة
لماذا لا يتطابق إحصاؤك أبدًا مع جزء تعليق Acrobat
افتح عقدًا مميزًا بعلامات في عارضك وفي Acrobat جنبًا إلى جنب ونادرًا ما تتفق الإجماليات. يعرض Acrobat عرضًا منسقًا: تم تجميع الترميز في سلاسل ردود، وتم طي النوافذ المنبثقة في الملاحظات التي تنتمي إليها، وتم استبعاد الارتباطات وعناصر واجهة مستخدم النموذج. تحمل المصفوفة الخام كل ذلك غير متمايز، لذا فإن العد الساذج يرتفع ببعض الطرق وينخفض ببعضها الآخر في نفس الوقت
تضخم النوافذ المنبثقة الإجمالي، لأن كل ملاحظة لاصقة تشحن مع كائن نافذة منبثقة منفصل وعد كلاهما يضاعف الملاحظة. الردود تقللها إذا قمت بالتصفية على العلامات المرئية، نظرًا لأن الرد عبارة عن تعليق توضيحي نصي مع عدم رسم أي شيء حتى يوسع شخص ما السلسلة، وإسقاطها يفقد المناقشة. تأخذ العلامتان المخفية (Hidden) و بلا عرض (NoView) تعليقًا توضيحيًا من الشاشة دون إخراجه من المصفوفة، لذلك يتضمن العد الأعمى للعلامات علامات لا يمكن للمستخدم رؤيتها. توضع التعليقات التوضيحية للارتباطات (Link) في نفس المصفوفة مثل التعليقات ولا تنتمي لا في العد ولا في القائمة. حدد قاعدة العد قبل أن تكتب الحلقة، واكتب القرار، لأن "لماذا تعرض لوحتك رقمًا مختلفًا عن Acrobat" هي التذكرة الأولى التي تكسبها ميزة المراجعة
قم بفهرسة كل شيء مرة واحدة، ثم لا تقم بإعادة تحليل الصفحة أبدًا
قاعدة تصميم واحدة تقود كل ما يلي: يجب ألا تعيد التصفية حسب المؤلف أو النوع أو الصفحة تحليل كائنات الصفحة أبدًا. في مستند مكون من 300 صفحة بترميز كثيف، يؤدي إعادة التحليل عند كل تغيير في القائمة المنسدلة إلى تحويل اللوحة إلى شيء يتعثر لثوانٍ في كل مرة. يعرض المكون AnnotationCount وخاصية Annotation[] المفهرسة، وكلاهما مقيد النطاق بالصفحة المحملة حاليًا، ويحمل سجل TPdfAnnotation الذي يعيدانه ما يحتاجه عرض القائمة: Subtype و Flags و Color و Rectangle و ContentsText و AuthorText. الخطوة الصحيحة هي مسح كل صفحة مرة واحدة في وقت الفتح والاحتفاظ بفهرسك المسطح الخاص بك:
procedure TReviewPanel.BuildIndex;
var
PageNo, i: Integer;
A: TPdfAnnotation;
begin
FItems.Clear;
for PageNo := 1 to Pdf.PageCount do
begin
Pdf.PageNumber := PageNo;
for i := 0 to Pdf.AnnotationCount - 1 do
begin
A := Pdf.Annotation[i];
// Keep reviewer-relevant subtypes only; record the page and
// index pair because all later edits are addressed by it
if A.Subtype in [anText, anHighlight, anInk] then
FItems.Add(TReviewItem.Create(PageNo, i,
A.AuthorText, A.ContentsText, A.Rectangle, A.Color));
end;
end;
end;
الزوج الذي يستحق وضع خط تحته هو (PageNo, i). تتم معالجة كل طفرة لاحقة، سواء كانت إعادة تلوين أو حذفًا، عن طريق رقم الصفحة بالإضافة إلى فهرس التعليق التوضيحي، والفهرس هش: إزالة تعليق توضيحي تعيد ترقيم كل شيء بعده في تلك الصفحة. لذا خطط لإعادة بناء إدخالات الصفحة المتأثرة بعد أي حذف بدلاً من ترقيع أرقام الفهرس في مكانها. تستغرق عملية إعادة البناء ملي ثانية. في المقابل، يحذف الفهرس القديم تعليق المراجع الخاطئ، وهو نوع الخطأ الذي يؤدي إلى تآكل الثقة في الميزة بأكملها
يستحق الترابط (Threading) فتحة في الفهرس حتى إذا كان إصدارك الأول يحصي الردود فقط بدلاً من إظهارها. قم بتجميع العناصر بواسطة المرجع الرئيسي الخاص بهم أثناء فتح الصفحة، حتى تتمكن اللوحة لاحقًا من طي سلسلة بطريقة Acrobat. إن إعادة بناء هذا التجميع بكسل أثناء التمرير يهزم نقطة الفهرسة بأكملها مرة واحدة، لأنه يعيد فتح الصفحات التي دفعت مقابل تحليلها. الهندسة تريد نفس الانضباط. Rectangle في كل سجل هو مساحة الصفحة، وتحويله لعرض الإحداثيات ينتمي إلى مساعد مشترك واحد، وليس منتشرًا عبر الكود. تنمو اللوحات بحشرات إحداثية عندما يخترع التحديد، واختبار الضرب، والطلاء كل منها رياضيات التكبير والدوران الخاصة بها؛ قم بتوجيه الثلاثة من خلال تحويل واحد وسيظل التمييز، وصفه في القائمة، وهدف النقر الخاص به مثبتين على نفس الحبر
إعادة تلوين الترميز وحق النقض لتيار المظهر
يبدو تغيير التمييز من اللون الأصفر إلى اللون الكهرماني وكأنه سطر واحد، وفي بعض الأحيان يكون كذلك. المصيد هو ISO 32000-1 §12.5.5. عندما يحمل تعليق توضيحي تيار مظهر /AP، يقوم عارض مطابق برسم ذلك التيار المبني مسبقًا ويعامل إدخال اللون في القاموس على أنه بيانات وصفية (metadata) ميتة. يكتب Acrobat تيارات المظهر لكل ما ينشئه بشكل أساسي، لذا فإن معظم التعليقات التوضيحية التي تصل من العملاء موجودة بالفعل في هذه الحالة، واللون الذي عينته بثقة شديدة لا يصل أبدًا إلى الشاشة. إعادة التلوين هي قراءة وتعديل وكتابة من خلال خاصية Annotation[]، والمكون صادق بشأن الصراع: عندما يرفض المحرك ترك لون قاموس يتجاوز مظهرًا مخبوزًا، تثير الكتابة EPdfError
A := Pdf.Annotation[Item.Index];
A.HasColor := True;
A.Color := $0000B0FF; // amber
A.ColorAlpha := 160;
try
Pdf.Annotation[Item.Index] := A;
except
on EPdfError do
begin
// The annotation owns a pre-rendered /AP stream; the dictionary
// color alone cannot change what viewers paint
Item.AppearanceLocked := True;
StatusBar.SimpleText := 'Color is fixed by the annotation appearance';
end;
end;
التقط هذا الاستثناء في كل مرة، وعامله كمعلومات بدلاً من اعتباره فشلاً. تخط الحارس وسوف تعرض لوحتك بمرح اللون الكهرماني في القائمة الخاصة بها بينما تستمر الصفحة في الرسم باللون الأصفر؛ يرسله المستخدم بعد أسابيع كـ "متصفحك يتجاهل تعديلاتي"، وتقضي فترة ما بعد الظهر في الفشل في إعادة إنتاجه على ملف يصادف أنه لا يحتوي على تيار مظهر. بمجرد أن تعرف أن المظهر مقفل، لديك استجابتان صادقتان: إعادة تلوين تراكب التحديد الخاص بك بدلاً من التعليق التوضيحي، حتى يرى المراجع على الأقل التمييز الذي اختاره، أو ضع علامة على الصف كمقفل للمظهر حتى لا يتوقع أحد أن يستمر التغيير
حذف التعليقات التوضيحية بدون ترك أشباح
تزيل DeleteAnnotation الكائن من شجرة التعليقات التوضيحية بالصفحة الحالية، لكنها تترك خط المسح الخاص بالصفحة المخبأة (cached page raster) بمفردها. قم بالطلاء فور الاستدعاء وسيظل التمييز المحذوف على الشاشة، جالسًا في صورة نقطية لم تعد تتطابق مع نموذج المستند خلفها. الإصلاح هو التعامل مع إعادة التصيير كجزء من عملية الحذف، وليس كخطوة قد ينساها المتصل:
Pdf.PageNumber := Item.PageNo;
Pdf.DeleteAnnotation(Item.Index); // raises EPdfError on failure
Bmp := Pdf.RenderPage(0, 0, ViewWidth, ViewHeight, ro0, [reAnnotations]);
try
PaintPageBitmap(Bmp);
finally
Bmp.Free; // RenderPage hands bitmap ownership to the caller
end;
RebuildPageEntries(Item.PageNo); // indices after Item.Index shifted
تفصيلان في هذه الكتلة من السهل أن تخطئ فيهما. يجب أن يكون خيار reAnnotations حاضرًا، وإلا فإن الخطية (raster) الجديدة تسقط كل تعليق توضيحي متبقي وتبدو الصفحة وكأنك مسحت مجموعة التعليقات بالكامل بدلاً من علامة واحدة. و Bmp.Free ليس اختياريًا: التحميل الزائد لـ RenderPage على غرار الدالة يسلم ملكية الصورة النقطية للمتصل، لذا فإن التحرير المفقود يسرب خط مسح كامل الصفحة في كل عملية حذف فردية، وهو ما سيحوله مراجع يعمل عبر مستند طويل إلى ضغط ذاكرة حقيقي في غضون دقائق
إضافة علامات مراجع من واجهة المستخدم الخاصة بك
يمر إنشاء التعليقات التوضيحية من خلال CreateAnnotation، والذي يأخذ سجل TPdfAnnotation ممتلئًا (النوع الفرعي، المستطيل، اللون، المحتويات، المؤلف) ويرفقه بالصفحة الحالية. الملاحظة اللاصقة، النوع الفرعي anText، هي الحالة السهلة: حدد الموضع والمحتويات والمؤلف وبذلك تكون قد انتهيت. التعليقات التوضيحية للحبر هي المكان الذي يقع فيه الناس في الفخ. يقيد مستطيل السجل الرسم فقط؛ الضربات نفسها عبارة عن مصفوفات من النقاط التي يجب إرفاقها بشكل منفصل من خلال استدعاء ضربة حبر المحرك، وتغذية FPDFAnnot_AddInkStroke ببيانات FS_POINTF، التي تم التقاطها من إدخال الماوس أو القلم بضربة واحدة في كل مرة. قم ببناء تعليق توضيحي بالحبر من مستطيل ولا شيء آخر وستحصل على خربشة فارغة يتم تصييرها كمساحة فارغة، والتي تبدو كخلل في المحرك وهي في الواقع تعليق توضيحي نصف مكتمل
قم بتسوية سياسة التأليف في نفس التنفس. يجب أن تحمل كل علامة تنشئها واجهة المستخدم الخاصة بك AuthorText متسقًا، لأن مرشح المراجع الذي تبنيه في الشهر المقبل جيد فقط مثل الأسماء التي تختمها على التعليقات اليوم. السلاسل الفارغة أو غير المتسقة للمؤلف لا يمكن إصلاحها بأثر رجعي دون إعادة فتح كل ملف
إخراج المراجعة من العارض
تكسب بيانات المراجعة قيمتها بمجرد أن تتمكن من ترك العارض، كملخص يقرأه قائد المشروع دون فتح الملف أو CSV يغذي ورقة التتبع. قم بالتصدير من الفهرس الذي قمت ببنائه بالفعل، وليس أبدًا من تحليل جديد، واختر طريقة مستقرة للرجوع إلى كل علامة. ينجو رقم صفحة مقترن بمستطيل التعليق التوضيحي من رحلات الذهاب والإياب التي لا يفعلها فهرس المصفوفة، لأن الحذف التالي يعيد ترقيم الفهارس بهدوء ويبدأ CSV في الإشارة إلى التعليقات الخاطئة
يحمل الصف الذي يستحق الاحتفاظ به الصفحة والنوع الفرعي والمؤلف والطابع الزمني للإنشاء عندما يسجل الملف واحدًا ونص المحتويات وعمود الحالة الذي تملكه بدلاً من الذي يوفره الـ PDF. يكون نفس تمرير الفهرسة مفيدًا في وقت سابق، أثناء الاستيعاب، عندما يصل مستند من خارج الفريق وتريد معرفة ما بداخلة قبل أن يراجعه أي شخص. تستعرض مقالة منضدة عمل استيعاب PDF هذا الفرز، ويغطي التنقل في حقل النموذج مشكلة الصورة المعكوسة: مراجعة المستندات المصممة لجمع البيانات بدلاً من التعليقات
حالة واحدة لن تظهرها لك المصفوفة
يستحق أحد أوضاع الفشل علامة لأنه يبدو كعيب في التعليمات البرمجية الخاصة بك وهو ليس كذلك. يُبلغ عميل عن تمييزات مرئية في جميع أنحاء الصفحة، لكن لوحتك لا تدرج أي شيء، ويعود AnnotationCount بصفر. التفسير المعتاد هو أن العلامات قد تم تسطيحها في مكان ما في المنبع. يخبز التسطيح مظاهر التعليقات التوضيحية في محتوى الصفحة العادي، لذلك تصبح التمييزات جزءًا من رسومات الصفحة وتتوقف عن الوجود ككائنات توضيحية تمامًا. لا يتبقى شيء لـ API التعليق التوضيحي لتعداده أو إعادة تلوينه أو حذفه. عندما ترى ترميزًا مرسومًا بإحصاء صفري، توقف عن البحث عن الخلل في حلقة التعداد الخاصة بك واسأل كيف تم إنتاج الملف
يتم شحن سطح التعليق التوضيحي المستخدم هنا، بدءًا من التعداد والإنشاء مرورًا بإعادة التلوين والحذف وخيارات التصيير التي تحافظ على صدق العرض، مع مكون PDFium لـ Delphi و C++Builder و Lazarus/FPC