בעת פיתוח פתרונות מסמכים ארגוניים, בהכרח תיתקלו בקבצי PDF שנוצרו על ידי מגוון עצום של כלים, החל מתוכנות Adobe מתקדמות ועד לספריות קוד פתוח עם באגים או מנהלי התקן של מדפסות וירטואליות. אחת הבעיות המבניות הידועות לשמצה ביותר שתתמודדו איתן היא PDF בעל "הפניות היברידיות" (hybrid-reference).
במאמר זה, נסביר מהן הפניות היברידיות, מדוע יישומי משרד מסוימים יוצרים אותן, וכיצד לנתח ולתקן מבנים אלה באופן תכנותי באמצעות Delphi וספריות PDF רובסטיות.
האבולוציה של טבלת ההפניות הצולבות (Cross-Reference) ב-PDF
כדי לאתר אובייקטים (גופנים, תמונות, דפים) במהירות מבלי לנתח את כל הקובץ, PDF משתמש בטבלת הפניות צולבות (XRef). במפרטי PDF מוקדמים (PDF 1.4 וישנים יותר), זו הייתה טבלת טקסט ASCII מילולית בסוף הקובץ.
החל מ-PDF 1.5, חברת Adobe הציגה את Cross-Reference Streams (XRefStm), שדחסו את נתוני ההפניות הצולבות לזרם בינארי והקטינו משמעותית את גודל הקבצים. עם זאת, לצורך תאימות לאחור עם קוראי PDF ישנים, מחוללים מסוימים החלו ליצור קבצי PDF בעלי הפניות היברידיות (Hybrid-Reference PDFs). קבצים אלה מכילים גם טבלת XRef ישנה מבוססת ASCII וגם זרם XRef מודרני.
הבעיה עם הפניות היברידיות
קבצים היברידיים הם תקפים מבחינה תאורטית, אך מחוללי PDF רבים (במיוחד תוספי "שמירה כ-PDF" מדור קודם בחבילות Office ישנות) כותבים אותם בצורה שגויה. באג נפוץ הוא כתיבת היסט בתים שגוי עבור המצביע startxref, או יצירת זרמי אובייקטים מנותקים שבהם טבלת ה-XRef מצביעה למספר דור שגוי.
אם יישום ה-Delphi שלכם מנסה לקרוא קובץ PDF היברידי פגום באמצעות מנתח קפדני, המנתח ייכשל עם חריגת "Corrupt XRef table" או "Invalid Object Number".
טיפול בגיבויי מערכת היברידיים עם PDFium
מנוע ה-PDFium (שפותח במקור על ידי Foxit ושוחרר כקוד פתוח על ידי Google) סובלני מאוד לקבצי PDF פגומים. כאשר הוא מזהה טבלת XRef שבורה, הוא סורק אוטומטית את הקובץ לאחור מ-EOF כדי לאתר את ה-XRefStm החלופי.
ב-Delphi, בעת עבודה עם PDFium, אינכם צריכים לנתח ידנית את מילוני ה-trailer. עם זאת, כדאי לבדוק אזהרות מבניות כדי שתוכלו להתריע בפני המשתמש או לרשום את הבעיה ביומן.
uses
System.SysUtils, pdfium_lib;
procedure LoadAndCheckHybridPDF(const FileName: string);
var
Doc: FPDF_DOCUMENT;
LastError: ULONG;
begin
FPDF_InitLibrary();
try
Doc := FPDF_LoadDocument(PAnsiChar(AnsiString(FileName)), nil);
if Doc = nil then
begin
LastError := FPDF_GetLastError();
case LastError of
FPDF_ERR_FILE: Writeln('File not found or could not be opened.');
FPDF_ERR_FORMAT: Writeln('File not in PDF format or corrupted.');
FPDF_ERR_PASSWORD: Writeln('Password required or incorrect password.');
FPDF_ERR_SECURITY: Writeln('Unsupported security scheme.');
FPDF_ERR_XFDF: Writeln('Invalid XRef or Hybrid Reference structure.');
else
Writeln('Unknown error occurred loading PDF.');
end;
Exit;
end;
Writeln('PDF loaded successfully despite hybrid or structural anomalies.');
// Proceed with processing...
FPDF_CloseDocument(Doc);
finally
FPDF_DestroyLibrary();
end;
end;
תיקון ובנייה מחדש של ה-PDF
אם זרימת העבודה שלכם דורשת העברת ה-PDF למערכת קפדנית יותר בהמשך הדרך (כמו חומרת RIP ישנה), עליכם "לשטח" את המבנה ההיברידי. הדרך האמינה ביותר לתקן קובץ PDF היברידי שבור ב-Delphi היא לטעון אותו לתוך מנוע סובלני ולבצע פעולת שמירה בשם (Save-As). פעולה זו מאלצת את המנתח לבנות מחדש טבלת XRef נקייה ומאוחדת מעץ האובייקטים שבזיכרון.
// Conceptual example using a high-level wrapper
procedure RebuildPdfStructure(const InputFile, OutputFile: string);
var
Doc: TlxPDFDocument;
begin
Doc := TlxPDFDocument.Create;
try
// Tolerant engine ignores the broken hybrid XRef and walks the objects
Doc.LoadFromFile(InputFile);
// Saving rewrites the file with a clean PDF 1.7 XRef stream
Doc.SaveToFile(OutputFile);
Writeln('PDF structure successfully rebuilt.');
finally
Doc.Free;
end;
end;
הבנה וצפייה מראש של שחיתות בהפניות היברידיות מבטיחות שצינורות עיבוד המסמכים שלכם יישארו עמידים, גם בעת התמודדות עם קבצי מורשת בני עשרות שנים.
הערה: פתרון הפניות היברידיות ותיקונים מבניים אוטומטיים נתמכים באופן מלא על ידי PDFium Component.