Microsoft Word veya Excel'in ürettiği bir PDF'i açın, sayfalarını çevirin ve olağan dışı hiçbir şey görünmez. Bunu bir Delphi programına yükleyin, sayfa sayısını geri okuyun; sayı doğrudur. Ardından şifreleme etkinken yeniden kaydedin; iş bir EListError ile başarısız olur veya çıktı, hasarlı bir çapraz referans uyarısıyla açılır. Dosya hiçbir zaman bozuk değildi. Bu bir hibrit referans dosyasıdır ve on beş yıllık bir görüntüleyicinin dosyayı açmasını sağlayan yapının ta kendisi, okumayı çok erken durduran bir yükleyiciyi bozmaktadır
Bu, her dahili testi geçen bir PDF boru hattının, dönüştüremediği bir dosyayla karşılaşmasının en yaygın yollarından biridir. Girdilerin tümü şirket içinde oluşturulmuşti, dolayısıyla hiçbir zaman hibrit değillerdi. İlk hibrit dosya, bir müşterinin bir elektronik tablodan aktarılan faturayı ilettiği gün gelir
Word ve Excel gerçekte ne yazar
ISO 32000-1, §7.5.8.4'te hibrit referans düzenini tanımlar. PDF 1.4 okuyucusunun dosyayı açmasına izin verirken nesne akışları gibi PDF 1.5 özelliklerini de isteyen bir uygulama, çapraz referans bilgilerini iki kez yazar. Sürüm 1.4'e kadar her PDF'i sonlandıran sabit genişlikteki ASCII satırları olan klasik bir çapraz referans tablosu vardır ve geri kalanını dizinleyen bir çapraz referans akışı vardır. Klasik bölümün son bilgisi (trailer), değeri bu akışın bayt kayması olan bir /XRefStm girdisi taşır
İş bölümü bilinçlidir. Eski bir okuyucunun ulaşması gereken nesneler, aralarında katalog ve sayfa ağacı da dahil olmak üzere, klasik tablodan adreslenebilir. Sıkıştırılmış nesne akışlarına katlanan nesneler, klasik tabloda bir f türü girdisiyle serbest olarak işaretlenir, böylece 1.4 okuyucusu doğrudan bunların üzerinden geçer ve ayrıştıramayacağı bir yapıya asla takılmaz. Gerçek konumları yalnızca çapraz referans akışında yaşar. Böyle bir dosyanın imzası kuyruğudur: genellikle xref ve ardından 0 0 alt bölüm başlığından başka bir şey olmayan kısa bir klasik bölüm; bunun son bilgisi, gerçek kurtarma verilerinin bulunduğu /XRefStm'yi işaret eder
Doğru bir sayfa sayısının neden hiçbir şeyi kanıtlamadığı
Katalog ve sayfa ağacı klasik tablodan bilerek ulaşılabildiği için, yalnızca o tabloyu okuyan bir yükleyici /Root'u bulur, sayfa ağacını dolaşır ve doğru sayfa sayısını bildirir. Eski bir okuyucunun ihtiyaç duyduğu her şey mevcuttur, bu nedenle dosya sağlıklı görünür. Kaybolan nesneler, nesne akışlarına paketlenmiş olanlardır: AcroForm alan sözlükleri, etiketli PDF yapı öğeleri, eski bir görüntüleyiciye asla görünmesi gerekmeyen küçük sözlüklerin uzun kuyruğu
Bir şey o nesnelere dokunana kadar boşluğu fark etmezsiniz ve tam bir yeniden kaydetme hepsine dokunur. Yeniden şifrelemek veya yeniden yazmak için belgeyi dolaşmak, tam olarak sırayla her nesne numarasını isteyen işlemdir; bu nedenle belirti, nedeninden çok uzakta, yükleme zamanı yerine kaydetme zamanında ortaya çıkar
Tuzak, xref'i görüp duran bir algılayıcıdır
Bir dosyanın nasıl dizinlendiğine karar vermenin ucuz yolu, startxref'i takip etmek ve işaret ettiği ilk baytları incelemektir. xref anahtar sözcüğü klasik bir tablo anlamına gelir; bir akış nesnesi, çapraz referans akışı anlamına gelir. Bu test, tek bir şemaya bağlı kalan her dosya için doğrudur. startxref'i yalnızca eski okuyucuları memnun etmek amacıyla klasik bir bölümü hedefleyen hibrit bir dosya için yanlıştır; oysa o bölümün son bilgisindeki /XRefStm, belgenin çoğunun gerçekte dizinlendiği yerdir. Karşılaştığı ilk xref üzerinde "klasik" döndüren bir algılayıcı asla /XRefStm'yi okumaz ve yalnızca akışta yaşayan her nesne görünmez hale gelir
var
Pdf: THotPDF;
PageCount: Integer;
begin
Pdf := THotPDF.Create(nil);
try
PageCount := Pdf.LoadFromFile('Invoice_XLS.pdf'); // count is correct
// inspect or edit the loaded document here
Pdf.SaveLoadedDocument('Invoice_secured.pdf'); // walks every object
finally
Pdf.Free;
end;
end;
Erken çıkış algılayıcısı devredeyken, yükleme iyi görünür ve yeniden kaydetme, olmayan nesnelerin kendilerini duyurduğu yerdir. Çözüm, başlangıçta daha fazla bayt okumak değil; hibrit son bilgiyi tanımak ve dosyanın bittiğine karar vermeden önce /XRefStm'yi takip etmektir
Birleştirme sırası tartışılamaz
Her iki dizin de okunduktan sonra, yalnızca tek bir yönde birleştirilebilirler. Klasik girdiler çevresinde doldurulurken, çapraz referans akışının önce birleştirilmesi gerekir. Nedeni, biçimin kalbindeki küçük aldatmacadır. Bir hibrit dosya, eski okuyucuların bunları yoksayması için sıkıştırılmış nesnelerini klasik tabloda serbest olarak işaretler. İlk görülen kazanır politikasını onurlandıran ve önce klasik tabloyu okuyan bir yükleyici, o nesne numaralarını serbest olarak kaydedecek, ardından bunları gerçekte konumlandıran akış girdilerini atacaktır, çünkü yuvalar zaten doludur. Sırayı tersine çevirin; akıştan gelen tip 2 girdileri (her biri bir nesne akış numarası artı bir dizin) sahip olmaları gereken yuvaları kazanır ve klasik girdiler bunların etrafına yerleşir
Aynı disiplin, daha eski bir revizyonun silinmiş bir nesneyi diriltmesine karşı da koruma sağlar. Artımlı güncellemeler /Prev üzerinden geriye doğru zincirlenir ve tip 0 serbest girdisi, daha yeni bir bölümün bir nesne numarasını emekliye ayırdığını gösteren bir nöbetçidir. Zincirdeki daha sonraki, daha eski bir bölümün bu nöbetçiyi eski bir konumla üzerine yazmasına izin verilmemelidir. Serbest işaretleyiciler için ilk görüleni yetkili olarak kabul edin; silinen nesne silinmiş olarak kalır. Dikkatsiz davranırsanız, dosyanın kendi geçmişi en son revizyonun kaldırdığı içeriği canlandırır
HotPDF'te bunun anlamı nedir
Motor, hibrit referans dosyalarını sizin için çözer ve bunu çapraz referans verilerini ayrıştırması gereken her yolda yapar. LoadFromFile veya LoadFromStream ile bir belge yükleyin, değişikliklerinizi yapın ve SaveLoadedDocument'ı çağırın; veya bir girdi okuyan ve bir çıktı yazan EncryptFile gibi tek seferlik bir işlem çalıştırın. Her iki durumda da kurtarma, /XRefStm'yi okur, akış bölümünü klasik girdilerin önünde birleştirir ve yazma işlemi bunları listelemeden önce akışlarda yaşayan nesneleri çözer. AES-256 şifreleme yolu, sorunun kendini ilk gösterdiği yerdir, çünkü bir belgeyi şifrelemek her nesneyi yeniden yazar ve bu nedenle her nesnenin zaten konumlandırılmış olmasını gerektirir
// One-shot: read the hybrid input, write an AES-256 encrypted copy
Pdf.EncryptFile('Letter_DOC.pdf', 'Letter_secured.pdf',
'owner-secret', '', aes256, [prPrint, prFillAnnotations]);
Aklınızda bulundurmanız gereken detay API'nin yukarısında yer alır. Word, Excel, PowerPoint ve uzun bir "PDF olarak kaydet" boru hatları listesinden gelen dosyalar rutin olarak hibrittir; bu nedenle yalnızca kendi oluşturucu çıktınıza karşı test ettiğiniz bir yükleyici testlerde bunlardan biriyle hiç karşılaşmayabilir. Test ortamlarınızı yalnızca kendi kodunuzun ürettiği dosyalarla değil, gerçek Office uygulamalarından aktarılan belgelerle besleyin
Şüphelendiğiniz bir dosyayı kontrol etme
İki inceleme soruyu hızla çözer. Dosyayı bir onaltılık (hex) görünümde açın ve son startxref'ten sonraki baytları okuyun; hibrit bir dosya, trailer sözlüğünde /XRefStm içeren kısa bir klasik bölüm gösterir. Vear veya tam bir ayrıştırmanın bildirdiği nesne sayısını, trailer'da /Size'ın bildirdiği en yüksek nesne numarasıyla karşılaştırın. Büyük bir boşluk, nesnelerin yükleyicinin açmadığı akışlarda gizlendiği anlamına gelir; bu da daha sonra kaydetme zamanı hatasına dönüşen eksikliğin ta kendisidir
Bu hikayenin yazıcı tarafı, nesne akışlarının ve sıkıştırılmış çapraz referansların ilk etapta nasıl üretildiği, nesne akışları ve artımlı güncellemeler hakkındaki makalemizde ele alınmıştır. Söz konusu hibrit dosya aynı zamanda çok büyük olduğunda, büyük PDF iş akışları için Doğrudan Dosya API kılavuzundaki yükleme teknikleri, her şeyi belleğe okumadan incelemenize olanak tanır. Her ikisi de, bu blogun başka yerlerinde ele alınan yükleme, düzenleme, şifreleme ve imzalama API'lerinin yanı sıra Delphi ve C++Builder için HotPDF Bileşeni'nin bir parçası olarak sunulan burada açıklanan kurtarma işlemiyle doğal olarak eşleşir