PDF artımlı güncellemeleri, bir Delphi uygulamasının yalnızca değiştirilen nesneleri ekleyerek belgede herhangi bir orijinal bayta dokunmadan değişiklik yapmasını sağlar. losLab PDF Kütüphanesi bunu, ISO 32000-1 §7.5.6 tarafından tanımlanan artımlı bölümü yazan AppendToStream aracılığıyla uygular; böylece 2 GB'lık bir dosyada yapılan tek bir yer işareti düzenlemesi, tam bir yeniden yazma yerine kilobaytlarca çıktı maliyeti getirir. İmzalı belgelerin imzalarını geçersiz kılmadan güncellenebilmesinin nedeni de aynı mekanizmadır
Bu özelliğin çözdüğü sorun somuttur. Tam kaydetme işlemi tüm dosyayı yeniden yazar: her nesne yeniden serileştirilir, her çapraz referans ofseti yeniden hesaplanır ve çıktı ile girdi arasında bayt düzeyinde bir ilişki kalmaz. 40 KB'lık bir fatura için bu sorun değildir. Ancak yalnızca belge başlığındaki bir yazım hatasını düzelttiğiniz 2 GB'lık taranmış bir arşiv için, yirmi baytı değiştirmek amacıyla iki gigabaytı yeniden yazmak saçmadır — ve eğer dosya dijital bir imza taşıyorsa, yeniden yazma işlemi bu imzayı yok etmiş olur
Bir PDF'i kaydetmek neden dijital imzasını bozar?
Bir PDF dijital imzası belgenin mantıksal içeriğini imzalamaz; fiziksel dosyanın bayt aralıklarını imzalar. İmza sözlüğündeki /ByteRange girdisi, dosyanın tam olarak hangi bölümlerinin kriptografik özet kapsamında olduğunu kaydeder. Bu baytları yeniden serileştiren herhangi bir kaydetme işlemi —semantik olarak aynı belgeyi üretse bile— özeti değiştirir ve her doğrulayıcı imzanın bozuk olduğunu bildirir. Bu kasıtlı bir tasarımdır: imza, yazarın gördüğü soyut bir belge modeline değil, imzalayanın gördüğü baytlara tanıklık eder
Artımlı güncellemeler, PDF spesifikasyonunun sunduğu kaçış kapısıdır. Artımlı bir kaydetme, yeni verileri orijinal %%EOF işaretinden sonrasına eklediğinden ve imzalanan bayt aralıklarına asla dokunmadığından, mevcut imza kapsadığı baytlar üzerinde doğrulanmaya devam eder. Doğrulayıcılar daha sonra eklenen değişiklikleri ayrı olarak sınıflandırır —ikinci bir imza, bir form doldurma, bir açıklama— ve bunlara izin verilen değişiklikler olup olmadığına karar verir. Çoklu imza iş akışlarının tamamı buna bağlıdır: her imzalayan bir öncekinin üzerine artımlı bir bölüm ekler. İmzalama iş hatları kuruyorsanız, Delphi'de PAdES imzalama ve doğrulama hakkındaki tamamlayıcı makale, imza bayt aralıklarının ve artımlı bölümlerin nasıl etkileşime girdiğini ayrıntılı olarak ele almaktadır
ISO 32000-1 §7.5.6 kapsamında artımlı güncellemeler nasıl çalışır?
ISO 32000-1 §7.5.6 modeli üç kuralla tanımlar. İlk olarak, orijinal dosya içeriği tamamen bozulmadan kalır —tek bir bayt bile hareket etmez. İkinci olarak, değiştirilen ve yeni oluşturulan nesneler son %%EOF işaretinden sonrasına eklenir ve her biri daha önce sahip olduğu nesne numarasıyla yazılır (değişen nesneler yalnızca eskisini gölgeleyen daha yeni bir tanım alır). Üçüncü olarak, yeni bir çapraz referans bölümü ve fragmanı (trailer) eklenir; fragmanın /Prev girdisi bir önceki çapraz referans bölümünün bayt ofsetini göstererek, okuyucunun her nesneyi en son tanımına çözümlemek için en yeniden en eskiye doğru yürüdüğü bir zincir oluşturur
Bu yapıdan iki yararlı özellik ortaya çıkar. Güncellemeler, belge boyutuna değil, neyin değiştiğine bağlı olarak ucuzdur —ekleme maliyeti, değiştirilen nesnelerin boyutu artı küçük bir xref/fragman genel gideridir. Ve dosya kendi sürüm geçmişi haline gelir: önceki her revizyon hala fiziksel olarak mevcuttur, bu nedenle bir denetçi dosyayı daha önceki herhangi bir %%EOF işaretinden kesebilir ve tam olarak o sırada mevcut olan belgeyi kurtarabilir. Her değişiklikten önce bir belgenin nasıl göründüğünü kanıtlaması gereken uyumluluk iş akışları için bu yerleşik denetim izi genellikle artımlı kaydetmeler lehine karar verici argümandır
AppendToStream ile artımlı bir güncelleme yazma
losLab PDF Kütüphanesi, artımlı çıktıyı, başarı durumunda 1 ve başarısızlık durumunda 0 döndüren AppendToStream(AppendMode: Integer; OutStream: TStream): Integer aracılığıyla sunar. AppendMode parametresi hedef akışa neyin yazılacağını seçer. Mod 0 tam bir dosya yazar: önce orijinal kaynak baytları akışa kopyalanır, ardından artımlı bölüm eklenir. Mod 1 yalnızca artımlı bölümün kendisini —deltayı— yazar ve kaynak baytlarını tamamen atlar. Mod 2 ise önce SetAppendInputFromString aracılığıyla kaydedilen kullanıcı tanımlı bir öneki yazar, ardından güncelleme bölümünü bunun üzerine ekler
var
Doc: TPDFlib;
Delta: TMemoryStream;
begin
Doc := TPDFlib.Create;
try
if Doc.LoadFromFile('contract.pdf', '') <= 0 then
Exit;
// Small edit: the kind of change that should not
// trigger a rewrite of the whole file
Doc.SetInformation(3, 'Amended 2026-07-04'); // key 3 = /Subject
Delta := TMemoryStream.Create;
try
// AppendMode = 1: write only the incremental section.
// Original bytes + Delta = a complete, valid PDF.
if Doc.AppendToStream(1, Delta) = 1 then
Delta.SaveToFile('contract.delta.bin');
finally
Delta.Free;
end;
finally
Doc.Free;
end;
end;
Mod 1, sistem tasarımı için ilgi çekici olanıdır. Delta kendi kendine yeten bir yapıda olduğu için onu orijinalden bağımsız olarak gönderebilirsiniz: revizyonları nesne depolama alanında ayrı bloblar olarak saklayabilir, yalnızca deltaları uzak bir siteye çoğaltabilir veya temel dosyayı artımların zinciriyle birleştirerek herhangi bir revizyonu yeniden oluşturabilirsiniz. Yeniden oluşturma kuralı düz bayt birleştirmedir —önce orijinal dosya, ardından sırasıyla her bir delta— çünkü §7.5.6'nın artımlı olarak güncellenmiş bir dosya için öngördüğü düzen tam olarak budur
Kütüphane orijinal dosyayı kopyalamadan xref ofsetlerini nasıl hesaplar?
Artımlı bir bölümün içindeki çapraz referans girişleri mutlak bayt ofsetlerini içerecektir —deltanın başlangıcından değil, tam dosyanın başlangıcından ölçülen konumlar. Bu, mod 1 için bir bulmaca yaratır: yazıcı orijinal baytları asla yaymaz, ancak kaydettiği her ofsetin sanki oradalarmış gibi davranması gerekir. losLab PDF Kütüphanesi bunu, serileştiriciye sanal bir koordinat alanı sunan dahili bir akış bağdaştırıcısı olan TPDFAppendSectionStream ile çözer. Bağdaştırıcı, orijinal dosyanın bayt uzunluğu temel ofseti olacak şekilde oluşturulur, konumunu ve boyutunu bu temel artı şimdiye kadar eklenen her şey olarak bildirir ve yalnızca yeni yazılan baytları arayanın hedef akışına iletir
Sonuç olarak, mod 1 asla kaynak belgenin bir kopyasını oluşturmaz —ne diskte ne de bellekte. Basit uygulama (tam dosyayı geçici bir arabelleğe yazmak ve ardından kuyruğunu kesmek), gigabayt ölçeğindeki girdiler için tam olarak artımlı güncellemelerin önlemeye çalıştığı maliyet olan orijinal PDF'in tamamının geçici bir kopyasını taşıyacaktır. Bu ofset sanallaştırma tekniği, kütüphanenin başka yerlerinde kullanılan bayt referans kaydırmanın yakın bir akrabasıdır; bayt referans kaydırma ile hızlı PDF birleştirme hakkındaki makale aynı fikrin belgeleri birleştirmeye uygulandığını gösterir ve doğrudan dosya erişimi ile büyük PDF birleştirme ve bölme kılavuzu, RAM'e rahatça sığmayan dosyalar için çevreleyen G/Ç mimarisini kapsar
SaveToStream ile tam kaydetmeleri akışa alma
Artımlı çıktı, akışa alma hikayesinin yarısıdır; diğer yarısı ise tam bir kaydetme sırasında ne olduğudur. losLab PDF Kütüphanesi'ndeki SaveToStream, belgenin tamamını önce bir ara AnsiString arabelleğine işleyip ardından bu arabelleği tek bir çağrıda yazmak yerine, belge serileştiricisini doğrudan hedef akışa karşı çalıştırır. Eski yaklaşım çalışıyordu, ancak her tam kaydetmenin bellekte çıktının ikinci bir tam kopyasını geçici olarak tutması anlamına geliyordu —10 MB'ta zararsız, 500 MB'ta sancılı ve 32 bitlik işlemlerde çoklu gigabaytlık çıktılar için sert bir engel. Doğrudan serileştirme, bellek zirvesinin serileştirilmiş uzunluğu yerine belgenin nesne yapılarını izlemesini sağlar
var
Doc: TPDFlib;
Output: TFileStream;
begin
Doc := TPDFlib.Create;
try
if Doc.LoadFromFile('archive.pdf', '') <= 0 then
Exit;
// ... edits that justify a full rewrite ...
Output := TFileStream.Create('archive-rewritten.pdf', fmCreate);
try
if Doc.SaveToStream(Output) = 0 then
Writeln('Save failed, error ', Doc.LastErrorCode);
finally
Output.Free;
end;
finally
Doc.Free;
end;
end;
Bir paylaşım modu dersi: AppendToFile 0 döndürdüğünde
Bu alandaki bir gerilemeyi anlatmakta fayda var çünkü başarısızlık deseni genelleştirilebilir. AppendToFile(FileName), artımlı bir güncellemeyi doğrudan diskteki mevcut bir PDF'e ekler —yerinde bir denetim izi iş akışı için doğal çağrı: bir dosyayı yükleyin, bir değişiklik yapın, aynı yola ekleyin. v3.71.2'de bu tam sıra 0 döndürmeye başladı. Kök neden yazıcıda değil, yükleyicide yatıyordu: büyük belgelerin isteğe bağlı olarak okunmasını desteklemek için LoadFromFile, kaynak dosya tutamacını belge nesnesinin ömrü boyunca açık tutar ve bu tutamaç fmShareDenyWrite ile açılmıştı. AppendToFile daha sonra yazmak için aynı dosyayı yeniden açmaya çalıştığında, yükleyicinin kendi paylaşım modu bunu reddetti ve API tek bir bayt yazmadan başarısız oldu
Düzeltme, yükleyicinin paylaşım modunu fmShareDenyNone olarak gevşetti; bu, artımlı bir eklemenin ne olduğu nedeniyle tam olarak güvenlidir: baytları kesinlikle dosyanın sonundan sonrasına ekler ve okuyucunun uzun ömürlü tutamacının hizmet ettiği bölgeyi asla yeniden yazmaz. Bu kütüphaneyi saran —veya benzer akış yükleyicileri oluşturan— herkes için genel ders, tembel, tutamaç tutan okuyucular ile aynı dosya yazıcılarının gerilim içinde olduğudur ve açılış anında seçtiğiniz paylaşım modu bir uygulama detayı değil, bir API sözleşmesidir. Kodunuzda AppendToFile 0 döndürürse, önce işleminizdeki başka bir şeyin hedef dosyayı kısıtlayıcı bir paylaşım moduyla hala tutup tutmadığını kontrol edin
Gerçek maliyetler: artımlı güncellemeler yanlış araç olduğunda
Artımlı güncellemeler, yazma verimliliği için dosya boyutundan ödün verir ve bu takas her zaman olumlu değildir. Her revizyon değiştirilen nesnelerini eklerken, yerine geçen tanımlar dosyada kalır; bu nedenle yüzlerce kez düzenlenen bir belge ölü nesneler ve her okuyucunun yürümesi gereken uzun bir /Prev zinciri biriktirir. Daha da kötüsü, "silinen" içerik gitmiş değildir: revizyon beşte kaldırılan metin, revizyon dördün baytlarında hala fiziksel olarak mevcuttur ve dosyayı kesen herkes tarafından kurtarılabilir. Bu nedenle karartma (redaction), temizleme veya hassas içeriğin herhangi bir şekilde kaldırılması tam bir yeniden yazma gerektirir —karartmanın artımlı olarak kaydedilmesi, ekstra adımlara sahip bir veri sızıntısıdır
Biriktirilen artışları ve kullanılmayan nesneleri sıkıştırmak hedeflendiğinde veya şifreleme gibi belge genelindeki özellikler değiştirildiğinde —yeniden şifreleme her dizeye ve akışa dokunduğundan, değişiklik hakkında "artımlı" bir şey kalmaz— ya da düzenleme geçmişinin dosyayla birlikte taşınmaması gereken temiz bir teslimat üretilirken tam bir kaydetme doğru karardır. Makul bir kural: bir belge canlıyken ve değişirken, özellikle de imza taşıdığında AppendToStream or AppendToFile kullanın; belge sisteminizden ayrıldığında veya geçmişi düzleştirilmesi gerektiğinde, yaşam döngüsü sınırlarında tam bir SaveToStream yeniden yazması kullanın
Artımlı güncellemeler, sanal ofset delta çıktısı ve doğrudan akışa serileştirme, yukarıda tartışılan imzalama ve büyük dosya özelliklerinin yanı sıra Delphi, C# ve VB.NET için standart losLab PDF Library'nin bir parçasıdır; ürün sayfası, imzalama ve büyük dosya özelliklerinin yanında tam kaydetme ve ekleme API yüzeyini listeler