מאמר טכני

ייצוא וייבוא XFDF ב-Delphi עם רכיב PDFium

רכיב PDFium עבור Delphi ו-Lazarus מחליף נתוני טפסים והערות באמצעות XFDF, פורמט חילופי XML המוגדר על ידי ISO 19444-1. הפונקציה TPdf.ExportXFDF מסדרת כל ערך של שדה טופס וכל הערה נתמכת במסמך הטעון לקובץ XFDF או ל-TStream; הפונקציה TPdf.ImportXFDF קוראת מסמך XFDF חזרה, מייצרת מחדש הערות בדפים שלהן ומחילה ערכי שדות. סבב (round-trip) זה מכסה שני תהליכי עבודה שכל אפליקציית מסמכים צריכה בסופו של דבר: מבקר מסמן קובץ PDF ב-Acrobat ושולח לכם את ההערות למיזוג, או שכלל תאימות קובע שנתוני טפסים חייבים לחיות בקובץ נפרד, הניתן להשוואה ולביקורת, במקום להיות מוטמעים בתוך ה-PDF עצמו

האם PDFium תומך בייבוא וייצוא של XFDF?

ספריית PDFium עצמה אינה תומכת בכך. ל-API המקורי של C אין פונקציות XFDF או FDF כלל — שום דבר בכותרות הציבוריות שלו אינו קורא או כותב אף אחד מהפורמטים, כך ששום מעטפת לא תביא אתכם לשם. לכן רכיב PDFium מיישם את מנוע ה-XFDF כולו ב-Pascal, ביחידה ייעודית הבונה ומנתחת את ה-XML ישירות מעל מודל הנתונים הקיים של הערות ושדות טפסים של TPdf. הכותב מפיק את ה-XML באופן ידני והקורא הוא מנתח מכונת מצבים שנכתב ידנית, כך שהתכונה אינה מוסיפה שום תלות ב-Delphi XMLDoc או ביחידות ה-DOM של FPC ומתנהגת באופן זהה ב-Delphi וב-Lazarus

בחירה עיצובית זו חשובה כאשר אתם מעריכים חלופות. אם קראתם לפונקציות FPDF_* גולמיות מ-Pascal,‏ XFDF הוא קיר: היכולת פשוט אינה קיימת ב-DLL. הרכיב חוצה קיר זה על ידי התייחסות ל-XFDF כבעיית סריאליזציה טהורה — איסוף ערכי השדות ורשומות ההערות שהמעטפת כבר יודעת לקרוא, כתיבתם כ-XML סטנדרטי, והפיכת התהליך בייבוא

מה מכיל קובץ XFDF?

תקן ISO 19444-1 מגדיר את XFDF כייצוג ה-XML של FDF, והפלט שלו מתפצל לשני בלוקים ברמה העליונה. האלמנט <fields> נושא שמות שדות טפסים היררכיים והערכים שלהם — כל מה שמשתמש הקליד, בחר או סימן ב-AcroForm. האלמנט <annots> נושא את ההערות: TPdf.ExportXFDF מפיק 18 סוגי משנה — טקסט, הדגשה, קו תחתון, קו חוצה, קו גלי, קו, עיגול, ריבוע, סימן חץ, פוליגון, פוליליין, חותמת, דיו, טקסט חופשי, קובץ מצורף, שמע, קישור ומחיקה. הערות Widget אינן מופיעות ב-<annots> מכיוון שהנתונים שלהן מועברים ב-<fields>, וסוגי משנה פנימיים של המציג כמו Popup אינם חלק מאוצר המילים של XFDF

כדי להפוך את הסבב לנאמן, רשומת TPdfAnnotation הורחבה עם המטא-נתונים ש-XFDF מצפה להם: Name (המזהה הייחודי NM),‏ Subject,‏ 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;

לשני הכיוונים יש עומס יתר (overload) של TStream, כך ששום דבר אינו מחייב קובץ זמני בדיסק. ייצוא לתוך TMemoryStream הוא המבנה הטבעי כאשר ה-XFDF מיועד לתגובת HTTP, לעמודת blob במסד נתונים, או לרשומת ארכיון חתומה

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 ושולח לכם כמה קילו-בייטים של XML במקום עותק נוסף של ה-PDF. הפונקציה 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, וערכי שדות מיובאים משתתפים בלוגיקת סדר הטאבים הרגילה המכוסה בניווט בשדות טפסים

מדוע מפרידים עשרוניים חשובים עבור XFDF?

כל הערה ב-XFDF ממוקמת לפי מחרוזות קואורדינטות — rect="70.5,540,200,560" וכדומה — ותקן ISO 19444-1 דורש נקודה כמפריד עשרוני. עיצוב ברירת המחדל של מספרים עשרוניים ב-Delphi עוקב אחר הגדרות האזור של Windows, כך שבמערכת גרמנית או צרפתית פונקציה פשוטה כמו FloatToStr הופכת את 70.5 ל-70,5, מה שמשחית רשימת קואורדינטות מופרדת בפסיקים לתוכן לא חוקי שמעבדים אחרים דוחים או קוראים בצורה שגויה. רכיב PDFium מנרמל כל מספר שהוא כותב: המפריד העשרוני המקומי משוכתב לנקודה ואפסים נגררים נחתכים, כך שהקובץ המיוצא זהה ביט לביט בין אם הופק במכונת en-US או במכונת de-DE. אם אי פעם ניפיתם באגים בכלי PDF שעבד במשרד ונכשל אצל לקוח אירופי, פגשתם בדיוק את סוג הבאג הזה — כדאי לדעת שהרכיב מטפל בזה עבורכם

מהם גבולות הסבב (round-trip)?

שני גבולות ראויים לציון. ראשית, XFDF מעביר נתוני הערות, לא מראה מרונדר — זרמי מראה אינם חלק מהפורמט, כך שהמציג המקבל מייצר מחדש כל מראה הערה מתוך המאפיינים שלה. הדגשה או ריבוע ירונדרו נכון בכל מקום; חותמת עם מראה מותאם אישית תיסוג למה שהמציג המקבל מצייר עבור אותו שם חותמת. שנית, ל-API של PDFium שמתחת לרכיב יש פונקציות להגדרת מחרוזות אך אין לו פונקציות להגדרת מספרי הערות או גיאומטריית נתיבים, ולכן Opacity, נקודות קצה של קווים, קודקודים ותנועות דיו הם לייצוא בלבד: הרכיב משמר אותם בנאמנות בעת כתיבת XFDF, אך אינו יכול לכתוב אותם חזרה לתוך PDF בעת ייבוא. ערכי שדות, תוכן, צבעים, מרובעים, נקודות מרובעות, תאריכים ומטא-נתוני זהות כולם מבצעים סבב מלא בשני הכיוונים

להתחלה מעשית, הדוגמה XfdfLab מגיעה בערכות ההדגמה של Delphi,‏ C++Builder ו-Lazarus ומחברת את שתי הקריאות לכפתורים מעל כל PDF שתפתחו. תמיכת XFDF כלולה בגרסה הנוכחית של רכיב PDFium, לצד ממשקי ה-API של הערות וטפסים עליהם היא נשענת