יש לך עשרת אלפים קובצי 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 שני, ואז שמור - הוא הדפוס להפנים.

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