مقاله فنی

خروجی و ورودی XFDF در دلفی با PDFium Component

کامپوننت PDFium برای دلفی و لازاروس، داده‌های فرم و یادداشت‌ها را از طریق XFDF مبادله می‌کند؛ فرمت تبادل XML که توسط استاندارد ISO 19444-1 تعریف شده است. متد TPdf.ExportXFDF هر مقدار فیلد فرم و هر یادداشت پشتیبانی‌شده در سند بارگذاری‌شده را در یک فایل XFDF یا TStream سریال‌سازی می‌کند؛ متد TPdf.ImportXFDF یک سند XFDF را به عقب می‌خواند، یادداشت‌ها را در صفحات خود بازسازی کرده و مقادیر فیلدها را اعمال می‌کند. این رفت و برگشت، دو جریان کاری را پوشش می‌دهد که هر برنامه سندی در نهایت به آنها نیاز دارد: یک بازبین، یک فایل PDF را در Acrobat نشانه‌گذاری می‌کند و کامنت‌ها را برای شما می‌فرستد تا ادغام کنید، یا یک قانون انطباق می‌گوید داده‌های فرم باید در یک فایل مجزا، قابل مقایسه و حسابرسی زندگی کنند به جای اینکه در خود PDF پخته شوند

آیا PDFium از ورودی و خروجی XFDF پشتیبانی می‌کند؟

خود کتابخانه PDFium خیر. API بومی C اصلاً هیچ تابع XFDF یا FDF ندارد — هیچ چیز در هدرهای عمومی آن قالب را نمی‌خواند یا نمی‌نویسد، بنابراین هیچ مقدار پوشش‌دهی شما را به آنجا نمی‌رساند. بنابراین کامپوننت PDFium کل موتور XFDF را در پاسکال پیاده‌سازی می‌کند، در یک یونیت اختصاصی که XML را مستقیماً روی مدل داده‌های موجود یادداشت‌ها و فیلدهای فرم TPdf می‌سازد و تجزیه می‌کند. نویسنده XML را به صورت دستی صادر می‌کند و خواننده یک تجزیه‌کننده ماشین وضعیت دست‌نویس است، بنابراین این ویژگی هیچ وابستگی به Delphi XMLDoc یا واحدهای FPC DOM اضافه نمی‌کند و در دلفی و لازاروس رفتاری کاملاً یکسان دارد

این تصمیم طراحی در هنگام ارزیابی گزینه‌های جایگزین اهمیت دارد. اگر توابع خام FPDF_* را از پاسکال فراخوانی می‌کرده‌اید، XFDF یک دیوار است: این قابلیت به سادگی در DLL وجود ندارد. کامپوننت با برخورد با XFDF به عنوان یک مسئله سریال‌سازی خالص، از این دیوار عبور می‌کند — مقادیر فیلدها و رکوردهای یادداشتی را که پوشش‌دهنده قبلاً می‌داند چگونه بخواند جمع‌آوری می‌کند، آنها را به عنوان XML استاندارد می‌نویسد و روند را در هنگام ورود معکوس می‌کند

یک فایل XFDF حاوی چه چیزی است؟

استاندارد ISO 19444-1 فرمت XFDF را به عنوان نمایش XML از FDF تعریف می‌کند و داده‌های آن به دو بلوک سطح بالا تقسیم می‌شوند. عنصر <fields> نام‌های سلسله‌مراتبی فیلدهای فرم و مقادیر آنها را حمل می‌کند — هر چیزی که کاربر در یک AcroForm تایپ، انتخاب یا علامت‌گذاری کرده است. عنصر <annots> یادداشت‌ها را حمل می‌کند: متد TPdf.ExportXFDF تعداد ۱۸ نوع فرعی را صادر می‌کند — شامل متن (text)، هایلایت (highlight)، خط زیرین (underline)، خط خوردگی (strikeout)، خط زیگزاگ (squiggly)، خط (line)، دایره (circle)، مربع (square)، علامت جهت (caret)، چندضلعی (polygon)، چندخطی (polyline)، مهر (stamp)، جوهر (ink)، متن آزاد (freetext)، پیوست فایل (fileattachment)، صدا (sound)، لینک (link) و اصلاح (redact). یادداشت‌های ابزارک (Widget) به طور تعمدی در <annots> وجود ندارند زیرا داده‌های آنها در <fields> سفر می‌کنند، و نوع‌های فرعی داخلی نمایشگر مانند Popup بخشی از واژگان XFDF نیستند

برای وفادار نگه داشتن رفت و برگشت، رکورد TPdfAnnotation با متادیتایی که XFDF انتظار دارد گسترش یافته است: Name (شناسه منحصربه‌فرد NMSubject، ModificationDate، CreationDate، Icon، Opacity، نقاط انتهایی خط، رئوس چندضلعی و چندخطی، و مسیرهای ژست جوهر. هر فیلد جدید با یک نگهبان بولین Has* جفت می‌شود، بنابراین کدهایی که یادداشت‌ها را بر اساس نسخه‌های قبلی ساخته‌اند همچنان کامپایل می‌شوند و همان دیکشنری‌ها را تولید می‌کنند — یک فیلد تنظیم‌نشده هرگز نوشته نمی‌شود. اگر از قبل به صورت برنامه‌نویسی نشانه‌گذاری ایجاد می‌کرده‌اید، همان رکوردی که برای یادداشت‌های نشانه‌گذاری متن با نقاط چهارگانه استفاده می‌کنید، اکنون حاوی تمام چیزهایی است که XFDF نیاز دارد

چگونه داده‌های فرم و یادداشت‌ها را به XFDF صادر کنیم؟

یک فراخوانی کل سند را پوشش می‌دهد. فایل PDF را با فعال بودن پر کردن فرم بارگذاری کنید، متد ExportXFDF را صدا بزنید و کامپوننت تک‌تک صفحات را می‌پیماید، مقادیر فیلدها و یادداشت‌ها را جمع‌آوری می‌کند و XML را می‌نویسد. مقدار برگشتی، تعداد بایت‌های UTF-8 نوشته شده است که یک بررسی ساده در لاگ‌ها به حساب می‌آید

var
  Pdf: TPdf;
  Bytes: Integer;
begin
  Pdf := TPdf.Create(Self);
  Pdf.FormFill := True;            // needed so field values are live
  Pdf.FileName := 'expense-report.pdf';
  Pdf.Active := True;
  Bytes := Pdf.ExportXFDF('expense-report.xfdf');
  ShowMessage(Format('%d bytes of XFDF written', [Bytes]));
end;

هر دو جهت دارای یک بارگذاری اضافی TStream هستند، بنابراین هیچ چیز فایل موقتی را روی دیسک تحمیل نمی‌کند. صادر کردن به یک TMemoryStream در زمانی که XFDF به سمت یک پاسخ HTTP، یک بلاب پایگاه‌داده یا یک ورودی آرشیو امضا شده می‌رود، شکل طبیعی کار است

var
  Buffer: TMemoryStream;
begin
  Buffer := TMemoryStream.Create;
  try
    Pdf.ExportXFDF(Buffer);        // same XML, no file involved
    Buffer.Position := 0;
    // hand the stream to a web response, blob column, or zip entry
  finally
    Buffer.Free;
  end;
end;

متد ImportXFDF چگونه کامنت‌ها را در سند ادغام می‌کند؟

سمت ورود جایی است که جریان کاری بازبین بسته می‌شود. یک همکار قرارداد را در Acrobat یادداشت می‌کند، کامنت‌ها را به عنوان XFDF صادر می‌کند و به جای نسخه دوم PDF، چند کیلوبایت XML برای شما ارسال می‌کند. متد TPdf.ImportXFDF آن فایل را تجزیه می‌کند، هر یادداشت را در صفحه‌ای که ویژگی page آن نام می‌برد ایجاد می‌کند و مقادیر فیلدها را در یادداشت‌های ویجت مربوطه می‌نویسد. این تابع تعداد کل فیلدها به علاوه یادداشت‌های اعمال‌شده را برمی‌گرداند، بنابراین رابط کاربری می‌تواند دقیقاً تأیید کند که چه مقدار اطلاعات رسیده است

var
  Applied: Integer;
begin
  Applied := Pdf.ImportXFDF('review-comments.xfdf');
  StatusBar.SimpleText :=
    Format('%d fields and annotations merged', [Applied]);
end;

تجزیه‌کننده یک اسکنر تگ دست‌نویس است که برای تحمل خطا ساخته شده است و نه سختی: عناصر ناشناخته نادیده گرفته می‌شوند، دستورالعمل‌های پردازش، تعاریف DOCTYPE و کامنت‌ها نادیده گرفته می‌شوند، نام فلگ‌های ناشناخته حذف می‌شوند و مقادیر ویژگی‌ها از طریق مجموعه کامل مراجع کاراکترهای عددی و موجودیت‌های XML، از جمله جفت‌های جایگزین UTF-16 رمزگشایی می‌شوند. فایل XFDF تولید شده توسط Acrobat، توسط یک نمایشگر وب یا ابزار دیگر بدون هیچ تشریفاتی ادغام می‌شود. هنگامی که یادداشت‌ها در سند قرار گرفتند، چرخه بررسی با مکانیزم پاسخ و وضعیت توضیح داده شده در ساخت یک جریان کاری بررسی یادداشت با استفاده از PDFium Component ادامه می‌آید و مقادیر فیلدهای وارد شده در منطق معمولی ترتیب تب‌ها که در ناوبری فیلدهای فرم پوشش داده شده است، شرکت می‌کنند

چرا جداکننده‌های اعشاری برای XFDF اهمیت دارند؟

هر یادداشت در XFDF با رشته‌های مختصات موقعیت‌دهی می‌شود — مانند rect="70.5,540,200,560" و موارد مشابه — و استاندارد ISO 19444-1 به نقطه به عنوان جداکننده اعشار نیاز دارد. فرمت‌بندی پیش‌فرض اعشاری دلفی از لوکال ویندوز پیروی می‌کند، بنابراین در یک سیستم آلمانی یا فرانسوی، یک متد ساده FloatToStr مقدار 70.5 را به 70,5 تبدیل می‌کند، که یک لیست مختصات جدا شده با ویرگول را به اطلاعات خرابی تبدیل می‌کند که سایر پردازشگرها آن را رد می‌کنند یا اشتباه می‌خوانند. کامپوننت PDFium هر عددی را که می‌نویسد نرمال‌سازی می‌کند: جداکننده اعشاری محلی به نقطه بازنویسی می‌شود و صفرهای انتهایی حذف می‌گردند، بنابراین فایل صادر شده بایت به بایت یکسان است، چه روی یک سیستم en-US تولید شده باشد و چه روی یک سیستم de-DE. اگر تا به حال یک ابزار PDF را دیباگ کرده‌اید که در دفتر کار می‌کرد و در سایت مشتری اروپایی با شکست مواجه شد، دقیقاً با همین کلاس از باگ‌ها ملاقات کرده‌اید — ارزش دانستن دارد که کامپوننت این کار را برای شما مدیریت می‌کند

محدودیت‌های رفت و برگشت چیست؟

دو مرز شایسته بیان صریح هستند. اول اینکه XFDF داده‌های یادداشت را منتقل می‌کند و نه ظاهر رندر شده را — استریم‌های ظاهر بخشی از این فرمت نیستند، بنابراین نمایشگر دریافت‌کننده ظاهر هر یادداشت را از روی ویژگی‌های آن بازسازی می‌کند. یک هایلایت یا مربع در همه جا به درستی رندر می‌شود؛ یک مهر با ظاهر سفارشی به آنچه نمایشگر هدف برای آن نام مهر رسم می‌کند تنزل می‌یابد. دوم اینکه API زیرین PDFium متدهایی برای تنظیم رشته‌ها دارد اما هیچ کدام را برای شماره‌های یادداشت یا هندسه مسیر ندارد، بنابراین Opacity، نقاط انتهایی خط، رئوس و ژست‌های جوهر فقط خروجی (export-only) هستند: کامپوننت در هنگام نوشتن XFDF آنها را به وفاداری حفظ می‌کند، اما نمی‌تواند در هنگام ورود آنها را به داخل PDF بنویسد. مقادیر فیلدها، محتویات، رنگ‌ها، کادرها (rects)، نقاط چهارگانه، تاریخ‌ها و متادیتای هویت همگی در هر دو جهت رفت و برگشت می‌کنند

برای شروع عملی، نمونه XfdfLab در مجموعه‌های دمو دلفی، C++Builder و لازاروس ارائه می‌شود و هر دو فراخوانی دکمه‌ها را روی هر PDF که باز می‌کنید متصل می‌نماید. پشتیبانی از XFDF در نسخه فعلی PDFium Component در کنار APIهای یادداشت و فرم که بر روی آنها ساخته شده است، گنجانده شده است