Küçük bir doğrulayıcı yazarsınız. Bir PDF açar, sona gider, startxref'i bulur, sapmayı (offset) okur ve altında sabit genişlikli çapraz referans tablosu bulunan xref anahtar kelimesine inmeyi bekler. Bu tablodan nesne sapmalarını toplar, ardından /Root ve /Size bilgilerini öğrenmek için trailer anahtar kelimesini geriye doğru tarar. Test etmek için ürettiğiniz her dosyada mükemmel çalışır. Daha sonra Word'ün güncel bir sürümüyle veya PDF 1.5'i hedefleyen bir kitaplıkla üretilmiş bir dosya gelir ve doğrulayıcı onun bozuk olduğunu beyan eder. Sapmanın işaret ettiği yerde xref anahtar kelimesi yoktur, hiçbir yerde trailer sözlüğü yoktur ve doğrulayıcının oluşturduğu nesne tablosu neredeyse boştur. Dosya geçerlidir. Doğrulayıcı ise onu on beş yıllık bir mercekle okumaktadır.
Bu durum, klasik düzene karşı yazılmış bayt düzeyinde bir PDF kontrolünün modern belgelerde başarısız olmasının en yaygın tek nedenidir. Bağımlı olduğu yapı, yani düz metin çapraz referans tablosu ve trailer anahtar kelimesi, PDF 1.5'te isteğe bağlı hale getirilmiş ve sıklıkla kullanılmamaktadır. İki özellik bunun yerini aldı: çapraz referans akışı (cross-reference stream) ve sıkıştırılmış nesne akışı (compressed object stream). Her ikisi de ISO 32000-1'de açıklanmıştır ve bunları bilmeyen bir doğrulayıcı sağlıklı bir dosyayı eksik nesneler yığını olarak görür.
PDF 1.5 dosya kuyruğu hakkında neleri değiştirdi
ISO 32000-1 §7.5.8 çapraz referans akışını tanımlar ve §7.5.7 /ObjStm tipindeki nesne akışını tanımlar. Birlikte, bir yazarın klasik bir ayrıştırıcının anahtar kelime olarak aradığı iki yapıyı düşürmesine olanak tanırlar. Bir PDF 1.5 dosyası hiç xref tablosu olmadan bitebilir. Bunun yerine, startxref'in işaret ettiği nesne, sözlüğü /Type /XRef taşıyan sıradan bir akış nesnesidir ve bu akış çapraz referans verilerini kompakt bir ikili biçimde tutar. trailer anahtar kelimesi de yoktur, çünkü trailer artık akışın kendi sözlüğüdür. Klasik bir ayrıştırıcının aradığı /Root, /Size ve /ID anahtarları bu sözlüğün içinde yaşar.
İkinci değişiklik nesnelerin kendilerini taşır. Her dolaylı nesneyi kendi bayt sapmasında yazmak yerine yazar, birçok küçük nesneyi (sayfa sözlüklerini, ek açıklama sözlüklerini, yapı ağacını) tek bir nesne akışında paketleyebilir ve tüm kabı Flate ile sıkıştırabilir. Ayrı nesnelerin artık dosyada bir bayt sapması yoktur. Sıkıştırılmış bir veri bloğu (blob) içinde bir konumları vardır. Ham baytları 1 0 obj için tarayan bir doğrulayıcı bunları asla bulamaz, çünkü bu metin yalnızca şişirmeden (inflation) sonra mevcuttur. Klasik bir ayrıştırıcıya göre belgenin yarısı basitçe ortadan kaybolmuştur。
Sıkıştırılmış bir dosyada bile trailer anahtarları düz metindir
Rahatlatıcı olan kısım, çapraz referans akışının trailer'ını okumanın hiçbir şeyi şişirmeyi gerektirmemesidir. Bir akış nesnesi, bir sözlük ve ardından stream anahtar kelimesi ve ardından sıkıştırılmış baytlar olarak yazılır. Sözlük düz metindir. Bu nedenle, startxref bir çapraz referans akışını işaret ettiğinde, nesne numarasından hemen sonraki baytlar sıradan bir sözlük gibi görünür ve /Root, /Size ve /ID, stream anahtar kelimesi ve Flate verileri başlamadan önce orada açıkça durur.
Bu, bir doğrulayıcının en çok ihtiyaç duyduğu üç gerçeği (kataloğun nerede olduğunu, dosyanın kaç nesne iddia ettiğini ve dosya tanımlayıcısını) yalnızca akış sözlüğünü ayrıştırarak öğrenebileceği anlamına gelir. Çapraz referans verilerinin sıkıştırmasını açması gerekmez ve içindeki ikili girişleri yorumlaması gerekmez. Saf bir ayrıştırıcıyı mağlup eden iş trailer'ı okumak değil, nesneleri bulmaktır. Bunlar iki ayrı problemdir ve ilkini çözmek ucuzdur。
Nesne akışları: bir başlık, ardından bir Flate bloğu
Bir nesne akışı bir kaptır. Sözlüğü /Type /ObjStm, içinde paketlenmiş nesnelerin sayısını veren bir /N girişi ve şişirilmiş veriler içinde ilk nesnenin gövdesinin başladığı bayt sapmasını veren bir /First girişi taşır. Sıkıştırılmış yük, şişirildikten sonra, /N tamsayı çiftlerinden oluşan küçük bir başlıkla başlar. Her çift, bir nesne numarası ve o nesnenin gövdesinin /First'e göre sapmasıdır. Başlıktan sonra nesne gövdelerinin kendileri gelir, birbirine bağlı olarak。
Baytlar şişirildikten sonra birini genişletmek mekanikdir. /N ve /First elde etmek için sözlüğü okur, akışı bir Flate kod çözücüyle şişirir, hangi nesne numarasının hangi sapmada yaşadığını öğrenmek için öndeki /N çiftini yürütür ve ardından sanki sıradan bir dolaylı nesneymiş gibi her gövdeyi dışarı çıkarırsınız. Tek gerçek bağımlılık Flate kod çözücüdür ve buna zaten sahipsiniz: Delphi System.ZLib gönderir ve Free Pascal, her ikisi de zlib'i saran ve harici bir kod olmadan ham bir Flate akışını şişiren zstream birimini gönderir. Çıkarılan her nesneyi doğrulayıcının nesne tablosuna ekleyen bir rutin, doğrulayıcının geri kalanının (sayfa ağacını yürüten ve /Root kontrolü yapan kısım) tıpkı klasik bir dosyadaymış gibi davranmasını sağlar。
Uygulamak zorunda olmadığınız şeyler
İşi abartmak kolaydır. Sıkıştırılmış bir dosyadan trailer anahtarlarını okumak, çapraz referans akışının ikili girişlerinin kodunu çözmeyi gerektirmez. §7.5.8 çapraz referans akışı üç giriş tipi kullanır ve tip 2 girişi (yani "this object lives inside object stream N at index i" diyen), tam bir sapma haritası oluşturmak için kodunu çözeceğiniz şeydir. İsteğe bağlı nesneleri numaraya göre çözmek için bu haritaya ihtiyacınız vardır. Plaintext sözlükte bulunan /Root, /Size ve /ID okumak için buna ihtiyacınız yoktur ve nesne akışlarını genişletmek için buna ihtiyacınız yoktur, çünkü cada /ObjStm kendi içeriğini /N ve /First aracılığıyla duyurur。
Sadece trailer anahtarlarını almak için bir çapraz referans akışının /DecodeParms aracılığıyla uygulayabileceği PNG ve TIFF öngörücü (predictor) fonksiyonlarını da işlemek zorunda değilsiniz. Öngörücüler, daha iyi sıkışmaları için ikili çapraz referans satırlarını filtreler; akıştan önceki sözlükle hiçbir ilgileri yoktur. Bu nedenle klasik bir doğrulayıcıyı modern PDF uyumlu hale getiren minimal yükseltme küçüktür: startxref xref anahtar kelimesi yerine bir akışa indiğinde, trailer anahtarları için akış sözlüğünü ayrıştırın ve karşılaştığınız /ObjStm nesnelerini genişletin, böylece içerikleri nesne tablosuna girer. Tip 2 girişlerinin ve öngörücülerin kodunu çözmek, rastgele nesne çözümlemesine gerçekten ihtiyaç duyana kadar erteleyebileceğiniz daha büyük, isteğe bağlı bir görevdir。
Bir uyumluluk kontrolü neden önce akışları genişletmelidir
Bu durum, bir profil kontrolü çalıştırdığınız an akademik olmaktan çıkar. Bir PDF/A veya PDF/X doğrulayıcı inspects specific objects: document catalog for an /OutputIntents array, the /Metadata stream for an XMP packet with the right identifier, every font descriptor for an embedded font file, the trailer for an /ID. Sıkıştırılmış bir dosyada, bu nesnelerin çoğu nesne akışlarının içindedir. Nesne akışlarını genişletmeyen bir doğrulayıcı kataloğun anahtarlarını göremez, meta verileri bulamaz ve yazı tiplerini listeleyemez. Tamamen uyumlu bir belgeyi çıktı amacından yoksun, XMP'si eksik ve yapısının yarısı eksik olarak rapor edecektir; çünkü ihtiyacı olan kanıt hala hiç şişirmediği bir Flate bloğunun içinde oturmaktadır。
Sıralama önemlidir. Genişletmenin kontroller çalışmadan önce gerçekleşmesi gerekir, onlarla birlikte değil, çünkü her kontrol bir nesneye numarasına göre ulaşabileceğini varsayar. Bir profil kontrolünü doğrudan ham bayt taramasına bağlarsanız, klasik ayrıştırıcının körlüğünü devralır ve tam da çapraz referans akışlarını ilk etapta yazacak kadar yeni araç zincirlerinden çıkan modern dosyalarda yanlış ihlaller üretir。
Ayrıştırmayı PDFium'un sizin için yapmasına izin vermek
PDFium Bileşeni, belgeyi yüklemenin bir parçası olarak çapraz referans akışlarını ve nesne akışlarını ayrıştırır; bu, elle şişirme ve genişletme adımından kaçınmanın pratik yoludur. Bir dosyayı TPdf bileşeniyle yüklediğinizde, /ObjStm kaplarında paketlenmiş nesneler zaten çözülmüştür ve doğrulama giriş noktaları tamamen genişletilmiş belgeyi görür. ValidatePdfA, Conformance alanı pac1b veya pacNone gibi bir TPdfAConformance değeri olan, Issues alanı bulunan sorunların bir kümesi olan ve IsCompliant yöntemi yalnızca bir uyumluluk seviyesi tespit edildiğinde ve sorun kümesi boş olduğunda doğru olan bir TPdfAValidationResult kaydı döndürür. Nesneler yükleme sırasında genişletildiği için, bir nesne akışının içinde yaşayan /OutputIntents dizisi veya gömülü bir yazı tipi bulunur, eksik olarak rapor edilmez。
uses
PDFium, FPdfPdfa;
function CheckPdfA(const FileName: string): TPdfAValidationResult;
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := FileName;
Pdf.Active := True; // parses xref/object streams on load
Result := Pdf.ValidatePdfA; // sees the expanded object table
finally
Pdf.Free;
end;
end;
Aynı durum, aynı şekle sahip bir TPdfXValidationResult döndüren ValidatePdfX için de geçerlidir. PDFium üzerinden yönlendirmenin amacı, yukarıda açıklanan yapısal sıkıştırma açma işleminin yükleyicinin içinde bir kez, doğru şekilde gerçekleşmesidir; böylece doğrulama kodunuz klasik bir dosya ile tamamen sıkıştırılmış bir dosya arasındaki farkı asla görmez. Her ikisi de doğrulayıcıya çözümlenmiş bir nesne kümesi olarak ulaşır。
var
Pdf: TPdf;
R : TPdfXValidationResult;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'Press_Ready.pdf';
Pdf.Active := True;
R := Pdf.ValidatePdfX;
if R.IsCompliant then
Writeln('PDF/X conformance: ', Ord(R.Conformance))
else
Writeln('Not conformant; issue count = ', SizeOf(R.Issues));
finally
Pdf.Free;
end;
end;
Baytlar zaten disk yerine bellekteyse, aynı yükleme ve ardından doğrulama sırası, ham dosya içeriğini alan ve çapraz referans ile nesne akışlarını dosya yolu ile aynı şekilde ayrıştıran LoadDocument(const Data: TBytes) aşırı yüklemesi (overload) aracılığıyla çalışır. Elle yazılmış bir doğrulayıcı için çıkarılacak ders API değil, yapısal kuraldır: trailer anahtarlarını düz metin olarak akış sözlüğünden okuyun, belgede yürümeden önce her /ObjStm nesnesini bir Flate kod çözücüyle genişletin ve ikili çapraz referans girişlerinin kodunu çözmeyi daha büyük, isteğe bağlı bir iş olarak ele alın。
Yapı genişletildikten sonra, bir doğrulayıcı bunun üzerinde iş akışının geri kalanını yürütebilir. Bir girdi klasörü boyunca uyumluluğu raporlayan bir komut satırı uçuş öncesi (preflight) donanımı için, toplu uçuş öncesi raporu CLI'si oluşturma kılavuzumuza bakın. Doğrulama, büyük bir belgeyi parçalara ayırmadan önceki bir geçit olduğunda, PDF belgelerini birden fazla dosyaya bölme kılavuzumuzdaki teknikler, burada gösterilen yükle ve kontrol et kalıbıyla doğal olarak eşleşir. Her ikisi de, Delphi ve C++Builder için PDFium Bileşeninin yükleme ve doğrulama yüzeyine dayanır。