يتم تخزين مرفقات ملفات PDF في شجرة الملفات المضمنة في المستند، وهي بنية تعرضها معظم العوارض كلوحة مشبك ورق أو شريط جانبي للمرفقات. من كود Delphi، يعرض مكون PDFium هذه الشجرة من خلال مجموعة صغيرة من الخصائص المفهرسة على TPdf: حيث تقوم بالتكرار بواسطة فهرس صحيح، وقراءة الأسماء وحمولات البايت، وإنشاء فتحات جديدة، وحذف الفتحات الموجودة. سطح واجهة برمجة التطبيقات (API) ضيق؛ وهناك فقط عدد قليل من قيود الترتيب وقاعدة تطهير واحدة تستحق المعرفة قبل كتابة كود الإنتاج حولها
قراءة المرفقات من مستند مفتوح
تعطي AttachmentCount عدد الملفات المضمنة التي يعلن عنها المستند. وهي تقرأ مباشرة من استدعاء PDFium الأساسي، لذا فهي تعكس فقط ما يحتويه ملف PDF بالفعل. ومن هناك، تعيد AttachmentName[Index] اسم العرض كـ WString، وتسلم Attachment[Index] البايتات الخام كمصفوفة TBytes. كلاهما يعتمد على الصفر. ويجب أن يكون المستند مفتوحاً (Pdf.Active = True) قبل الاستعلام عن أي من الخاصيتين؛ حيث يؤدي استدعاءهما في مستند مغلق إلى إعطاء نتيجة صفرية أو فارغة دون إثارة استثناء
هناك شيء واحد يجب مراعاته: تقوم Attachment[Index] بتخصيص وإرجاع حمولة الملف الكاملة في كل قراءة. بالنسبة لمستند يحمل أصلاً مضمناً كبيراً، فإن التكرار عبر جميع المرفقات لإنشاء قائمة عرض يعني دفع تكلفة التخصيص هذه في كل استدعاء. وإذا كنت بحاجة فقط إلى الأسماء لأغراض العرض، فاقرأ AttachmentName أولاً وأرجئ جلب البايتات حتى يطلب المستخدم الملف بالفعل
procedure ListAttachments(Pdf: TPdf);
var
I: Integer;
Data: TBytes;
begin
if not Pdf.Active then
Exit;
for I := 0 to Pdf.AttachmentCount - 1 do
begin
Data := Pdf.Attachment[I];
Writeln(Format('%d: %s (%d bytes)',
[I, Pdf.AttachmentName[I], Length(Data)]));
end;
end;
استخراج المرفق إلى القرص
لا يوجد مساعد باسم SaveAttachment. فأنت تقرأ البايتات وتكتبها أينما تريد، مما يضع بناء المسار وتطهيره بالكامل على الكود الخاص بك. هذا الأمر مهم عندما تأتي أسماء المرفقات من مستندات غير موثوقة. أسماء مرفقات PDF هي سلاسل نصية مخزنة داخل الملف؛ ويمكن أن تحتوي على فواصل مسارات، ومتشابهات Unicode، وأحرف أخرى تنتج نتائج غير متوقعة إذا مررتها مباشرة إلى TFileStream.Create. قم دائماً بتشغيل الاسم من خلال ExtractFileName قبل بناء أي مسار إخراج، وفكر في رفض الأسماء التي تبدأ بنقطة أو تحتوي على أحرف خارج ما يتوقعه نظامك
مصفوفة البايتات التي تعيدها Attachment[Index] مملوكة للمستدعي. اكتبها باستخدام TFileStream عادي وهي ملكك لتفعل بها ما تشاء، بما في ذلك فحص البايتات القليلة الأولى للتحقق من تنسيق الملف الفعلي بدلاً من الوثوق بالاسم المعلن
procedure ExtractAttachment(Pdf: TPdf; Index: Integer; const OutputDir: string);
var
SafeName: string;
OutPath: string;
Data: TBytes;
FS: TFileStream;
begin
SafeName := ExtractFileName(Pdf.AttachmentName[Index]);
if SafeName = '' then
SafeName := Format('attachment_%d', [Index]);
OutPath := IncludeTrailingPathDelimiter(OutputDir) + SafeName;
Data := Pdf.Attachment[Index];
FS := TFileStream.Create(OutPath, fmCreate);
try
if Length(Data) > 0 then
FS.WriteBuffer(Data[0], Length(Data));
finally
FS.Free;
end;
end;
إضافة المرفقات والكتابة المكونة من خطوتين
يستغرق إنشاء مرفق استدعاءين، وليس استدعاءً واحداً. تسجل دالة CreateAttachment(Name) فتحة جديدة في شجرة الملفات المضمنة وتعيد True عند النجاح. تبدأ هذه الفتحة فارغة. ثم تقوم بتعيين الحمولة عن طريق الكتابة إلى Attachment[AttachmentCount - 1]، مستهدفاً الإدخال الذي تم إنشاؤه مؤخراً. وإذا أعادت CreateAttachment القيمة False، فلن يتم إنشاء الفتحة وسيتسبب التعيين في إتلاف المرفق عند أي فهرس يصدف أنه الأخير
بعد تعديل قائمة المرفقات، تعيش التغييرات في الذاكرة فقط. استدعى SaveAs لكتابة ملف جديد مع شجرة الملفات المضمنة المحدثة. لا يدعم مكون PDFium حالياً الحفظ في نفس الملف المفتوح حالياً، لأن المحرك يحتفظ بمقبض قراءة للمصدر. النمط القياسي للتحديث في المكان هو الحفظ في مسار مؤقت، وإغلاق المستند، وحذف أو إعادة تسمية الملف الأصلي، ثم إعادة تسمية الملف المؤقت إلى موضعه وإعادة فتحه
procedure AddFileAttachment(Pdf: TPdf; const FilePath: string);
var
FS: TFileStream;
Data: TBytes;
AttachName: string;
begin
if not Pdf.Active then
Exit;
FS := TFileStream.Create(FilePath, fmOpenRead or fmShareDenyWrite);
try
SetLength(Data, FS.Size);
if FS.Size > 0 then
FS.ReadBuffer(Data[0], FS.Size);
finally
FS.Free;
end;
AttachName := ExtractFileName(FilePath);
if Pdf.CreateAttachment(AttachName) then
Pdf.Attachment[Pdf.AttachmentCount - 1] := Data;
end;
معلومات نوع المرفق
بالإضافة إلى الاسم وحمولة البايت، تعيد AttachmentType[Index] سلسلة نوع MIME المخزنة في قاموس الملف المضمن في PDF، إذا تم تسجيل أحدها عند إرفاق الملف في الأصل. يترك العديد من المولدين هذا الحقل فارغاً أو يعينونه إلى قيمة عامة مثل application/octet-stream، لذا لا يمكنك الاعتماد عليه للكشف عن التنسيق في خط أنابيب الإنتاج. لتحديد موثوق، اقرأ البايتات القليلة الأولى من الحمولة وافحص توقيعات الملفات المعروفة: مثل %PDF لملف PDF متداخل، وترويسة ملف ZIP المحلي PK\x03\x04 لمستندات Office Open XML، و \xD0\xCF\x11\xE0 للملفات الثنائية المركبة القديمة. معلومات النوع من القاموس جيدة لعرضها في تسمية واجهة المستخدم، ولكن لا ينبغي أن تقود قرارات المعالجة عندما تكون البايتات الفعلية متوفرة لديك
حذف المرفقات
تقوم DeleteAttachment(Index) إزالة الإدخال عند هذا الموضع وتعيد True عند النجاح. وبعد الحذف، تنزاح الإدخالات المتبقية لأسفل، لذا إذا كنت تحذف مرفقات متعددة في حلقة تكرارية، فيجب عليك التكرار من الفهرس الأخير تنازلياً، وليس تصاعدياً، لتجنب تخطي الإدخالات بعد كل إزاحة. التغيير يكون في الذاكرة فقط حتى تستدعي SaveAs
السيناريو الشائع في خطوط أنابيب معالجة المستندات هو تجريد جميع المرفقات من ملف PDF الوارد قبل تمريره لمرحلة لاحقة، لأسباب تتعلق بالأمان أو الحجم. عد مرة واحدة قبل الحلقة وقم بالتكرار بشكل عكسي:
procedure StripAllAttachments(Pdf: TPdf);
var
I: Integer;
begin
for I := Pdf.AttachmentCount - 1 downto 0 do
Pdf.DeleteAttachment(I);
end;
أين تظهر مرفقات PDF في الممارسة العملية
تعمل واجهة برمجة تطبيقات المرفقات على أي ملف PDF يمكن لـ PDFium فتحه، ولكن المستندات التي تواجه فيها بالفعل ملفات مضمنة تتركز حول حالات قليلة محددة. يسمح PDF/A-3 (معيار ISO 19005-3) صراحة بملفات مضمنة متوافقة كآلية لحزم البيانات المصدرية جنباً إلى جنب مع النسخة الأرشيفية؛ وتعتمد الفواتير الإلكترونية ZUGFeRD و Factur-X على هذا بالضبط لتضمين حمولة XML مهيكلة داخل تخطيط PDF المقروء بشرياً. وتحمل ملفات PDF المستمدة من البريد الإلكتروني أحياناً مرفقات رسائلها الأصلية الممررة إلى شجرة الملفات المضمنة. كما تحزم الوثائق التقنية التي نشأت في أنظمة تأليف مهيكلة أصولاً داعمة بنفس الطريقة أحياناً
عندما يعالج تطبيقك ملفات PDF الواردة من خارج مؤسستك، فإن فحص AttachmentCount كجزء من استلام المستند أمر يستحق القيام به لسببين مستقلين. أولاً، قد تحمل الملفات المضمنة بيانات تريد استخراجها ومعالجتها، مثل XML داخل ملف PDF للفاتورة. ثانياً، يمكن للملفات المضمنة أن تحمل محتوى قابلاً للتنفيذ، لذا فإن معرفة ما هو موجود أمر مهم حتى عندما لا تنوي استخراجه أبداً. ولا يتطلب أي من السببين القيام بأي شيء معقد: اقرأ العدد، وتحقق من الأسماء، وقرر ما ستفعله بالبايتات
خصائص المرفقات المعروضة هنا هي جزء من مكون PDFium لـ Delphi و C++Builder