מאמר טכני

ערוך מטא נתונים של PDF שנטען בדלפי ללא כתיבה מחדש

יש לך עשרת אלפים קובצי PDF של חוזים מתריסר מחוללים שונים, והחוק רוצה שכל אחד מהם ישא את הזכות Author, א מתוקן Producer מחרוזת, ומצב קריאה שפותח את חלונית הסימניות בעת ההשקה.

הצעד הנכון הוא להתייחס למסמך הנטען כאל גרף אובייקטים שאתה משנה במקום: הכנס למילון המידע, /Metadata זרם, והקטלוג, שנה את הערכים המעטים שחשובים לך, וכתוב את התוצאה בחזרה.

שני מקומות מאחסנים את אותם מטא נתונים, והם לא מסכימים

PDF נושא מידע על מסמך בשני מיקומים מקבילים, וזהו השורש של רוב כרטיסי "שיניתי את הכותרת אבל Acrobat עדיין מציגה את הישן". /Info חפץ עם /Title, /Author, /Subject, /Keywords, /Creator, ו /Producer מפתחות, המוגדרים ב-ISO 32000-1 §14.3.3. /Metadata, המוגדר ב-§14.3.2 ובנוי על מודל הנתונים של Adobe XMP

שניהם יכולים להחזיק בתואר. /Info - וזה מה שהרוב הגדול של קוד "הגדר מטא נתונים של PDF" עושה - קורא שסומך על XMP ימשיך להראות את הערך המעופש, ובודק PDF/A יסמן את אי ההתאמה. ו ליצור מחדש את ה-XMP, כך שהשניים יישארו עקביים.

עריכת מילון המידע

העוזרים בצד המידע הם רזים וצפויים SetLoadedTitle, SetLoadedAuthor, SetLoadedSubject, SetLoadedKeywords, SetLoadedCreator, ו SetLoadedProducer כל אחד לוקח סינגל AnsiString וכתוב את המפתח המתאים למילון המידע הנטען, החלף את הערך אם המפתח קיים והוסף אותו אם לא. /Creator זה נותן שמות לכלי הפנימי שלך - התקשר RemoveLoadedInfoKey עם שם המפתח החשוף. /Info להתנגד לכך LoadFromFile נמצא כאשר הוא ניתוח את הקובץ

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    if Pdf.LoadFromFile('contract-in.pdf', '') > 0 then
    begin
      Pdf.SetLoadedTitle('Master Services Agreement 2026');
      Pdf.SetLoadedAuthor('Legal Department');
      Pdf.SetLoadedSubject('Executed contract, retention 7 years');
      Pdf.SetLoadedKeywords('contract; MSA; 2026; executed');
      Pdf.SetLoadedProducer('Acme Document Pipeline');
      Pdf.RemoveLoadedInfoKey('Creator');  // drop the originating tool name
      Pdf.SaveLoadedDocument('contract-out.pdf');
    end;
  finally
    Pdf.Free;
  end;
end;

פרט אחד למען האמת: אלה לוקחים AnsiString.

שכתוב חבילת XMP

SetLoadedXMPMetadata הוא החצי השני של הכתיבה הכפולה. AnsiString והוא עושה אחד משני דברים: אם הקטלוג כבר מפנה א /Metadata stream, הוא מחליף את התוכן של הזרם הזה במקום, תוך שמירה על אותו מספר אובייקט; /Type /Metadata ו /Subtype /XML, מקצה מספר אובייקט ומקשר אותו מהקטלוג.

אתה מספק את ה-XML, כלומר אתה שולט בסכימה - dc:title, dc:creator, xmp:CreatorTool, וכן הלאה.

const
  XMP_TEMPLATE =
    '<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?>' +
    '<x:xmpmeta xmlns:x="adobe:ns:meta/">' +
    '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">' +
    '<rdf:Description rdf:about="" xmlns:dc="http://purl.org/dc/elements/1.1/">' +
    '<dc:title><rdf:Alt><rdf:li xml:lang="x-default">%s</rdf:li></rdf:Alt></dc:title>' +
    '<dc:creator><rdf:Seq><rdf:li>%s</rdf:li></rdf:Seq></dc:creator>' +
    '</rdf:Description></rdf:RDF></x:xmpmeta><?xpacket end="w"?>';
begin
  // After setting the Info dictionary, mirror the same values into XMP:
  Pdf.SetLoadedTitle('Master Services Agreement 2026');
  Pdf.SetLoadedAuthor('Legal Department');
  Pdf.SetLoadedXMPMetadata(
    AnsiString(Format(XMP_TEMPLATE,
      ['Master Services Agreement 2026', 'Legal Department'])));
  Pdf.SaveLoadedDocument('contract-out.pdf');
end;

הסדר הזה - מידע ראשון, XMP שני, ואז שמור - הוא הדפוס להפנים.

Diagram showing a PDF Info dictionary and an XMP metadata stream both holding title and author, edited in place alongside the bookmark outline tree
מטא נתונים חיים בשני מקומות - מילון המידע וזרם ה-XMP - בתוספת רמזי הקריאה ברמת הקטלוג ועץ המתאר.

כיוון כיצד הצופה פותח את הקובץ

שלוש ערכי קטלוג מחליטים מה הקורא רואה ברגע שהמסמך נפתח, ושלושתם עריכות בשורה אחת בגרף הנטען SetLoadedPageMode כותב /PageMode כמו חפץ שם: לעבור 'UseOutlines' כדי לפתוח את חלונית הסימניות, 'UseThumbs' עבור מסילת התמונות הממוזערות, 'FullScreen' עבור מצב מצגת, או 'UseAttachments' כדי להציג את חלונית הקבצים המצורפים (ISO 32000-1 §7.7.3.1, טבלה 28) SetLoadedPageLayout כותב /PageLayout באותו אופן - 'SinglePage', 'OneColumn', 'TwoColumnLeft', וכל השאר.

SetLoadedLanguage כותב הקטלוג /Lang ערך, תג השפה הטבעית עבור המסמך כולו - 'en-US', 'de-DE', תג BCP 47. /PageMode ו /PageLayout הם PDF שֵׁם חפצים, תוך כדי /Lang הוא א חוּט. /PageMode /UseOutlines נֶגֶד /Lang (en-US), ועכשיו אתה יודע למה. /Lang הערך חשוב יותר ממה שהוא נראה: זה מה שטכנולוגיה מסייעת קוראת כדי לבחור הגייה, וזו דרישה קשה להתאמה לנגישות PDF/UA

if Pdf.LoadFromFile('handbook.pdf', '') > 0 then
begin
  Pdf.SetLoadedPageMode('UseOutlines');     // /PageMode, a name
  Pdf.SetLoadedPageLayout('TwoColumnLeft'); // /PageLayout, a name
  Pdf.SetLoadedLanguage('en-US');           // /Lang, a string
  Pdf.SaveLoadedDocument('handbook-tagged.pdf');
end;

שינוי שמות של סימניות מבלי להפריע לעץ

כותרות של סימניות הן ניקוי שגרתי - שגיאת הקלדה בכותרת, פרק ממוספר מחדש לאחר בניית המתווה SetLoadedOutlineTitle לוקח אינדקס מבוסס אפס לתוך ברמה העליונה ערכי מתאר וכותרת חדשה, עובר בקטלוג → /Outlines/First/Next שרשרת לעמדה זו, ומחליף את הערך /Title חוּט.

if Pdf.LoadFromFile('report.pdf', '') > 0 then
begin
  Pdf.SetLoadedOutlineTitle(0, 'Executive Summary');
  Pdf.SetLoadedOutlineTitle(1, 'Financial Results');
  Pdf.SaveLoadedDocument('report-renamed.pdf');
end;

שינוי השם בטוח בדיוק מכיוון שהוא אף פעם לא נוגע במונים המבניים. לֹא לעריכה ידנית. /Count, וכן - לפי ISO 32000-1 §12.3.3 - הספירה הזו אינה מספר הילדים המיידיים. צאצאים גלויים: חיובי /Count של N פירושו ש- N צאצאים נחשפים כעת, בעוד שערך שלילי פירושו שלצומת יש צאצאים אך הוא ממוטט. /Outlines לא ניתן פשוט להפחית את ספירת השורשים באחד; /Count"מדלג על צאצאיו של כל צומת ממוטט (ספירה שלילית). תטעה וסך הסימניות שקורא מציג דריפטים - הוא קופץ ביותר מאחד לכל מחיקה. שינוי שם עוקף את כל זה, וזו עוד סיבה להעדיף את העוזר הממוקד על פני חיטוט במילון בעצמך

איך השמירה נשארת במקום

כל עריכה למעלה משנה אובייקטים בזיכרון; SaveLoadedDocument פועל. זרמי אובייקט ועדכונים מצטברים עֲבוֹדָה. זרמי הפניות מוצלבות עם הפניות היברידיות בקובצי PDF של Office מכסה כיצד קבצים אלה בנויים ומה שורד נסיעה הלוך ושוב

שני גבולות לכבד.

ממשק ה-API לכתובת מסמך טעון שמוצג כאן מגיע עם התקן רכיב HotPDF עבור Delphi ו-C++ Builder, לצד הסט המלא של שיטות עריכת מטא נתונים, מתאר וקטלוגים