يتحكم مكوّن PDFium في طبقات المحتوى الاختياري في PDF (OCGs) في Delphi عبر أسلوبين من TPdf: يسرد InspectOptionalContent كل طبقة مع الرؤية التي سيرسمها PDFium فعلًا، ويكتب SaveAsOptionalContentConfigured نسخة متحقق منها بدلت فيها الطبقات التي تختار تشغيلها أو إطفاءها. والأسلوب الثاني يعطل أيضًا قواعد Usage و/AS التي كانت ستبطل تعديلك بهدوء لولا ذلك. وكلاهما يعمل على المستند المفتوح سلفًا في TPdf، فلا يوجد محلل ثانٍ تبقيه متزامنًا مع ما يعرضه العارض
الطلب يصل عادة من ورشة CAD أو GIS: تشحن حزمة الرسومات بأبعاد وتعليقات ولوحة عنوان على طبقات منفصلة، ويريد العميل نسخة الأبعاد مخفاة فيها قبل أن تذهب إلى مورد. يرسم PDFium المحتوى الاختياري بشكل صحيح، لكن ABI العام لديه لا يملك دالة لتعداد OCGs أو اختيار تهيئة أو قلب حالة طبقة. فتهبط إلى مستوى الكائنات، وتحرر /OCProperties، وتحفظ، وتعيد التحميل، والطبقة ما تزال هناك. والسبب منطق الرؤية في PDFium، ويستحق الفهم قبل لمس أي بايتات
لماذا لا يغير تحرير /ON و/OFF ما يرسمه PDFium؟
تحرير مصفوفتي /ON و/OFF في قاموس التهيئة لا يكفي، لأن PDFium يدع حالة صريحة داخل قاموس /Usage الخاص بالـ OCG نفسه تتغلب على تلك المصفوفات، وقاعدة auto-state في /AS تستطيع بعدها تجاوز الاثنين. تصف ISO 32000-1 §8.11.4 التهيئات وقواميس الاستخدام آليتين منفصلتين؛ لكن مُصيّر PDFium يطويهما في قرار واحد، ويعيد InspectOptionalContent إنتاجه بهذا الترتيب:
- ابدأ من
/BaseStateفي التهيئة، حيث تحسب/ONو/Unchangedكلتاهما مرئية ولا يخفي إلا/OFF - طبق مصفوفة
/ONفي التهيئة ثم مصفوفة/OFFفيها، فتنتهي مجموعة مسردة في كلتيهما مخفاة - طبق حالة Usage الصريحة للمجموعة للاستخدام المطلوب، مثل
/Usage << /View << /ViewState /OFF >> >>، وهي تتجاوز كل ما سبق - عامل المجموعة التي لا يتضمن
/Intentلديها/Viewولا/Allبوصفها مرئية، لأنها لا تشارك في رؤية قصد العرض - أخيرًا شغّل مصفوفة
/ASفي التهيئة المختارة، التي تضبط مداخيلها للحدث المطابق حالةَ المجموعات التي تسردها
الخطوة الثالثة هي التي تحرق الناس. ملف حفظته أداة تخطيط كثيرًا ما يحمل /ViewState /ON على كل OCG، فيتجاهل PDFium حينها مصفوفة /OFF التي حررتها بعناية: الحفظ ينجح، والملف يعيد الفتح نظيفًا، والطبقة ترسم ما تزال. وللطباعة والتصدير، تقرأ OcExplicitUsageState PrintState أو ExportState أولًا وتعود إلى ViewState عند غياب المدخل المحدد، فـ ViewState /ON وحدها تثبت الطبقة للطباعة أيضًا. والمحتوى الموسوم الذي يشير إلى OCMD (§8.11.2.2) يحسم بعدها مقابل هذه النتائج لكل مجموعة، عبر سياسة /P أو، عند حضورها، تعبير الرؤية /VE
كيف تسرد الطبقات التي سيعرضها PDFium فعلًا؟
يعيد TPdf.InspectOptionalContent قيمة TPdfOptionalContentInventory تحمل مصفوفة Groups فيها رقم كائن كل OCG واسمه وقصوده وحالات Usage الثلاث ولغته ومدى التكبير وعلم Locked ومؤشر مجموعة الراديو وEffectiveVisible المحسوبة. يطلب الأسلوب أولًا من PDFium حفظ المستند الحالي في الذاكرة، وينشر تدفقات الكائنات، ويمسح الناتج، فتنعكس التعديلات التي أجريت سابقًا في الجلسة. مؤشر التهيئة 0 هو دائمًا قاموس /D الافتراضي وتليه مداخيل /Configs من المؤشر 1؛ والوسيط الافتراضي -1 يختار المؤشر 0. والمستند بلا /OCProperties يجعل الأسلوب يعيد False والسبب في ErrorMessage بدل رفع استثناء
procedure TFormMain.ListLayers;
var
Inv: TPdfOptionalContentInventory;
G: TPdfOptionalContentGroup;
begin
// Usage افتراضها ocuView؛ و-1 يختار التهيئة 0، أي قاموس /D
if not Pdf.InspectOptionalContent(Inv) then
begin
Memo1.Lines.Add('No usable layers: ' + Inv.ErrorMessage);
Exit;
end;
Memo1.Lines.Add(Format('Configuration %d: %s',
[Inv.SelectedConfigurationIndex,
string(Inv.Configurations[Inv.SelectedConfigurationIndex].Name)]));
for G in Inv.Groups do
Memo1.Lines.Add(Format('obj %d %s visible=%s locked=%s radio=%d',
[G.ObjectNumber, string(G.Name),
BoolToStr(G.EffectiveVisible, True),
BoolToStr(G.Locked, True), G.RadioGroupIndex]));
end;
تبلغ مصفوفة Memberships عن كل OCMD مع Policy خاصته (ocmpAnyOn وocmpAllOn وocmpAnyOff وocmpAllOff) ونص VisibilityExpression الخام وEffectiveVisible خاصته. وبضعة قواعد حدية مقصودة. افتراض /P هو /AnyOn، وOCMD بلا مجموعات تحسب مرئية. والإشارة إلى رقم كائن ليس OCG معروفًا تعامل مرئية بدل إفشال التعبير كله. ويقف تقييم /VE عند عمق تداخل 32 ويعامل ما هو أعمق مخفيًا، مما يمنع تعبيرًا معاديًا أو ذاتي الإشارة من تحويل الفحص إلى طفح مكدس
كتابة حالة طبقة جديدة عبر SaveAsOptionalContentConfigured
يأخذ TPdf.SaveAsOptionalContentConfigured مصفوفة من سجلات TPdfOptionalContentStateChange (رقم كائن المجموعة زائد Visible) ويكتب مستندًا تنتج فيه التهيئة المختارة تلك الحالة بالضبط. تنال التهيئة المختارة /BaseState /ON زائد مصفوفتي /ON و/OFF كاملتين تغطيان كل مجموعة، وينال كل OCG يملك قاموس Usage سلفًا حالة ViewState صريحة (أو PrintState / ExportState وفق Options.Usage) تطابق حالته الجديدة. ومع TPdfOptionalContentConfigureOptions.Default ينزع مفتاح /AS من التهيئة المختارة كي لا يقلب حدث فتح أو طباعة أو تصدير الطبقات مرة أخرى
procedure TFormMain.SaveWithoutDimensions(DimensionsObj, NotesObj: Integer);
var
Changes: TPdfOptionalContentStateChanges;
Options: TPdfOptionalContentConfigureOptions;
Report: TPdfOptionalContentConfigureReport;
begin
SetLength(Changes, 2);
Changes[0].GroupObjectNumber := DimensionsObj;
Changes[0].Visible := False;
Changes[1].GroupObjectNumber := NotesObj;
Changes[1].Visible := True;
// التهيئة 0، وocuView، وDisableAutomaticState وEnforceRadioGroups بقيمة True
Options := TPdfOptionalContentConfigureOptions.Default;
if not Pdf.SaveAsOptionalContentConfigured('C:\Out\Drawing-NoDims.pdf',
Changes, Options, Report) then
raise Exception.Create('Layer update rejected: ' + Report.ErrorMessage);
Log(Format('%d of %d groups changed, %d Usage states rewritten, /AS removed: %s',
[Report.ChangedGroupCount, Report.GroupCount,
Report.UpdatedUsageStateCount,
BoolToStr(Report.RemovedAutomaticState, True)]));
end;
يحفظ مسار الكتابة مخرج حفظ PDFium ذاته بادئةً بايتًا ببايت ويلحق فقط مالك التهيئة المعاد كتابته وكائنات OCG الحاملة لقواميس Usage، يليهما قسم xref جديد وtrailer. وقبل أن يبلغ بايت واحد وجهتك، يعاد فتح الناتج في TPdf منفصل تحت سياسة التحميل الصارمة، ويفشل الأسلوب إن لم يتحقق جدول الإشارات المرجعية. والتحميل الزائد للملفات يخطو خطوة أبعد: يكتب في ملف مؤقت بجوار الهدف ولا يستبدل الهدف إلا بعد نجاح التحقق، فلن يترك تحديث مرفوض رسمة نصف مكتوبة خلفه أبدًا. وهو أسلوب المراجعة التزايدية المتحقق منها نفسه الذي يستخدمه محرر شجرة الأسماء وشجرة الأرقام في PDF في مكوّن PDFium
ما الذي يرفض الحفظ المهيأ فعله؟
يرفض الحفظ المهيأ أي تغيير يحظره المستند نفسه أو لا يستطيع تمثيله بأمان، وكل رفض يقع قبل لمس الوجهة. رقم كائن ليس في /OCGs يفشل تمامًا. تغيير مجموعة مسردة في مصفوفة /Locked في التهيئة يفشل، مع جواز إعادة ذكر قيمتها الحالية. ومع تفعيل EnforceRadioGroups، أي مجموعة /RBGroups ستنتهي بأكثر من عضو مرئي ترفض بدل إطفاء البقية بهدوء. والمستندات المشفرة ترفض لأن الكائنات التزايدية الصريحة لا تستطيع حمل معالج الأمان النشط. والمستندات الموقعة ترفع EPdfError ما لم تمرر AllowSignedDocument = True، لأن تغيير ما تعرضه صفحة قد يكسر تغطية التوقيع أو سياسة اعتماد
function TFormMain.SavePrintPreset(Target: TStream;
const Changes: TPdfOptionalContentStateChanges): Boolean;
var
Options: TPdfOptionalContentConfigureOptions;
Report: TPdfOptionalContentConfigureReport;
begin
Options := TPdfOptionalContentConfigureOptions.Default;
Options.Usage := ocuPrint; // يكتب /Print << /PrintState ... >>
Options.ConfigurationIndex := 1; // أول مدخل في /Configs، لا /D
try
Result := Pdf.SaveAsOptionalContentConfigured(Target, Changes, Options,
Report); // تبقى AllowSignedDocument بقيمة False
if not Result then
ShowMessage(Report.ErrorMessage);
except
on E: EPdfError do
begin
ShowMessage(E.Message); // ملف موقّع: لا يكتب شيء في Target
Result := False;
end;
end;
end;
اعرف المفاضلات قبل أن توصل هذا في عمل دفعي. المراجعة الملحقة تجلس فوق إعادة حفظ PDFium الكاملة، لا فوق بايتات ملفك الأصلي، وهذا بالضبط سبب حاجة المدخل الموقع إلى موافقة صريحة. والكتابة من جديد تطبع أيضًا التهيئة المختارة إلى /BaseState /ON، فخط الأساس /Unchanged أو /OFF من المؤلف يستبدل بمصفوفات صريحة تعطي الرؤية الناتجة نفسها. ونزع /AS يزيل حيل الطباعة فقط كطبقة علامة مائية تظهر على الورق وحده؛ واضبط DisableAutomaticState إلى False للاحتفاظ بتلك القواعد، مقبلًا بأنها قد تتجاوز الحالة التي طلبتها لذلك الحدث. ومن الجانب المضيء، تحرمان PDF/A-2 (ISO 19005-2 بند 6.9) وPDF/UA (ISO 14289-1 بند 7.10) كلتاهما /AS في قواميس التهيئة، فيزيل المخرج الافتراضي مشكلة كانت التحقق المسبق PDF/A عبر مكوّن PDFium ستبلغ عنها لولا ذلك
أين يقع التحكم في الطبقات داخل عارض PDF لدلفي
في عارض، التحكم في الطبقات قائمة فحص تقودها المخزونات زائد إعادة تحميل للناتج المحفوظ. املأ القائمة من Groups، وعطل المداخيل المقفلة Locked، وعامل الأعضاء المتقاسمين RadioGroupIndex بوصفهم متعارضين، وعند التطبيق اكتب في TMemoryStream وحمّل ذلك التدفق مرة أخرى في TPdf لترسم العرض الحالة الجديدة. والربط بين TPdf وTPdfView مغطى في بناء عارض PDF غني بالميزات عبر PDFium VCL في دلفي. والترخيص وتنزيلات التجربة وبقية مجموعة الميزات على صفحة منتج مكوّن PDFium لدلفي