İmzalanmış bir sözleşmenin 3. sayfasını değiştirmek, içindekiler tablosunu hareket ettirmemelidir. Eski sayfayı silin, yenisini ekleyin ve orayı işaret eden her yer imi artık başka bir yere iner. PDFlibPas Delphi PDF kütüphanesi, hedef sayfa nesnesinin kendisini koruyarak ve yalnızca görsel içerik taşıyan girdileri aktararak bunu önler
Bir PDF sayfası değiştirildikten sonra yer imleri neden bozulur?
Yer imleri bozulur, çünkü bir PDF hedefi bir sayfayı sayfa numarasıyla değil dolaylı nesne referansıyla adlandırır. ISO 32000-1 §12.3.2.2, açık bir hedefi, ilk elemanı sayfa nesnesine dolaylı bir referans olan bir dizi olarak tanımlar. O nesneyi silin ve bir değiştirme ekleyin, referans sarkık kalır: çoğu görüntüleyici okuyucuyu sayfa 1'e bırakarak yanıt verir; bu, sil-sonra-ekle bir değiştirmeden sonra insanların bildirdiği tam belirtidir. Sayfa ağacı mükemmel görünür, sayfa sayısı doğrudur, rendering doğrudur ve tüm gezinme katmanı sessizce yanlıştır
İsimlendirilmiş hedefler de sizi kurtarmaz. §12.3.2.3, bir ismi doküman kataloğundaki /Dests isim ağacı üzerinden yönlendirir, ama o ismin çözüldüğü yaprak yine de aynı sayfa referansını tutan açık bir hedef dizisidir. İsimlendirme, sayfa referansının üzerine değil çevresine bir dolaylılık katmanı ekler. Aynı akıl yürütme, §12.5'te anlatılan etkileşimli katmanın geri kalanını kapsar: bir link annotation, /D'si o dizi olan bir /Dest ya da bir /A GoTo eylemi taşır, her annotation kendi sayfasına dolaylı bir referans olan bir /P girdisi taşıyabilir ve bir form alanı widget'ı tam olarak aynı zeminde bir annotation'dır. Saf bir sayfa değişimi dört alt sistemi bir kerede ayırır ve bunları gerçek bir dosyada numaralandırılmış görmek isterseniz, aynı nesne grafiği outline ve annotation introspection'ının gezdiği şeydir
Hangi sayfa girdileri kimlik taşır, hangileri görünüm taşır
Bir sayfa sözlüğü iki tür girdiyi karıştırır ve yerinde bir değiştirme, tam olarak onları ayırdığınızda başarılı olur. Görünüm tarafı sonludur ve numaralandırılabilir: /Contents, /Resources, beş sayfa kutusu /MediaBox, /CropBox, /BleedBox, /TrimBox ve /ArtBox, artı /Rotate, /Group, /UserUnit ve /BoxColorInfo. Bu on bir girdi, bir rasterleştiricinin sayfa için ürettiği her şeye karar verir ve dosyada başka hiçbir şey isimle onlara işaret etmez
Kimlik tarafı, dokümanın geri kalanının kendini bağladığı şeydir: sayfa nesnesi numarası ve üretimi, sayfa ağacına /Parent geri bağlantısı ve /Annots. PDFlibPas bunların her birini dokunulmadan bırakır. ReplacePageRanges, on bir görsel girdiyi hedef sayfa sözlüğünden temizler ve içe aktarılan kaynak sayfadan yeniden ekler, böylece hedef sayfa nesnesi değiştirilmek yerine yerinde değiştirilir. §7.7.3'ün gerektirdiği sayfa ağacı yapısı da şekilde bayt-özdeş kalır: /Kids sırası, /Count ve hayatta kalan her /Parent, öncesi ve sonrasında aynıdır, çünkü hiçbir düğüm hiç bağlantısı kesilmedi
PDFlibPas nesneleri yeniden numaralandırmadan bir sayfayı nasıl değiştirir?
Çağrı bir kaynak doküman, 1-tabanlı bir hedef başlangıç sayfası, bir kaynak aralık ifadesi ve bir seçenekler bayrağı alır. Her iki doküman da aynı örnekte açık olmalıdır ve hedef doküman seçili olandır. Hedef sayfa sayısı hiçbir zaman değişmediğinden, istediğiniz aralığın TargetStartPage'den başlayarak dokümana sığması gerekir ve bu, herhangi bir şey oluşturulmadan önce kontrol edilir
var
Lib: TPDFlib;
TargetDoc, SourceDoc: Integer;
begin
Lib := TPDFlib.Create;
try
// The document whose bookmarks and links must survive
if Lib.LoadFromFile('contract-final.pdf', '') <> 1 then
Exit;
TargetDoc := Lib.SelectedDocument;
// The revised clause page, rendered by whatever produced it
if Lib.LoadFromFile('clause-7-revised.pdf', '') <> 1 then
Exit;
SourceDoc := Lib.SelectedDocument;
Lib.SelectDocument(TargetDoc);
// Source page 1 overwrites the visuals of target page 3.
// Page count, page 3 object number, bookmarks and annotations are kept.
if Lib.ReplacePageRanges(SourceDoc, 3, '1', 0) = 1 then
Lib.SaveToFile('contract-final.pdf');
finally
Lib.Free;
end;
end;
Dahili olarak kaynak sayfalar doküman sınırları arasında basitçe okunamaz, çünkü içlerindeki her dolaylı referans kaynak nesne numaralandırmasına aittir. Bu yüzden kaynak aralık önce sıradan yolla, son gerçek sayfadan sonra eklenen geçici sayfalar olarak içe aktarılır, ki bu tam nesne-grafiği yeniden eşlemesini çalıştırır: içerik akışları, fontlar, XObject'ler, gölgelendirmeler ve renk uzayları hedef dokümana yeniden numaralandırılır. Yalnızca o zaman on bir görsel girdi her geçici sayfadan hedef sayfasına kopyalanır ve yalnızca o zaman geçici sayfaların sayfa ağacıyla bağlantısı kesilir. Yeniden eşleme işi, ucuz ve güvenli olduğu yerde gerçekleşir ve yıkıcı düzenleme, zaten var olan sayfalarda bir sözlük-seviyesi değişime indirgenir
Az önce aktardığınız şeyi yok edecek silme yolu
Bu geçici sayfaları kaldırmak, önemsiz görünen ama öyle olmayan adımdır. Kütüphanedeki sıradan sayfa-silme yolu, bir düğümün bağlantısını kesmekten daha fazlasını yapar: silinen her sayfanın katmanlarını birleştirir, ilk içerik akışını boşaltır ve başka hiçbir sayfanın paylaşmadığı kaynakları geri kazanır. Bu, gerçek bir silme için doğru davranıştır ve burada felakettir, çünkü geçici sayfalar kaldırıldığında hedef sayfalar zaten tam olarak o içerik akışlarına ve kaynak nesnelerine referans vermektedir. Onları boşaltmak, az önce değiştirdiğiniz sayfayı boşaltırdı ve kaynak taraması, şimdi canlı bir sahibi olan fontları ve görüntüleri toplardı
Düzeltme, dahili silme yolu üzerinde referans-verilen-nesneleri-koru modudur. Ayarlandığında, silme hem paylaşılmayan-kaynak taramasını hem de içerik-akışı temizlemesini atlar ve sayfaları sayfa ağacından ayırmak ve ağaç muhasebesini düzeltmek dışında hiçbir şey yapmaz. Aktarılan nesneler yeni bir sahiple hayatta kalır ve işlemden sonraki nesne sahipliği, bir beyaz tahtaya çizeceğiniz şeydir: bir içerik akışı, bir sahip sayfa, hiç taşınmamış bir nesne numarası. Sayfa oluşturma, silme ve yeniden sıralama için ilgili yaşam döngüsü kuralları, doküman ve sayfa yaşam döngüsü işlemleri üzerine notlarda ayrı olarak ele alınmıştır
Sıralama, tekrarlar ve hep-ya-da-hiç başarısızlık
Seçenekler bayrağı, kaynak aralığın nasıl yorumlandığını seçer. 0, ayrıştırılmış sayfa numaralarını sıralar ve tekrarları kaldırır; bu, çağıranın '4-6,2' gibi bir şey geçirdiğinde ve basitçe o dört sayfayı kastettiğinde akla uygun varsayılandır. 1, yazdığınız sırayı korur ve bir sayfanın tekrarlanmasına izin verir, bu yüzden '2,1,2' gerçekten iki kaynak sayfadan alınan üç değiştirme anlamına gelir. Doğrulama önce çalışır ve eksiksiz çalışır: aralık sözdizimi, her sayfa numarası kaynak sayfa sayısına karşı, seçenek değerinin kendisi ve hedef kapasitesi, tek bir nesne oluşturulmadan önce kontrol edilir. Reddedilen bir çağrı, LastErrorCode'u 412'ye ayarlar, önceden seçili sayfayı geri yükler ve dokümanı tam olarak olduğu gibi bırakır
var
Replaced: Integer;
begin
Lib.SelectDocument(TargetDoc);
// Options = 1: source order is preserved and repeats are allowed, so
// target pages 5, 6 and 7 receive source pages 2, 1 and 2 respectively
Replaced := Lib.ReplacePageRanges(SourceDoc, 5, '2,1,2', 1);
if Replaced = 0 then
raise Exception.CreateFmt('Replacement rejected, LastErrorCode = %d',
[Lib.LastErrorCode]);
// On success the selection is the first replaced page
Assert(Lib.SelectedPage = 5);
end;
Atomiklik, doğrulamanın ötesinde aktarımın kendisine de uzanır. İlk kaynak sayfa içe aktarılmadan önce, aralıktaki her hedef sayfanın on bir görsel girdisi kodlanmış değerler olarak anlık görüntüsü alınır. İçe aktarma başarısız olursa ya da içe aktarılan sayfa sayısı istenenle eşleşmezse, anlık görüntüler hedef sayfalara geri kodu çözülür ve geçici sayfalar kaldırılır, bu yüzden yarım kalan bir başarısızlık bile orijinal görünümleri orijinal nesnelerinde yerinde bırakır. Bu, kulağa geldiğinden daha önemlidir: bir sözleşmede yarı değiştirilmiş bir sayfa aralığı, başarısız bir çağrıdan daha kötüdür, çünkü dosyada hiçbir şey onu yarım-bitmiş olarak işaretlemez
// Post-conditions worth asserting in a regression test
Lib.SelectPage(3);
// Geometry now comes from the source page
WriteLn(Format('%.2f x %.2f', [Lib.PageWidth, Lib.PageHeight]));
// Annotations that were already on target page 3 are still attached
WriteLn(Lib.AnnotationCount);
// The bookmark created before the replacement still resolves to page 3
WriteLn(Lib.GetOutlinePage(OutlineID));
// And the document is still the same length
WriteLn(Lib.PageCount);
Yerinde değiştirme sizin için hâlâ ne yapmaz?
Kaynak annotation'ları, kaynak form alanları ve kaynak outline'lar kasıtlı olarak içe aktarılmaz. Bir widget'ı /AcroForm alan girdisi olmadan ya da işaretli-içerik taşıyan bir annotation'ı yapı ağacı sahipliği olmadan aktarmak, hiçbir görüntüleyicinin akıl yürütemeyeceği yarı içe aktarılmış bir etkileşimli nesne üretir, bu yüzden işlem yalnızca görünümü aktarır. Pratik sonuç, değiştirme sayfasının yeni form alanları ya da yeni bağlantılar taşıması gerekiyorsa, bunları sonrasında hâlâ onları bekleyerek orada oturan hedef sayfa nesnesine karşı eklersiniz
Kendi dosyalarınızda kontrol etmeye değer iki sınır daha var. Birincisi, /Annots korunur ama sayfa geometrisi korunmaz, bu yüzden 220 mm'lik bir sayfayı 320 mm'lik bir sayfayla değiştirmek, annotation dikdörtgenlerini farklı boyutlu bir /MediaBox içinde eski koordinatlarında tutar; geometri değişirse, tuttuğunuz annotation'ları yeniden konumlandırın. İkincisi, on bir görsel anahtarın dışındaki girdiler tasarım gereği hedef sayfayla kalır, ki bu /Trans ya da /AA için doğrudur ve /Thumb için bayattır, bu yüzden bir değiştirmeden sonra küçük resimleri yeniden oluşturun. Etiketlenmiş dokümanlar bir düşünce daha gerektirir: yapı elemanları hâlâ /Pg üzerinden doğru sayfa nesnesine işaret eder, ama işaretli-içerik tanımlayıcıları artık orada olmayan içeriği tanımlar, bu yüzden bir PDF/UA iş akışı içinde bir sayfa değişimi bir içerik düzenlemesi kadar bir yapı-ağacı düzenlemesidir. İşiniz gerçekten değiştirmek değil de birleştirmekse, tuttuğunuz sayfalara sanat katmanı eklemekse, sayfa dikişleme ve şablon yaklaşımı daha ucuz araçtır
Aralık ifadesi sözdizimi, seçenek değerleri ve çevresindeki sayfa manipülasyonu API'si dahil burada anlatılan her şey, Delphi ve C++Builder için standart PDFlibPas Delphi PDF Library'de gönderilir; referans belgelendirmesi, sayfa değiştirme çağrısı ve hata kodları için tam girdiyi taşır