losLab PDF Library, SetDeterministicDocumentID(1) çağırdığınızda aynı girdi için byte özdeş PDF çıktısı üretebilir. Varsayılan olarak trailer /ID dizisi duvar saatinin bir MD5 özeti olduğundan, aynı üreticinin iki çalıştırması en azından bu baytlarda farklılaşır. Deterministik mod ise /ID'yi bunun yerine sabit bir tohumdan türetir, bu da tekrarlanabilir derlemeleri geri getirir
Belirti genellikle kimse aramaya başlamadan önce CI'da ortaya çıkar. Şablon değişmemiştir, girdi kaydı değişmemiştir, fontlar değişmemiştir ve üretilen PDF hâlâ her pipeline çalıştırmasında farklı hash üretir. Derleme önbellekleri hiç isabet etmez. İçerik adresli depolama her gecelik derleme için taze bir blob biriktirir. Byte seviyesinde regresyon diffleri kimsenin dokunmadığı dosyalarda yanıp söner. Diff'i gerçek baytlara kadar takip edin, neredeyse her zaman dosya trailer'ında oturan aynı birkaç hex hanesidir
Trailer ID dizisi ne işe yarar?
Trailer /ID, içeriğin bir sağlama toplamı değil, bir dosya kimlik işaretçisidir. ISO 32000-1 §14.4 bunu iki byte dizesinden oluşan bir dizi olarak tanımlar: ilk eleman, belge oluşturulduğunda atanan ve sonraki her düzenlemeden hayatta kalması amaçlanan kalıcı tanımlayıcıdır, ikinci eleman ise bir yazıcının dosya her değiştirildiğinde yenilediği değişen tanımlayıcıdır. Birlikte, bir sistemin iki dosyanın bir belgenin revizyonları mı yoksa iki alakasız belge mi olduğuna karar vermesini sağlarlar. §7.5.5, trailer /Encrypt taşıdığında /ID'yi de taşıması gerektiğinden, girişi pratikte etkin biçimde zorunlu kılar
Spesifikasyonda değerin nasıl hesaplanacağına dair hiçbir şey yazmaz. Öneri, geçerli zaman, dosya yolu, dosya boyutu ve belge bilgi sözlüğü gibi şeylerin bir özetidir ve sonucu benzersiz yapan bileşen duvar saatidir. Bu, kimlik için tam olarak istediğiniz özelliktir ve tam olarak tekrarlanabilirliği yok eden özelliktir, bu yüzden bunun sessiz bir davranış değişikliği yerine açık bir anahtar olması gerekir
Aynı derleme neden her seferinde farklı bir PDF üretiyor?
Çünkü varsayılan tanımlayıcı üretim anından türetilir. Tarihsel olarak losLab PDF Library, /ID dizelerini geçerli zaman damgasının bir MD5'inden inşa ediyordu, bu yüzden bir saniye arayla iki kez oluşturulan bir belge, dosyadaki diğer her byte özdeş olsa bile iki farklı kalıcı tanımlayıcı taşır. Sonraki maliyet gerçektir: eserleri hash ile anahtarlayan bir derleme sistemi bir PDF adımını asla yeniden kullanamaz, tekilleştiren bir nesne deposu belge başına bir kopya yerine derleme başına bir kopya tutar ve bir ikili diff'e bakan bir gözden geçirici, diff'in geri kalanına güvenmeden önce tek değişikliğin gürültü olduğunu kanıtlamak zorunda kalır. Deterministik /ID üretimi, nesne akışları ve çapraz referans akışları notlarında anlatılan düzen kararlılığı çalışmasıyla aynı ruhla, bu gürültüyü gidermek için vardır
Tekrarlanabilir bir tanımlayıcıya geçiş
Deterministik mod, belge başına opt-in'dir ve varsayılan olarak kapalıdır, dolayısıyla siz istemeden mevcut çıktı değişmeden kalır. SetDeterministicDocumentID, 0 veya 1 kabul eder ve değer kabul edildiğinde 1, aralık dışındaki her şey için 0 döndürür; GetDeterministicDocumentID geçerli durumu bildirir. SetDocumentIDSeed, her şeyin önüne geçen açık bir tohum dizesi sağlar ve boş bir tohum geçmek türetilmiş tohuma geri döndürür. GetDocumentFileID, kaydetmeden sonra /ID[0]'ı geri okur, böylece onu loglayabilir veya üzerinde doğrulama yapabilirsiniz
var
Lib: TPDFlib;
FileID: WideString;
begin
Lib := TPDFlib.Create;
try
Lib.SetDeterministicDocumentID(1);
Lib.SetDocumentIDSeed('invoice-4471-rev3');
Lib.SetOrigin(1);
Lib.DrawText(100, 700, 'Invoice 4471');
Lib.SaveToFile('invoice.pdf');
FileID := Lib.GetDocumentFileID; // identical on every run
finally
Lib.Free;
end;
end;
Yenileme, bayrağı çevirdiğiniz anda değil kaydetme anında gerçekleşir, dolayısıyla deterministik modu bir belge derlemesinin geç bir aşamasında etkinleştirmek yine de etkili olur. Bu aynı zamanda değiştirilmiş bir tohumun bir sonraki tam kaydetmede dosyaya ulaşacağı anlamına gelir: tohum A'yı ayarlayın, kaydedin, tohum B'yi ayarlayın, kaydedin, ve iki dosya farklı tanımlayıcılar taşır, tohum A'yı geri yüklemek ise orijinal değeri geri getirir. Belgenizin fatura numarası, kayıt revizyonu veya git commit tanımlayıcısı gibi doğal, sabit bir anahtarı olduğunda açık bir tohum doğru seçimdir, çünkü tanımlayıcıyı tesadüfi meta verilerden ayırır
Siz tohum sağlamadığınızda tohum nereden geliyor?
Açık bir tohum olmadan, losLab PDF Library, aynı belgenin yeniden üretimlerinde değişmez olması gereken belge durumundan bir tanesini türetir: PDF sürüm başlığı, sayfa sayısı ve belge bilgi sözlüğündeki her giriş. Dize ve isim değerleri aynen alınır, diğer nesne türleri serileştirilmiş formlarını katkıda bulunur ve bunların tamamı /ID dizelerine hash'lenir. Önemli sonuç, CreationDate ve ModDate'in bilgi sözlüğünün bir parçası olduğu ve dolayısıyla tasarım gereği tohumun bir parçası olduğudur. İki çalıştırma, yalnızca gerçekten aynı belge meta verilerini ürettiklerinde aynı tanımlayıcıyı kazanır
Lib.SetDeterministicDocumentID(1);
// No SetDocumentIDSeed: the seed is derived from document state,
// so the timestamps in the Info dictionary have to be pinned.
Lib.SetInformation(2, 'Quarterly Report'); // Title
Lib.SetInformation(5, 'reporting-service 4.2'); // Creator
Lib.SetInformation(7, 'D:20260101000000Z'); // CreationDate
Lib.SetInformation(8, 'D:20260101000000Z'); // ModDate
Lib.SaveToFile('report.pdf');
ModDate'i anahtar 8 ile sabitlemek çifte görev yapar ve insanları yakalayan kısım budur. Tek başına deterministik bir /ID, dosyayı byte özdeş yapmaz, çünkü kaydetme yolu, çağıran açıkça belirlemediği sürece ModDate'i geçerli zamanla damgalar. Anahtar 8'i ayarlamak, değeri çağıran tarafından sağlanmış olarak işaretler ve bu damgayı bastırır. Yalnızca tekrarlanabilir bir tanımlayıcı değil de tekrarlanabilir bir dosya istiyorsanız, meta veri zaman damgalarını derleme girdileri olarak ele alın: bunları kaynak kayıttan veya sabit bir epoktan türetin, asla Now'dan değil
ID'yi yeniden yazmak şifreli bir PDF'i neden bozar?
Çünkü /ID[0], şifreli bir belgede yalnızca meta veri değildir, anahtar malzemesidir. ISO 32000-1 §7.6.3.3 Algoritma 2, revizyon 2 ile 4 arasındaki standart güvenlik işleyicisi için dosya tanımlayıcısının ilk elemanını, doldurulmuş şifreyle, /O değeriyle ve izin bitleriyle birlikte şifreleme anahtarı hesaplamasına besler. Türetilen anahtar daha sonra bir okuyucunun açılışta kontrol ettiği /U doğrulama dizesini üretir ve dosya anahtarı, Encrypt'i çağırdığınızda ya da şifreli bir belge yüklendiğinde türetilip önbelleğe alınır, ikisi de kaydetmeden önce gerçekleşir. Kaydetme sırasında tanımlayıcıyı yeniden yazmak, bu yüzden yeniden açıldığında /U kontrolü başarısız olan yapısal olarak geçerli bir dosya üretir: ince bir bozulma değil, siz dahil kimsenin açamayacağı bir belge. Bu yüzden deterministik yenileme, şifreleme durumu taşımayan belgelerle sınırlıdır ve şifreli bir belge, deterministik mod olsun ya da olmasın zaten sahip olduğu /ID'yi korur, ayar bu yolda basitçe hiçbir etkiye sahip değildir. İlgili revizyon işleme ve izin semantiği, PDF şifreleme ve izin denetimi makalesinde ele alınmıştır. Ayrıca şifreleme geri yükleme yolunun yalnızca /ID[1]'i, tam olarak §14.4'ün amaçladığı gibi, değişim tanımlayıcısını yenilediğini de not edin
Artımlı kaydetmelerin orijinal tanımlayıcıyı neden koruduğu
İkinci sınır, ekleme modudur. Artımlı bir güncelleme dosyanın önceki her byte'ını dokunulmadan bırakır ve ardından yeni bir revizyon yazar ve §14.4 boyunca /ID[0]'ın kalıcılığı, bir tüketiciye yeni revizyonun eskisiyle aynı belgeye ait olduğunu söyleyen şeydir. Onu yeniden yazmak bu bağlantıyı koparır, dosyada zaten oturan revizyonlarla çelişir ve imza semantiğine müdahale eder, çünkü bir imza belirli bir belgenin belirli bir revizyonunun bir byte aralığını kapsar. losLab PDF Library bu yüzden deterministik tanımlayıcıyı yalnızca tam kaydetmelerde yeniler ve asla ekleme modu sırasında yenilemez, bu da PDF artımlı güncellemeleri ve akışa ekleme makalesinde anlatılan garantiyi bozulmadan tutar
Tanımlayıcı üretimi için tek bir dar boğaz
losLab PDF Library'deki tüm /ID üretimi artık tek bir dahili rutine, NewFileIDString'e akar, ki deterministik anahtarı bir kod yolu üzerindeki bir yamadan çok güvenilir yapan da budur. Boş belge oluşturma, eksik bir /ID dizisinin talep üzerine tembel oluşturulması ve şifreleme parmak izi geri yükleme yolu bunların hepsi onu çağırır, dolayısıyla duvar saatinin geri sızabileceği tam olarak tek bir yer vardır. Ayrıca içerikten türetilmiş bir tanımlayıcı gibi gelecekteki varyantların, tüm serileştiricinin bir denetimi yerine tek bir fonksiyonda bir değişiklik olacağı anlamına gelir
function BuildQuote(const Seed: WideString): AnsiString;
var
Lib: TPDFlib;
begin
Lib := TPDFlib.Create;
try
Lib.SetDeterministicDocumentID(1);
Lib.SetDocumentIDSeed(Seed);
Lib.SetInformation(7, 'D:20260101000000Z');
Lib.SetInformation(8, 'D:20260101000000Z');
Lib.SetOrigin(1);
Lib.DrawText(100, 700, 'Quote 8812');
Result := Lib.SaveToString;
finally
Lib.Free;
end;
end;
// Regression guard: two independent builds, one byte sequence.
if BuildQuote('quote-8812') = BuildQuote('quote-8812') then
WriteLn('reproducible')
else
WriteLn('nondeterminism leaked into the output');
Tekrarlanabilir çıktıya başka bir yerde güvenmeden önce bu karşılaştırmayı test paketinize bağlayın, çünkü yeni bir özellik bir zaman damgasını yeniden ortaya çıkardığı anda gürültülü bir şekilde başarısız olur. Tekrarlanabilirlik aksi halde sessizce bozulan bir özelliktir ve bellek içi iki kaydetme üzerinde tek bir doğrulama, her derlemede çalıştırmanın neredeyse hiçbir maliyeti yoktur
Burada gösterilen deterministik tanımlayıcı API'si, tam belge bilgisi, şifreleme ve artımlı kaydetme referansıyla birlikte Delphi ve C++Builder için losLab PDF Library ile birlikte sunulur