losLab PDF Library מעבירה נתוני טפסים פנימה והחוצה מתוך PDF בשלוש דרכים שונות: FDF ו-XFDF עבור ערכי שדות של AcroForm, אותם שני פורמטים עבור הערות (annotations), וחבילת XFA XDP שלמה שנכתבת ישירות לתוך הטופס. ExportFormDataToXFDF, ImportFormDataFromXFDF, ImportAnnotationsFromFDF, ExportAnnotationsToXFDF ו-SetXFAFromString הם נקודות הכניסה, ולכל אחת מהן יש גרסת קובץ וגרסת מחרוזת
הסיבה שיש כל כך הרבה שיטות היא שנתוני טפסים ב-PDF אינם מקשה אחת. AcroForm מלא מכיל ערכי שדות, הוא עשוי גם לשאת הערות סקירה, וטופס XFA מיושן מטמיע תיאור יישום XML שלם שאין לו שום קשר לשניהם. losLab PDF Library שומרת על שלושת הנושאים הללו בממשקי API נפרדים בכוונה, מכיוון שמיזוגם יכפה מודל נתונים שגוי על שניים מהם לפחות. ההחלטה איזו משפחה נחוצה לך היא בחירת העיצוב הראשונה, והיא בדרך כלל נקבעת ברגע שאתה יודע מה המערכת המקבלת בצד השני באמת צורכת
מה ההבדל בין נתוני טפסים של FDF, XFDF ו-XFA?
FDF ו-XFDF נושאים את אותו המידע בשני תחבירים שונים, ו-XFA הוא עולם נפרד. FDF הוא מסמך קטנטן בתחביר PDF (ISO 32000-2 §12.7.8): מערך /Fields של מילוני << /T (name) /V (value) >>, עם מחרוזות מוחרגות (escaped) בדיוק באותו אופן שבו מחרוזות מילוליות מוחרגות בתוך PDF. XFDF הוא פורמט ה-XML של אותם נתונים (ISO 19444), עץ <fields> ש-Acrobat ורוב מערכות הניהול של טפסים קוראים וכותבים באופן טבעי. XFA אינו אף אחד מאלה: הוא תבנית XML Forms Architecture יחד עם הנתונים שלה, המאוחסנת כחבילת נתוני XML (XDP) שה-PDF מפנה אליה מתוך /AcroForm/XFA. בחר ב-FDF או ב-XFDF כאשר אתה מקיים אינטראקציה על ערכי שדות, ופנה ל-XFA רק כאשר אתה מתחזק מסמך שנכתב מלכתחילה כטופס XFA
בתוך FDF ו-XFDF, losLab PDF Library משרטטת קו גבול שני: ערכי שדות לעומת הערות סקירה. משפחת נתוני הטפסים (ExportFormDataToFDF, ImportFormDataFromFDF, ExportFormDataToXFDF, ImportFormDataFromXFDF) קוראת וכותבת את תת-העץ /Fields ואינה נוגעת בשום דבר אחר. משפחת ההערות (ExportAnnotationsToFDF, ImportAnnotationsFromFDF, ExportAnnotationsToXFDF, ImportAnnotationsFromXFDF) קוראת וכותבת את תת-העץ /Annots במקום זאת, לזה מתכוונת Acrobat בייצוא הערות. השניים אינם חופפים לעולם, כך שייצוא ערכי שדות לא יגרור הערות סקירה תועות, וייבוא הערות לא יפריע לערכים שמשתמש כבר הקליד. מה שפקד (widget) עושה כאשר לוחצים עליו או מחשבים אותו מחדש הוא נושא שלישי, המכוסה בהערה הנלווית על פעולות טפסים אינטראקטיביות ו-JavaScript
כיצד מאכלסים טופס PDF מתוך FDF או XFDF בדלפי?
טען את המסמך, קרא לשיטת ייבוא אחת, ושמור. ImportFormDataFromXFDF ו-ImportFormDataFromFDF מנתחות כל אחת את נתוני הטופס הנכנסים, מתאימות כל רשומה לשדה AcroForm לפי שמו המלא, מגדירות את הערך ומחזירות את מספר השדות שעודכנו בפועל. שתי השיטות מעדכנות רק שדות שכבר קיימים ב-PDF המטרה; אף אחת מהן אינה ממציאה שדה עבור שם שהטופס אינו מגדיר, מה שמונע מקובץ נתונים תועה או עוין להגדיל בשקט את הטופס שלך
שני פרטים קובעים אם נתונים מורכבים ישרדו את המסלול. הראשון הוא החרגה (escaping): FDF מאחסן ערכים כמחרוזות מילוליות של PDF, ולכן ערך המכיל סוגריים, לוכסן אחורי או בית שאינו ניתן להדפסה מגיע עטוף בהחרגה אוקטלית המוגדרת ב-ISO 32000-2 §7.3.4.2, ו-losLab PDF Library הופכת את ההחרגה הזו בייבוא כך שהסוגריים והלוכסנים חוזרים כתווים המילוליים שהמשתמש הקליד. השני הוא קידוד: השיטות המבוססות על קבצים כותבות וקוראות XFDF ו-FDF כ-UTF-8, וזה מה שהצהרת ה-XFDF מבטיחה ומה שכל ערך שדה שאינו ASCII (שם עם סימני דיאקריטיקה, סמל מטבע, כתובת CJK) צריך כדי להשלים סבב ללא השחתה. אם אתה בונה את ה-XFDF בעצמך, הצהר על UTF-8 וכתוב ב-UTF-8, והייבוא יתאים לדרישותיך
שדות היררכיים, אפשרויות בחירה מרובות וטקסט עשיר
שמות שדות היררכיים הם המקום הראשון שבו מייצא נאיבי נשבר. AcroForm פונה לשדה מקונן עם כותרת מלאה עם נקודות כמו Applicant.FullName, אך XFDF אינו מציב את המחרוזת עם הנקודות במאפיין שם יחיד; ISO 19444 מקונן אותו, בתור <field name="Applicant"><field name="FullName">. losLab PDF Library מפצלת את הכותרת עם הנקודות לאלמנטים של <field> מקוננים בייצוא ומרכיבה מחדש את האלמנטים המקוננים בחזרה לכותרת המלאה בייבוא, כך ששני הכיוונים נשארים סימטריים. בייצוא היא גם מדלגת על שדות אב שאינם סופיים, מכיוון שצומת אב ב-AcroForm נושא רק את רמת השיוך ואין לו ערך משלו; פליטתו תייצר <value></value> ריק שאינו תואם לטופס האמיתי. תיבות רשימה עם בחירה מרובה הן המלכודת השנייה: שדה בחירה יכול להחזיק מספר ערכים נבחרים בו-זמנית, מה ש-XFDF מבטא כאלמנטים חוזרים של <value> ו-FDF מבטא כמערך /V, ו-losLab PDF Library מפצלת שדה למספר ערכים רק כאשר מדובר באמת בבחירה מרובה, כך ששדה טקסט רגיל מרובה שורות שומר על מעברי השורות שלו במקום להתפרק לערכים מדומים
טקסט עשיר הוא השלישי, והוא הנושא שאנשים טועים בו הכי הרבה. שדה עם עיצוב מאחסן את הקידוד שלו ברשומת RV כתת-עץ XHTML, ו-XFDF נושא אותו ב-<value-richtext>. losLab PDF Library כותבת את תת-העץ הזה כקטע XML חי במקום להחריג אותו, כך שכלי בהמשך הזרם קורא טקסט עשיר אמיתי במקום מחרוזת של תגיות גלויות; בייבוא היא שומרת על תת-העץ ה-RV הגולמי ועדיין מוחלת את ה-<value> הפשוט על V. כאשר שדה מציע את שניהם, הערך הפשוט מנצח עבור V והטקסט העשיר נלווה אליו ב-RV, וזהו כלל התאימות שמונע מייבוא טקסט עשיר לשכתב בשקט ערך שכלי אחר כבר הגדיר. כאשר הטקסט העשיר שלך קיים כדי להעביר את מבנה המסמך לטכנולוגיות מסייעות, התייחס אליו באותו אופן שבו היית מתייחס לסדר הקריאה הנדון בהערה על PDF מתויג ומבנה נגישות
var
Lib: TPDFlib;
const
XFDF =
'<?xml version="1.0" encoding="UTF-8"?>' +
'<xfdf xmlns="http://ns.adobe.com/xfdf/" xml:space="preserve">' +
'<fields>' +
' <field name="Applicant">' +
' <field name="FullName"><value>Alice Example</value></field>' +
' </field>' +
' <field name="Skills">' +
' <value>Delphi</value><value>PDF</value>' +
' </field>' +
' <field name="Notes">' +
' <value>See attachment</value>' +
' <value-richtext><body><p>See <b>attachment</b></p></body></value-richtext>' +
' </field>' +
'</fields></xfdf>';
begin
Lib := TPDFlib.Create;
try
Lib.LoadFromFile('intake.pdf', '');
// Applicant.FullName reassembles; Skills fills the /V + /I arrays;
// Notes gets V from <value> and RV from <value-richtext>
Lib.ImportFormDataFromXFDFString(XFDF);
Lib.SaveToFile('intake-filled.pdf');
finally
Lib.Free;
end;
end;
כיצד מעבירים הערות סקירה הלוך ושוב כ-FDF או XFDF?
הערות עוברות על משפחת ההערות (annotations), והתת-קבוצה הנתמכת היא קטנה בכוונה. ExportAnnotationsToXFDF ו-ImportAnnotationsFromXFDF, יחד עם עמיתיהן ב-FDF, נושאים סימונים בסגנון טקסט עם השדות שבאמת עוברים סבב מלא בצורה נקייה: תת-סוג ההערה, המלבן שלה, אינדקס העמוד שלה מבוסס 0, המחבר ב-T, הנושא ב-Subj, ה-Contents הפשוטים, והצבע, שממפה את השלשה PDF /C DeviceRGB אל ומתוך מאפיין XFDF #RRGGBB. ייבוא מקבל רק שמות אלמנטים ידועים ומדלג על כל רשומה שהעמוד שלה מחוץ לטווח או שהמלבן שלה חסר, כך ש-XFDF שנערך ידנית או זר אינו יכול להפיל הערה פגומה לתוך המסמך. מה שתת-הקבוצה עדיין אינה נושאת ראוי לציין בבירור: תוכן טקסט עשיר (RC), חלונות קופצים (popups), quadpoints, רשימות דיו, קודקודים ונתיבי תצוגה מוכנים (appearance streams) מחוץ לטווח בשלב זה, כך שגיאומטריית הדגשה ותצוגות חותמות מותאמות אישית לא ישרדו בנתיב זה. כדי לאשר מה באמת הגיע, עבור על הערות העמוד או על עץ האלמנטים הרחב יותר המכוסה ב-חיפוש טקסט ופירוט אלמנטים בעמוד
var
Src, Dest: TPDFlib;
Xfdf: WideString;
begin
Src := TPDFlib.Create;
Dest := TPDFlib.Create;
try
Src.LoadFromFile('reviewed.pdf', '');
Xfdf := Src.ExportAnnotationsToXFDFString; // <annots> subset only
Dest.LoadFromFile('clean-copy.pdf', ''); // pages must line up by index
Dest.ImportAnnotationsFromXFDFString(Xfdf);
Dest.SaveToFile('clean-copy-commented.pdf');
finally
Dest.Free;
Src.Free;
end;
end;
כתיבת חבילת XFA XDP מלאה עם SetXFAFromString
XFA הוא החריג, ו-SetXFAFromString היא הדרך שבה אתה כותב את כל העניין בבת אחת. השיטה מקבלת חבילת נתוני XML מלאה (XML Data Package), מסמך <xdp:xdp> עם חבילות התבניות וערכות הנתונים שלו, ומאחסנת אותו כזרם (stream) המופנה מתוך /AcroForm/XFA. losLab PDF Library יוצרת את מיכל ה-AcroForm לפי דרישה כאשר למסמך אין כזה, כך שאינך צריך להוסיף שדה זמני רק כדי לתת ל-XFA מקום להיתלות בו, והיא משליכה כל מצב פארסינג של XFA שהיא החזיקה כך שקריאה מאוחרת יותר תשקף את החבילה שזה עתה כתבת ולא מטמון ישן. מכיוון שחבילת XFA היא XML ולא בהכרח UTF-8, הספרייה מזהה סימן סדר בתים (BOM) של UTF-16 ומפענחת את החבילה בצורה נכונה לפני הניתוח, דבר החשוב לחבילות המיוצרות על ידי כלים המשתמשים בברירת מחדל של UTF-16
ברגע שהחבילה במקומה, GetXFAFormFieldValue ו-SetXFAFormFieldValue פונות לשדות בודדים באמצעות נתיב ה-Scripting Object Model (SOM) שלהם. losLab PDF Library מקבלת את שורשי ה-SOM הסטנדרטיים כמו גם נתיבים יחסיים פשוטים, כך ש-form1.FullName, $data.form1.FullName ו-xfa.datasets.data.form1.FullName כולם פותרים לאותו צומת נתונים, וצד התבנית מקבל את $template ו-xfa.template באותו אופן. סובלנות זו חשובה כאשר נתיבי ה-SOM מיוצרים על ידי מערכת אחרת שתמיד פולטת את הפורמט המלא
var
Lib: TPDFlib;
const
Xdp =
'<?xml version="1.0" encoding="UTF-8"?>' +
'<xdp:xdp xmlns:xdp="http://ns.adobe.com/xdp/">' +
' <template xmlns="http://www.xfa.org/schema/xfa-template/3.3/">' +
' <subform name="form1">' +
' <field name="FullName"><ui><textEdit/></ui></field>' +
' </subform>' +
' </template>' +
' <xfa:datasets xmlns:xfa="http://www.xfa.org/schema/xfa-data/1.0/">' +
' <xfa:data><form1><FullName>Alice Example</FullName></form1></xfa:data>' +
' </xfa:datasets>' +
'</xdp:xdp>';
begin
Lib := TPDFlib.Create;
try
Lib.SetXFAFromString(Xdp, 0); // creates the AcroForm container if none exists
// read back through the data DOM with an SOM path
if Lib.GetXFAFormFieldValue('form1.FullName') = 'Alice Example' then
Lib.SetXFAFormFieldValue('form1.FullName', 'Bob Example');
Lib.SaveToFile('xfa-packet.pdf');
finally
Lib.Free;
end;
end;
היכן שהדיוק מסתיים
דע את הגבולות לפני שאתה מתחייב לזרימת עבודה עם הגשרים האלה. מייצא ערכת הנתונים (dataset) משמיט עמודות בינאריות בשקט, ולכן דוח שחייב להציג תמונה משולבת זקוק לגישה שונה מאשר טבלה ישירה. נתיב QuickReport, מכיוון שהוא עובר דרך EMF, אופה טקסט לתוך גליפים וקטוריים — הדף נראה תקין אך אינו נושא טקסט הניתן לבחירה או לחיפוש וללא מבנה PDF מתויג עבור נגישות. FastReport ו-ReportBuilder שומרים על טקסט אמיתי, אך המטפלים המוגדרים מכסים את התצוגות הנפוצות — תזכיר (memo), תמונה, צורה, קו — ונסוגים לתיבה חוסמת (bounding box) או מדלגים על האקזוטיים שבהם, כך שדוח הנשען על טקסט עשיר, ברקודים או מילויי מעבר צבעים (gradient) יאבד פרטים. אף אחד מאלה אינו פגם; זהו הגבול הכנה של שכבת תרגום
אזהרה מבצעית אחת עולה בחשיבותה על השאר. אף אחד משלושת המנועים אינו מגיע בתוך losLab PDF Library, והגשרים מוחרגים בכוונה מהבנייה הראשית — הם מתקמפלים רק מפרויקט שכבר כולל את FastReport VCL 6.x, QuickReport 8, או ReportBuilder 20 בנתיב החיפוש שלו. משטחי ה-metafile וה-DrawCommand שהם מכוונים אליהם נותרו יציבים לאורך כמה גרסאות מנוע עיקריות, אך התאמת הגרסאות היא באחריותך. עשה זאת נכון ושני הנתיבים יכסו את כל הטווח, החל מתוצר DBGrid נקודתי ועד לחשבונית מעוצבת. מייצא הטבלאות וגשרי הדוחות מגיעים שניהם עם losLab PDF Library עבור דלפי ו-C++Builder