PDF Library for Delphi ממזגת שני מסמכי AcroForm עם מדיניות מפורשת עבור שדות החולקים שם. MergeDocumentEx מקבלת את מזהה מסמך המקור ואחת משלוש אסטרטגיות: dfsReject דוחה את המיזוג, dfsMerge שומרת את השם המשותף ומסנכרנת ערכים, ו-dfsAutoNumber משנה שם לשדות הנכנסים באופן דטרמיניסטי. סריקת השמות מתרחשת לפני שמספרי אובייקטים זזים, כך שמיזוג שנדחה משאיר את שני המסמכים שמישים במלואם
מי שהרכיב אי פעם חבילת יישום PDF נתקל בזה. שלושה טפסים, כל אחד עם שדה בשם Signature או Date או Total, ממוזגים לקובץ אחד. ב-AcroForm, שם השדה המלא-מוסמך הוא הזהות של השדה, כך ששני שדות עם אותו שם הם לא באמת שני שדות: מילוי אחד ממלא את השני, וחתימה שמוחלת על אחד מכסה היקף שאף אחד לא התכוון אליו
למה התנגשות השם מוכרעת לפני המיזוג?
ה-MergeDocument הישן משרשר את שני מערכי השדות השורשיים של AcroForm ולא מציע בחירה. גרוע מזה, כשהתוצאה בלתי-שמישה, הגילוי מתרחש אחרי שמספרי אובייקטים מוספרו מחדש ועצי עמודים נתפרו, מה שמשאיר את הקורא מחזיק מסמך במצב שאף אחד מהמקוריים לא היה בו
MergeDocumentEx הופכת את הסדר. היא אוספת את שמות השדות ברמה העליונה משני המסמכים, משווה אותם, ומיישמת את האסטרטגיה לפני שדבר זז. דחייה היא לכן no-op נקי: מסמך היעד ללא נגיעה, מסמך המקור ללא נגיעה, ושניהם נשארים פתוחים ושמישים, מה שבדיקת המיזוג מאמתת על ידי קריאת ערך שדה בחזרה מהמקור אחרי מיזוג שסורב
ההשוואה משתמשת בקבוצת שמות מסודרת ורגישה-רישיות, כך שהעלות פרופורציונלית לסכום ספירת השדות כפול גורם לוגריתמי במקום למכפלת שתי הספירות. רגישות רישיות היא הבחירה הנכונה כאן מכיוון ששמות שדות PDF רגישים לרישיות; קיפולם היה ממזג שדות שהתקן מתייחס אליהם כנבדלים
שלוש האסטרטגיות, ומתי כל אחת נכונה
dfsReject היא האסטרטגיה עבור צינורות עבודה אוטומטיים שאסור להם להפיק מסמכים דו-משמעיים. המיזוג מחזיר אפס ו-LastErrorCode מדווח 705, קוד ייעודי כך ששמות כפולים ניתנים להבחנה מכל כשל מיזוג אחר ולניתוב לתרופה ספציפית, בדרך כלל שינוי שם שדות במעלה הזרם
dfsMerge שומרת את השם המשותף במכוון ומסנכרנת את ערך היעד וערך ברירת המחדל לתוך שדה המקור, כך שצופה תואם מתייחס לכמה הוידג'טים כשדה יחיד בעל שם לוגי, שזוהי התנהגות AcroForm סטנדרטית עבור שדה עם כמה annotation וידג'ט. מה שהיא לא עושה הוא לקפל מילוני שדות שונים לתוך אובייקט יחיד. כל שדה שומר את שיוך העמוד, המראה והפעולות שלו, מכיוון שקיפולם היה משמיט בשקט עיצוב והתנהגות ששייכים למסמך הנכנס
dfsAutoNumber משנה שם לכפילויות נכנסות על ידי הוספת סיומת מספרית שמתחילה ב-_2 ולוקחת את הראשונה הפנויה. התוצאה ניתנת לשחזור: היא תלויה רק בשמות הנוכחים, לעולם לא במספרי אובייקטי שדה, כך שמיזוג אותו זוג מסמכים פעמיים מפיק את אותם שמות בשתי הפעמים. תכונה זו חשובה כשקוד במעלה הזרם, ייבוא FDF או מיפוי מסד נתונים מפנה לשדות לפי שם
uses
PDFlibrary;
var
Lib: TPDFlib;
TargetDoc, SourceDoc: Integer;
begin
Lib := TPDFlib.Create;
try
TargetDoc := Lib.SelectedDocument;
Lib.LoadFromFile('application-part1.pdf', '');
SourceDoc := Lib.NewDocument;
Lib.LoadFromFile('application-part2.pdf', '');
Lib.SelectDocument(TargetDoc);
if Lib.MergeDocumentEx(SourceDoc, dfsReject) = 0 then
begin
if Lib.LastErrorCode = 705 then
begin
// שני המסמכים עדיין שלמים - ניסיון חוזר עם מדיניות
Log('duplicate field names; retrying with auto-numbering');
Lib.MergeDocumentEx(SourceDoc, dfsAutoNumber);
end;
end;
Lib.SaveToFile('application-complete.pdf');
finally
Lib.Free;
end;
end;
שימו לב לתבנית הדו-שלבית בקוד הזה, שאפשרית רק מכיוון שדחייה אינה הרסנית. נסו את המדיניות המחמירה קודם, בדקו את השגיאה, ואז החליטו. עם מיזוג שנכשל באמצע, החלופה הייתה צריכה להתחיל מחדש על ידי טעינה מחדש של שני הקבצים
איך הטופס הממוזג נראה אחר כך
תחת dfsMerge, שדה יעד בשם Shared הנושא "Target value" ושדה מקור עם אותו שם מפיקים שני שדות, שניהם בשם Shared, ששניהם מדווחים את ערך היעד, מכיוון שערך היעד וערך ברירת המחדל מסונכרנים לתוך השדה הנכנס. זו הסמנטיקה המכוונת עבור שם משותף: שדה לוגי אחד, כמה וידג'טים, ערך אחד
תחת dfsAutoNumber, אותו קלט מפיק Shared ו-Shared_2 כשדות נפרדים עם ערכים עצמאיים. בחרו בין השניים על ידי שאלת שאלה אחת: האם מילוי אחד צריך למלא את השני? עבור שם חותם שחוזר על כל חלק בחבילה, כן, ו-dfsMerge נכונה. עבור סכום שאומר משהו שונה בכל טופס, לא, ושינוי שם אוטומטי נכון
// אחרי מיזוג, מנו את מה שבאמת קיבלתם
for I := 1 to Lib.FormFieldCount do
Log(Format('%d: %s = %s',
[I, Lib.GetFormFieldTitle(I), Lib.GetFormFieldValue(I)]));
הערות מעשיות להרכבת חבילות טפסים
מיזוג מוצלח צורך את מסמך המקור: הוא מוסר מרשימת המסמכים של הספרייה, וזו הסיבה ש-DocumentCount יורד משניים לאחד. אל תמשיכו להשתמש במזהה המקור אחר כך. גרסת המסמך מוגבהת לגבוהה מבין השתיים, כך שמיזוג טופס PDF 2.0 לתוך מסמך 1.7 מפיק קובץ 2.0
סדר חשוב לשמות. מיזוג A לתוך B ומיזוג B לתוך A מפיקים תוצאות שינוי-שם-אוטומטי שונות, מכיוון שהמסמך שמבצע את המיזוג שומר את שמותיו ללא שינוי. כשלחבילה יש טופס ראשי קנוני, הפכו אותו ליעד
שדות חתימה ראויים לשיקול משלהם. חתימה שהוחלה לפני מיזוג מכסה רק את הגרסה שעליה חתמה, כך שמיזוג מבטל אותה במובן המעשי שהקובץ השתנה מאז החתימה. הרכיבו קודם וחתמו על המסמך המורכב, במקום למזג חלקים חתומים. כשהמיזוג נוגע לתוכן עמודים ולא לטפסים, הנתיב המהיר יותר המתואר במיזוג PDF מהיר עם הזזת הפניות בייטים הוא הכלי הטוב יותר
לבסוף, תכננו את צד הנתונים של החבילה יחד עם המיזוג. אם ערכי שדות מגיעים ממערכת חיצונית, החליטו אם המערכת הזו מפנה לשדות לפי שם לפני שבוחרים בשינוי שם אוטומטי, מכיוון ש-Shared_2 לא יתאים למיפוי שמצפה ל-Shared. פורמטי ייבוא וייצוא מכוסים בחליפין נתוני טופס FDF, XFDF ו-XFA, והתנהגות סקריפטים ברמת שדה שגם עלולה להיות מושפעת משינוי שם מכוסה בפעולות טופס אינטראקטיביות וג'אווהסקריפט
מיזוג טפסים, חליפין נתונים וחתימה רצים באותה ספרייה לדלפי, C++Builder ו-Free Pascal; רשימת התכונות המלאה נמצאת בעמוד PDF Library for Delphi