Bir düzine farklı üreticiden gelen on bin sözleşme PDF'iniz var ve hukuk departmanı her birinin doğru Author (Yazar) bilgisini, düzeltilmiş bir Producer (Üretici) dizesini ve açılışta yer imi panelini açan bir okuma modunu taşımasını istiyor. Saf yaklaşım, her dosyayı yüklemek, sayfaları yeniden düzenlemek ve yeni bir belge yazmaktır. Bunu yaparsanız, mevcut her nesne numarasını, artımlı güncelleme (incremental-update) geçmişini, dijital imzaları ve orijinal aracın yaydığı özenle ayarlanmış xref tablosunu çöpe atmış olursunuz. Sayfalar aynı görünür ancak dosya yapısal olarak bir yabancıdır. Bir meta veri düzenlemesi için bu tamamen yanlış bir ticarettir
Doğru hareket, yüklü belgeyi yerinde değiştirdiğiniz bir nesne grafiği gibi ele almaktır: Info sözlüğüne, /Metadata akışına ve Kataloğa (Catalog) erişin, önemsediğiniz birkaç girişi değiştirin ve sonucu geri yazın. Delphi ve C++Builder için yerel VCL PDF bileşeni olan HotPDF, yüklü belge yazma API'si aracılığıyla tam olarak bu yüzeyi sunar. Bu makale bunu doğru şekilde kullanmak ve neredeyse herkesin yaptığı tek bir hata hakkındadır: Info sözlüğünü düzenlemek ve aynı meta verinin ikinci bir kopyasının XMP içinde yaşadığını unutmak
Aynı meta veriyi iki yer saklar ve birbiriyle çelişir
PDF, belge bilgilerini iki paralel konumda taşır ve bu, çoğu "başlığı değiştirdim ama Acrobat hala eskisini gösteriyor" destek taleplerinin temelidir. İlki, ISO 32000-1 §14.3.3'te tanımlanan /Title, /Author, /Subject, /Keywords, /Creator ve /Producer anahtarlarına sahip klasik /Info nesnesi olan belge bilgi sözlüğüdür. İkincisi ise §14.3.2'de tanımlanan ve Adobe XMP veri modeli üzerine inşa edilen, /Metadata altında Kataloğun dışında bir akış olarak saklanan bir XML belgesi olan XMP paketidir
Her ikisi de bir başlık tutabilir. Spesifikasyonda hiçbir şey onları aynı fikirde olmaya zorlamaz. Modern görüntüleyiciler ve çoğu PDF/A doğrulayıcı, mevcut olduğunda XMP paketini tercih eder ve olmadığında Info sözlüğüne geri döner. Bu nedenle, yalnızca /Info'yu güncellerseniz (ki "PDF meta verilerini ayarla" kodlarının büyük çoğunluğu bunu yapar), XMP'ye güvenen bir okuyucu eski değeri göstermeye devam edecek ve bir PDF/A denetleyicisi uyumsuzluğu işaretleyecektir. İkisinin tutarlı kalması için Info girişini değiştirin ve XMP'yi yeniden oluşturun. HotPDF size her iki yarıyı da sunar; bunları birlikte kullanma disiplini size aittir
Info sözlüğünü düzenleme
Info tarafı yardımcıları ince ve öngörülebilirdir. SetLoadedTitle, SetLoadedAuthor, SetLoadedSubject, SetLoadedKeywords, SetLoadedCreator ve SetLoadedProducer işlevlerinin her biri tek bir AnsiString alır ve anahtar mevcutsa değeri değiştirerek, yoksa ekleyerek ilgili anahtarı yüklü Info sözlüğüne yazar. Bir anahtarı tamamen kaldırmak için (örneğin dahili araçlarınızı adlandıran sızdıran bir /Creator), yalın anahtar adıyla RemoveLoadedInfoKey çağrısı yapın. Bunların hiçbiri XMP'ye dokunmaz; tamamen LoadFromFile'ın dosyayı ayrıştırırken bulduğu /Info nesnesi üzerinde çalışırlar
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'); // kaynak araç adını bırakın
Pdf.SaveLoadedDocument('contract-out.pdf');
end;
finally
Pdf.Free;
end;
end;
Dürüst olunması gereken bir ayrıntı: Bunlar AnsiString alır. ASCII başlıklar için bu sorun değildir, ancak Latin olmayan karakterlere ihtiyaç duyan PDF metin dizgeleri, teslim etmeden önce spesifikasyonun gerektirdiği şekilde (bayt sırası işaretli UTF-16BE veya PDFDocEncoding) kodlanmalıdır. Kütüphane verdiğiniz baytları bir dize nesnesine yazar; sizin için bir kodlama tahmini yapmaz. Başlıklarınız düz İngilizce ise bunu göz ardı edin. Aksanlı veya CJK karakterler taşıyorlarsa, bilerek kodlayın ve gerçek bir görüntüleyicide test edin
XMP paketini yeniden yazma
SetLoadedXMPMetadata çift yazmanın diğer yarısıdır. Tam XMP paketini AnsiString olarak geçirin ve iki şeyden birini yapar: Katalog zaten bir /Metadata akışına başvuruyorsa, aynı nesne numarasını koruyarak o akışın içeriğini yerinde değiştirir; meta veri akışı yoksa bir tane oluşturur, bunu /Type /Metadata ve /Subtype /XML olarak işaretler, bir nesne numarası tahsis eder ve Katalogdan bağlar. Her iki durumda da görüntüleyicilerin okuyacağı geçerli bir meta veri nesnesi elde edersiniz
XML'i siz sağlarsınız, bu da şemayı (dc:title, dc:creator, xmp:CreatorTool vb.) kontrol ettiğiniz anlamına gelir. Bu aynı anda hem güç hem de sorumluluktur: Kütüphane paketinizi ayrıştırmaz veya doğrulamaz ve uygulanan herhangi bir akış filtresi olmaksızın baytları sıkıştırılmamış olarak yazar. Hatalı biçimlendirilmiş bir paket çağrıdan geçer ve daha sonra bozuk meta veri şikayeti olarak ortaya çıkar. XML'i dikkatlice oluşturun ve iki görünümün birbiriyle asla çelişmemesi için Info sözlüğüne yazdığınız değerleri tam olarak yansıtın
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
// Info sözlüğünü ayarladıktan sonra aynı değerleri XMP'ye yansıtın:
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;
Önce Info, sonra XMP, ardından kaydet şeklindeki bu sıralama içselleştirilmesi gereken modeldir. İki çağrı birbirinden bağımsızdır; tutarlılık yalnızca onlara aynı dizeleri beslediğiniz için mevcuttur. XMP paketine sahip bir dosyada XMP çağrısını atlarsanız, bu bölümün tamamen önlemek için var olduğu sessiz eskime hatasına geri dönersiniz

Görüntüleyicinin dosyayı nasıl açacağını yönlendirme
Üç Katalog girişi, belgenin açıldığı anda okuyucunun ne göreceğine karar verir ve üçü de yüklü grafik üzerinde tek satırlık düzenlemelerdir. SetLoadedPageMode, /PageMode değerini bir ad nesnesi olarak yazar: Yer imi panelini açmak için 'UseOutlines', küçük resim rayı için 'UseThumbs', sunum modu için 'FullScreen' veya ekler panelini göstermek için 'UseAttachments' geçirin (ISO 32000-1 §7.7.3.1, Tablo 28). SetLoadedPageLayout, /PageLayout değerini aynı şekilde yazar ('SinglePage', 'OneColumn', 'TwoColumnLeft' ve diğerleri). Her ikisi de adı önde eğik çizgi olmadan alır; kütüphane bunu çıktıda ekler
SetLoadedLanguage, belgenin bütünü için doğal dil etiketi olan Katalog /Lang girişini yazar ('en-US', 'de-DE', bir BCP 47 etiketi). İnsanları tökezleten tip farkına dikkat edin: /PageMode ve /PageLayout PDF ad (name) nesneleriyken, /Lang bir dize (string) nesnesidir. HotPDF bunu dahili olarak doğru yapar, ancak çıktıyı incelerseniz /Lang (en-US) karşısında /PageMode /UseOutlines göreceksiniz ve artık nedenini biliyorsunuz. /Lang girişi göründüğünden daha önemlidir: Yardımcı teknolojilerin telaffuz seçmek için okuduğu şeydir ve PDF/UA erişilebilirlik uygunluğu için katı bir gerekliliktir
if Pdf.LoadFromFile('handbook.pdf', '') > 0 then
begin
Pdf.SetLoadedPageMode('UseOutlines'); // /PageMode, bir ad
Pdf.SetLoadedPageLayout('TwoColumnLeft'); // /PageLayout, bir ad
Pdf.SetLoadedLanguage('en-US'); // /Lang, bir dize
Pdf.SaveLoadedDocument('handbook-tagged.pdf');
end;
Ağacı bozmadan yer imlerini yeniden adlandırma
Yer imi başlıkları rutin temizliktir; başlıktaki bir yazım hatası, anahat oluşturulduktan sonra yeniden numaralandırılan bir bölüm. SetLoadedOutlineTitle, üst düzey anahat girişlerine sıfır tabanlı bir dizin ve yeni bir başlık alır, Katalog → /Outlines → /First → /Next zincirini o konuma kadar yürür ve girişin /Title dizesini değiştirir. Yalnızca başlığı değiştirir; hedef, açık/kapalı durum ve çocuk yapısı bozulmaz
if Pdf.LoadFromFile('report.pdf', '') > 0 then
begin
Pdf.SetLoadedOutlineTitle(0, 'Executive Summary');
Pdf.SetLoadedOutlineTitle(1, 'Financial Results');
Pdf.SaveLoadedDocument('report-renamed.pdf');
end;
Yeniden adlandırma güvenlidir çünkü yapısal sayaçlara asla dokunmaz. Anahat girişini silmek can sıkan durumdur ve sadece yeniden adlandırıyor olsanız bile bunu anlamak değerlidir; çünkü size neyi elinizle düzenlememeniz gerektiğini söyler. Her anahat düğümü bir /Count taşır ve ISO 32000-1 §12.3.3 uyarınca bu sayı doğrudan çocukların sayısı değildir. Toplam görünür alt nesne (visible descendants) sayısıdır: N'lik pozitif bir /Count değeri, şu anda N alt nesnenin açık olduğunu gösterirken, negatif bir değer düğümün alt nesneleri olduğunu ancak daraltıldığını gösterir. Üst düzey bir giriş kaldırıldığında, /Outlines kök sayısı basitçe bir azaltılamaz; daraltılmış (negatif sayımlı) herhangi bir düğümün alt nesnelerini atlayarak, hayatta kalan her üst düzey düğüm üzerinden "düğümün kendisi için bir artı pozitif /Count" toplanarak yeniden hesaplanmalıdır. Bunu yanlış yaparsanız, okuyucunun görüntülediği yer imi toplamı sapar (silme başına birden fazla atlar). Yeniden adlandırma tüm bunları atlar; bu da sözlüğü kendiniz kurcalamak yerine hedefli yardımcıyı tercih etmek için bir başka nedendir
Kaydetme işleminin nasıl yerinde kaldığı
Yukarıdaki her düzenleme bellekteki nesneleri değiştirir; SaveLoadedDocument çalışana kadar hiçbir şey diske ulaşmaz. Bu yaklaşımın ucuz olmasının nedeni, kaydetmenin belgeyi yeniden oluşturmamasıdır; yükleme sırasında HotPDF'in ayrıştırdığı mevcut nesne numaralarını ve yapıyı korur, değiştirdiğiniz ve yeni ayrılan birkaç nesneyle aynı grafiği geri yazar. Meta veri geçişinin tüm dosyayı yeniden yazmasını engelleyen şey budur ve nesne akışları ile artımlı güncellemelerin çalışmasını sağlayan yerinde güncelleme mekanizmasının aynısıdır. Kaynak dosyalarınız Word veya başka bir ofis paketinden çıkıyorsa, nesne düzenlerinin düzenlemeden önce bilinmeye değer kendi tuhaflıkları vardır; Ofis PDF'lerindeki hibrit referanslı çapraz referans akışları hakkındaki makale, bu dosyaların nasıl yapılandırıldığını ve bir gidiş-dönüşte nelerin hayatta kaldığını kapsar
Saygı gösterilmesi gereken iki sınır var. İlk olarak, bu yerinde düzenleme modelidir, bir karartma (redaction) veya temizleme aracı değildir: Bir Info anahtarını kaldırmak o anahtarı kaldırır, ancak aynı dosyanın önceki bir artımlı güncelleme neslinde kalabilecek eski değerleri temizlemez. Gereksiniminiz hassas meta verilerin gerçek anlamda kaldırılmasıysa, bu farklı ve daha ağır bir işlemdir. İkinci olarak, XMP yazımı kelimesi kelimesinedir; kütüphane XML'inize güvenir ve onu doğrulamaz, bu nedenle PDF/A veya katı bir doğrulayıcıya yönelik her şey için paketi bilinen iyi bir şablondan oluşturun ve çıktıyı doğrulayın. Bu sınırlar dahilinde kullanıldığında, yerinde meta veri düzenleme tam boyutlu araçtır: Yanlış olan birkaç baytı düzeltir ve dosyanın zaten doğru olan yüzde doksan dokuzunu tam olarak orijinal üreticinin yazdığı gibi bırakır
Burada gösterilen yüklü belge yazma API'si; meta veri, anahat ve Katalog düzenleme yöntemlerinin eksiksiz setiyle birlikte Delphi ve C++Builder için standart HotPDF Bileşeni ile birlikte gönderilir