Algılama kodunuz, başka bir üreticiden gelen bir PDF/UA dosyasının hiçbir iddia taşımadığını bildirir ve bir PDF/R taraması, bir düzenleyici ona bir yapışkan not eklediği an PDF/R olmaktan çıkar. HotPDF, yüklenmiş bir belgeden bu iddiaları IsLoadedPDFUA, GetLoadedPDFR1Version ve GetLoadedPDFDeclarations gibi erişicilerle okur; her biri, kendi standardının tanımlayıcıyı sakladığı yere bakar
Her PDF standardı tanımlayıcısını nereye yazar
Yan standartlar iddia için tek bir yerde hiç anlaşamadı. Çoğu, kataloğun /Metadata akışındaki XMP özelliklerini kullanır; birkaç eskisi belge bilgi sözlüğündeki anahtarlara hâlâ güvenir; PDF/R dosya trailer'ının yanına bir yorum satırı koyar; PDF 2.0 kuşağı ise iki mekanizma daha ekler: katalogdaki geliştirici uzantı sözlükleri ile XMP içindeki PDF Declarations. Bir tanımlayıcı, dosyanın kendisi hakkındaki kendi beyanıdır; içeriğin buna uyup uymadığına bir doğrulayıcı karar verir ve uyumlu PDF/A, PDF/X ve PDF/UA çıktısı üretmek başlı başına bir konudur. Yöneltme için (PDF/X için baskı kuyruğu, PDF/A için arşiv, WTPDF için erişilebilirlik hattı) gereken şey iddiadır; doğru yerden okuyun:
| Standart | İddianın yeri | HotPDF erişicisi |
|---|---|---|
| PDF/A (ISO 19005) | XMP pdfaid:part ile pdfaid:conformance, PDF/A-4'te ayrıca pdfaid:rev | IsLoadedPDFA, GetLoadedPDFAVersion, GetLoadedPDFARevision |
| PDF/X (ISO 15930) | PDF/X-1a ve PDF/X-3 için Info sözlüğünde GTS_PDFXVersion, sonraki bölümler için XMP'te | IsLoadedPDFX, GetLoadedPDFXVersion |
| PDF/UA (ISO 14289) | XMP pdfuaid:part | IsLoadedPDFUA, GetLoadedPDFUAVersion |
| PDF/VT (ISO 16612-2) | XMP pdfvtid:GTS_PDFVTVersion | IsLoadedPDFVT, GetLoadedPDFVTVersion |
| PDF/E (ISO 24517-1) | ISO_PDFEVersion; Info sözlüğünden, katalogdan ya da XMP'ten okunur | IsLoadedPDFE, GetLoadedPDFEVersion |
| PDF/R-1 (ISO 23504-1) | Son startxref'in hemen üstünde %PDF-raster-x.y yorumu | IsLoadedPDFR1, GetLoadedPDFR1Version |
| PDF/VCR-1 (ISO 16613-1) | Katalogda ya da XMP'te GTS_PDFVCRVersion | IsLoadedPDFVCR, GetLoadedPDFVCRVersion |
| WTPDF 1.0, ISO/TS 32005 | XMP'te pdfd:declarations girdileri | IsLoadedWTPDF, GetLoadedWTPDFVersion, IsLoadedISO32005 |
| PDF 2.0 geliştirici uzantıları | Katalogda /Extensions /ISO_ | HasLoadedISODeveloperExtension, GetLoadedISODeveloperExtensionLevels |
uses
System.SysUtils, HPDFDoc;
procedure ReportClaims(const FileName: string; const Password: AnsiString);
var
Pdf: THotPDF;
PDFX, PDFXLevel: AnsiString;
begin
Pdf := THotPDF.Create(nil); // yalnızca inceleme için kullanılan ayrı bir örnek
try
// Şifre overload'u akışları, XMP paketi dahil, çözer;
// açılırken istem çıkarmayan dosyalar için '' geçirin
if Pdf.LoadFromFile(FileName, Password) <= 0 then
raise Exception.CreateFmt('Cannot open %s', [FileName]);
if Pdf.IsLoadedPDFA then
Writeln('PDF/A: ', Pdf.GetLoadedPDFAVersion); // örn. PDF/A-2b
PDFX := Pdf.GetLoadedPDFXVersion; // önce XMP, sonra Info
if PDFX <> '' then
begin
PDFXLevel := Pdf.GetLoadedInfoKeyValue('GTS_PDFXConformance');
if PDFXLevel <> '' then
Writeln('PDF/X: ', PDFX, ', conformance ', PDFXLevel)
else
Writeln('PDF/X: ', PDFX);
end;
if Pdf.IsLoadedPDFUA then
Writeln('PDF/UA: ', Pdf.GetLoadedPDFUAVersion); // örn. PDF/UA-1
if Pdf.IsLoadedPDFVT then
Writeln('PDF/VT: ', Pdf.GetLoadedPDFVTVersion);
if Pdf.IsLoadedPDFE then
Writeln('PDF/E: ', Pdf.GetLoadedPDFEVersion);
if Pdf.IsLoadedPDFR1 then
Writeln('PDF/R: ', Pdf.GetLoadedPDFR1Version); // örn. PDF/R-1.0
if Pdf.IsLoadedWTPDF then
Writeln('WTPDF: ', Pdf.GetLoadedWTPDFVersion);
finally
Pdf.Free;
end;
end;
İnceleme için örnekteki gibi ayrı bir THotPDF örneği kullanın. IsLoaded… işlevleri, bileşenin elindeki belge için yanıt verir; bileşenin henüz inşa ettiği belge dahil: çıktı için PDFUACompliance açılmışken IsLoadedPDFUA da True döner ve IsLoadedPDFX, boş olmayan bir PDFXCompliance ile aynı fikirde olur. İnşaat ortasında kullanışlıdır; ama iki iş için birden kullanılan bir bileşende, başkasının dosyası hakkındaki hükme kendi çıktı ayarlarınızı karıştırır
PDF/X, tanımlayıcısı bölümler arasında yer değiştiren tek ailedir. PDF/X-1a ile PDF/X-3, GTS_PDFXVersion'u belge bilgi sözlüğüne koyan ve hiç XMP istemeyen PDF 1.3 profilleridir; PDF/X-4 ve sonraki bölümler ise pdfx:GTS_PDFXVersion XMP özelliğini zorunlu kılar. GetLoadedPDFXVersion önce XMP'e — pdfx ya da pdfxid öneki altında — sonra Info sözlüğüne, sonra da bazı eski üreticilerin yazdığı katalog anahtarlarına bakar ve bildirilen değeri hiç dokunmadan döndürür. PDF/X-1a'nın kodlanmaya değer bir kıvrımı vardır: sürüm değeri PDF/X-1:2001'dir ve PDF/X-1a:2001 uyumluluk düzeyi kendi anahtarında, /GTS_PDFXConformance'te yaşar; örnek, düz X-1'i X-1a'dan ayırmak gerektiğinde bunu GetLoadedInfoKeyValue ile okur
Geçerli bir XMP iddiası nasıl görünür?
XMP, XML olarak serileştirilmiş RDF'tir ve RDF/XML, basit bir özelliğin bir rdf:Description içinde iki türlü yazılmasına izin verir: çocuk öğe olarak, <pdfuaid:part>1</pdfuaid:part>; ya da tanımın kendisinde bir öznitelik olarak, tek ya da çift tırnakla pdfuaid:part="1". ISO 16684-1 ikisini de kabul eder ve bir paket özelliklerini birkaç rdf:Description öğesine bölebilir. Bölme hipotetik değildir. HotPDF, PDF/UA, PDF/VT, PDF/E ve PDF/X için çocuk öğe biçimini yazar; veraPDF test korpusu ile pek çok dış üretici ise kısa yazımı yazar — pdfuaid:part="1" ya da pdfvtid:GTS_PDFVTVersion="PDF/VT-1" gibi tanımlar tüm iddiayı özniteliklerde taşır. Pos('<pdfuaid:part>', Xmp) çevresine kurulmuş ve yalnızca kendi içi çıktıyla sınamış bir algılayıcı, tüm yerel denetimleri geçer ve sonra ilk veraPDF örneğini iddiasız sanır. HotPDF'in XMP okuyucusu tablodaki her erişici için iki biçimi de işler. GetLoadedPDFAVersion, pdfaid:part ile pdfaid:conformance'ı PDF/A-1b, PDF/A-2u, PDF/A-4 ya da PDF/A-4f gibi tanımlayıcılara birleştirir; GetLoadedPDFARevision ise PDF/A-4'ün zorunlu kıldığı (2020) ve 1-3 bölümlerinin tanımlamadığı baskı yılını, pdfaid:rev'i döndürür:
procedure ReportPDFALevel(Pdf: THotPDF);
var
Level, Revision: AnsiString;
begin
Level := Pdf.GetLoadedPDFAVersion; // 'PDF/A-1b', 'PDF/A-2u', 'PDF/A-4f', ...
if Level = '' then
Exit;
Revision := Pdf.GetLoadedPDFARevision; // PDF/A-4 için '2020', 1-3 bölümleri için boş
if Revision <> '' then
Writeln(Level, ', edition ', Revision)
else
Writeln(Level);
// Beklenen: üretici <pdfaid:part>2</pdfaid:part> yazsa da
// pdfaid:part="2" yazsa da sonuç "PDF/A-2b" olur
end;
Önekler hareketli diğer parçadır. XML'de önek yalnızca yerel bir takma addır; PDF/A şemasını tanımlayan şey http://www.aiim.org/pdfa/ns/id/ ad alanı URI'sidir — onaylı ISO 19005-4 errata'sı onun https:// yazımını da kabul eder — ve bir üretici onu istediği öneke bağlayabilir. GetLoadedPDFAVersion, part ile düzeyi okumadan önce paketin o URI için bildirdiği öneki arar; dolayısıyla pdfaid:part yerine pdfa:part yazan bir paket de tanınır. GetLoadedXMPMetadata'nın döndürdüğü çözülmüş paketten kendi başınıza okuduğunuz her başka XMP özelliği aynı özeni hak eder: her iki serileştirme, xmlns bildiriminden çözülmüş önek ve eşittir işaretinin çevresine izin verilen boşluk
Şifreli dosyalar iddiayı bayt aramalarından gizler
Ham dosyada pdfaid ya da ISO_PDFEVersion aramak ayrıştırmayı atlar ve iki sıradan durumda yanılır. XMP akışlarının çoğu Flate ile sıkıştırıldığından özellik adları diskte metin olarak hiç görünmez. Ve şifreli bir belgede meta veri akışı şifreli metindir: ISO 32000-2 §7.6.4.2, varsayılanı true olan EncryptMetadata'yı tanımlar; üretici açıkça vazgeçmedikçe paket, öbür her akışla birlikte şifrelenir. Dolayısıyla şifreli bir PDF/E-1 dosyasına karşı yapılan bayt araması, ISO_PDFEVersion iddiası oradayken bile eksik bildirir. Güvenilir sıra şudur: yükle, şifreyi çöz, sıkıştırmayı aç, sonra ara. Dosyayı şifre overload'u, LoadFromFile(FileName, Password) ile açın; yalnızca bir owner password taşıyan ve herhangi bir görüntüleyicide istemsiz açılan dosyalar için boş dizgi geçirin. Tek argümanlı LoadFromFile(FileName) belge yapısını akışlarını çözmeden yükler ve XMP paketini okunmaz bırakır; şifre overload'unun ardından GetLoadedXMPMetadata ve yukarıdaki her erişici, çözülmüş ve açılmış paket üzerinde çalışır
Bir PDF/R dosyası düzenlemeden sonra kimliğini neden yitirir?
PDF/R-1 kendini, hiçbir yan standardın yapmadığı bir biçimde tanıtır. ISO 23504-1 madde 5, startxref taşıyan son satırın hemen üstündeki satırda %PDF-raster-1.0 (ya da geçerli olan hangi sürümse o) okuyan bir yorum satırı ister. Bir PDF ayrıştırıcısı çapraz başvuru verisini okurken yorumları çöpe atar; dolayısıyla nesne modelinin bildireceği bir şey yoktur ve kanıt yalnızca dosyanın sonundaki ham baytlarda durur. GetLoadedPDFR1Version belgenin yüklendiği kaynağa geri gider, en çok son 64 KiB'yi okur, son startxref'i bulur ve üstündeki satıra bakar. Şimdi bir artımlı güncellemeyi izleyin. Ek açıklama ekleyen bir düzenleyici, özgün %%EOF'un ardına yeni nesneler, yeni bir çapraz başvuru bölümü, yeni bir trailer ve yeni bir startxref ekler. %PDF-raster-1.0 satırı hâlâ dosyadadır ama artık sondan ikinci startxref'in üstünde durur ve standart hangisinin sayıldığı konusunda hiç belirsiz değildir. Beklenen: düzenlemeden sonra IsLoadedPDFR1 False döner; düzenleme aracı yorumu kendi güncellemesinde yeniden yazmadıkça. Tüm dosyada %PDF-raster- tarayan bir algılayıcı evet der, oysa madde 5 kuralına göre bu yanıt yanlıştır. Dosyalarını PDF/R tutması gereken bir yakalama iş akışı, ek açıklamaları bir yan dosyaya ya da tüm dosyayı PDF/R olarak yeniden yazan bir araca bırakır
Geliştirici uzantıları: tamsayı düzeyler, adlar ve diziler
PDF 2.0 belgeleri, güvendikleri ISO teknik şartnamelerini (ISO/TS 32001'den SHA-3 karmaları, ISO/TS 32002'den ECC imzaları, ISO/TS 32007'den glTF varlıkları) kataloğun /Extensions sözlüğüyle duyurur; sözlük ISO 32000-2 §7.12'de tanımlıdır. Kayıtlı /ISO_ öneki altında Tablo 48, ya tek bir geliştirici uzantı sözlüğüne ya da — PDF 2.0'la gelen yenilik — onlardan oluşan bir diziye izin verir; Tablo 49, §7.12.5 ile birlikte /ExtensionLevel'i bir tamsayı yapar. İki kap biçiminden yalnızca birini işleyen bir okuyucu, öbür türlü yazılmış her dosyayı kaçırır. HasLoadedISODeveloperExtension ile GetLoadedISODeveloperExtensionLevels ikisini de kabul eder ve yol boyunca dolaylı başvuruları çözer; gerçi §7.12.1 bu sözlüklerin doğrudan nesne olması gerektiğini söyler:
procedure ReportISOExtensions(Pdf: THotPDF);
var
Levels: THPDFIntegerArray;
Revision, URL: AnsiString;
I: Integer;
begin
Levels := Pdf.GetLoadedISODeveloperExtensionLevels;
for I := 0 to High(Levels) do
Writeln('ISO_ extension level ', Levels[I]);
if Pdf.HasLoadedISODeveloperExtension(32001, Revision, URL) then
Writeln('ISO/TS 32001 revision ', Revision, ', documented at ', URL);
// EnsureISO32001Extensions ile yazılmış bir dosya için beklenen:
// ISO/TS 32001 revision :2022, documented at https://www.iso.org/standard/45874.html
if Pdf.HasLoadedISODeveloperExtension(32007) then
Writeln('glTF 3D assets per ISO/TS 32007');
end;
ISO/TS 32007:2024'ün ilk baskısı §4.8'de /ExtensionLevel /32007 yazan bir örnek taşır; PDF Errata Issue #835 bunu 32007 tamsayısına düzeltti ama özgün örneği kopyalayan bir üretici hâlâ adı yazar. HotPDF'in okuyucusu bu yüzden tamsayı araması boş döndüğünde, baştaki eğik çizgili ya da çizgisiz, bir ad ya da dizgi değerini ayrıştırmaya geçer; dolayısıyla HasLoadedISODeveloperExtension(32007) her iki yazımı da bulur. Öbür erratum /URL'ü ilgilendirir: ISO 32000-2:2020'nin Tablo 49'u /URL'ü isteğe bağlı sayar ve Issue #732 onu zorunlu düzeye çıkarır. Yazma tarafında HotPDF her EnsureISO…Extensions çağrısında tamsayı biçimini ve bir /URL'yi basar; okuma tarafında 2020 metnine göre yazılmış dosyalar için boş bir URL çıkış parametresi normaldir — bunu bir bilgi olarak görün, uzantıyı reddetmek için hiçbir zaman bir neden olarak değil
Üretici hangi WTPDF URI'sini yazdı?
WTPDF 1.0 ve ISO/TS 32005 iddiaları PDF Declarations'tır: XMP'te, PDF Association'ın PDF Declarations teknik notunun tanımladığı http://pdfa.org/declarations/ ad alanı altındaki bir pdfd:declarations torbasında duran girdiler. Her girdi bir conformsTo URI'si ile isteğe bağlı claimBy, claimDate, claimCredentials ve claimReport alanları taşır; GetLoadedPDFDeclarations bunları bir THPDFDeclarationEntry kayıtları dizisi olarak döndürür. Yazma tarafında AddPDFDeclaration aynı beş alanı XMP paketine, PDF 2.0, PDF/A-4 ve PDF/UA-2 kimlik ad alanlarının yanına yerleştirir. Torbayı düz metin aramasıyla girdilere bölmek kendi tuzağını taşır: her conformsTo geçişinde bölün; çocuk öğe biçiminin kapanış etiketi </pdfd:conformsTo> yeni bir girdinin başlangıcı gibi sayılır ve ilk bildiriyi kendi sonundan önce keser. HotPDF okuyucusu kapanış etiketlerinin üzerinden geçer ve iki serileştirmeyi de kabul eder
Zor taraf asıl URI'lerdir. Aynı iddia size http:// ya da https:// olarak ulaşabilir ve WTPDF 1.0 Issue #395, #'den önce / oturup oturmayacağını değiştirdi; dolayısıyla erratum öncesi ve sonrası yazılan dosyalar tek o karakterde ayrışır ve düz bir dizgi karşılaştırması onları farklı standartlar sayar. HasLoadedPDFDeclaration URI'leri büyük küçük harf duyarsız karşılaştırır, https://'yi http://'ye katlar ve iki tarafta da #'nin hemen önündeki /'yi düşürür; dolayısıyla aşağıdaki sorgu, üreticinin hangi yazımı seçtiğine bakmaksızın eşleşir. Parça bozulmadan kalır: #accessibility1.0 ile #accessibility1.0-validated farklı iddialardır; ikincisi, bir doğrulayıcının dosyayı denetlediğini öne sürer ve iş akışınızın ilgili olduğu iddiayı isteyen taraf siz olun. GetLoadedWTPDFVersion yöneltme için ikisini tek bir 'WTPDF 1.0 Accessibility' ya da 'WTPDF 1.0 Reuse''a indirger
procedure ReportDeclarations(Pdf: THotPDF);
var
Decls: THPDFDeclarationEntryArray;
I: Integer;
begin
Decls := Pdf.GetLoadedPDFDeclarations;
for I := 0 to High(Decls) do
Writeln(Decls[I].ConformsTo, ' claimed by ', Decls[I].ClaimBy,
' on ', Decls[I].ClaimDate);
// http ya da https ile eşleşir; '#'den önce eğik çizgi olsun olmasın
if Pdf.HasLoadedPDFDeclaration(
'https://pdfa.org/declarations/wtpdf#accessibility1.0') then
Writeln('WTPDF 1.0 Accessibility (unvalidated claim)');
// Reuse ile Accessibility'in düz ve -validated çeşitlerini birlikte kapsar
Writeln('WTPDF profile: ', Pdf.GetLoadedWTPDFVersion);
end;
Bir yakalama ya da baskı yönelticisi için işlemlerin sırası herhangi bir tekil erişiciden daha önemlidir: IsLoadedPDFR1'i dosya daha yeni gelmişken, bir ek açıklama ya da damga adımı güncellemesini eklemeden önce çalıştırın; PDF/X'i GetLoadedPDFXVersion ile yöneltin — bu, PDF/X-1a ve PDF/X-3 işlerinin Info sözlüğünü de PDF/X-4 işlerinin XMP'ini de kapsar ve /GTS_PDFXConformance yalnızca X-1a'nın düz X-1'den ayırt edilmesi gereken yerde okunur. Her iki denetim de, şifresiyle açılmış şifreli dosyalar dahil, HotPDF Delphi PDF component'üne yüklenen her belgede çalışır