Bir PDF/A-3 kontrol listesi genellikle XMP paketinin iki kimlik girdisi taşıması gerektiğini söyler: pdfaid:part ve pdfaid:conformance. Doğru yazılmış bir PDF/A-4 dosyası pdfaid:part 4 ile pdfaid:rev 2020 taşır ve taban profil için hiç conformance girdisi yoktur. Onu "düzeltmek" için bir harf eklemek dosyayı yanlış yapar: ISO 19005-4, taban parçayı numarayla ve revizyon yılıyla tanımlar ve yalnızca iki uzantı bir harf korur
PDF/UA-2'nin bir katman aşağıda, yapı ağacının içinde benzer bir tuzağı vardır. Kendi ad alanında özel bir rol bildirmek, işin tamamı gibi görünür; çünkü ad alanları, parça 2'nin eklediği özelliktir. İşin yarısıdır. https://example.com/ns/widgets içindeki WidgetGroup gibi bir rol, uyumlu bir denetleyici onu kabul etmeden önce yine de bir standart role eşlenmek zorundadır ve görüntülenen sayfada hiçbir şey, eşlemenin eksik olduğuna işaret etmez
PDFACompliance ile PDF/A-4 seçmek
HotPDF'te PDF/A-4 profilleri, var olan PDFACompliance özelliğinin üç değeridir: taban parça için '4', mühendislik belgeleri için '4E' ve gömülü dosya taşıyan dosyalar için '4F'. '4' seçiliyken XMP paketi, pdfaid ad alanından başka hiçbir şey almaz; yalnızca pdfaid:part 4 ve pdfaid:rev 2020. '4E' ile '4F', E ya da F taşıyan pdfaid:conformance ekler. Çözücü işlevleri aynı ayrımı raporlar: PDFAPart üç değer için de 4 döndürür, PDFAConformanceLevel ise E, F ya da taban profil için #0 döndürür
Üçü de bir PDF 2.0 başlığı zorlar. Yükseltme EndDoc içinde olur; dolayısıyla PDF 1.4'te başlayan bir belge, ayrı bir çağrı olmadan 2.0 olarak çıkar. Onu durduran tek durum StrictVersionLock'tur. O bayrak otomatik yükseltmeleri yasaklar; dolayısıyla EndDoc, meta verisinde parça 4 iddia eden bir 1.7 dosyası yazmak yerine "PDF/A-4 requires PDF 2.0; StrictVersionLock prevents the version upgrade" fırlatır. Eski bir baskı iş akışı için sürümü sabitlemiş bir kod tabanı, biri profili çevirdiği ilk anda tam o iletiyi görür
İki PDF/A-4 kuralı, treylere ve Catalog'a ne düşeceğini değiştirir. ISO 19005-4 §6.1.3, bir belge bilgi sözlüğüne yalnızca Catalog'un PieceInfo girdisi taşıdığında izin verir ve o zaman bile onu ModDate ile sınırlar; dolayısıyla HotPDF parça 4 için Info sözlüğü yazmaz ve başlık, XMP'de dc:title olarak yol alır. Title ve Lang hâlâ zorunludur. İkisinden birini boş bırakın, EndDoc fırlatır. AddPDFAOutputIntent ile hiçbir çıktı niyeti eklenmemişken de fırlatır ve EnableEncrypt, bir PDF/A profil seçiliyken çalışmayı reddeder; çünkü şifreli bir arşiv dosyası, standardın izin vermediği bir çelişkidir
PDF/A-4f ve 4e altında ilişkili dosyalar
İlişkili dosyalar, PDF/A-4f seçmenin baş nedenidir: görüntülenen sayfanın arkasında bir XML fatura, aşağı akış bir sistemin ayrıştırdığı bir JSON yükü. AddPDFAssociatedFile bir dosya adı, bir MIME türü, bir açıklama, bir AFRelationship değeri ve baytları alır ve kendinizin isteğe bağlı anahtarlar ekleyebilmesi için dolaylı FileSpec sözlüğünü döndürür
var
Pdf: THotPDF;
Icc: TFileStream;
begin
Pdf := THotPDF.Create(nil);
Icc := TFileStream.Create('sRGB2014.icc', fmOpenRead or fmShareDenyWrite);
try
Pdf.FileName := 'invoice-archive.pdf';
Pdf.PDFACompliance := '4F'; // parça 4, rev 2020, conformance F
Pdf.Title := 'Invoice 2026-0731'; // yalnızca XMP dc:title'e yazılır
Pdf.Lang := 'en-US';
Pdf.BeginDoc;
Pdf.AddPDFAOutputIntent('sRGB IEC61966-2.1', 'sRGB IEC61966-2.1',
Icc, 3, 'DeviceRGB');
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Invoice 2026-0731');
// LoadInvoiceBytes, TBytes döndüren kendi işlevinizdir
Pdf.AddPDFAssociatedFile('invoice.xml', 'text/xml',
'Structured invoice data', 'Data', LoadInvoiceBytes);
Pdf.EndDoc;
finally
Icc.Free;
Pdf.Free;
end;
end;
Her çağrı dört şey yapar. /Subtype'ı MIME türünü bir PDF adı olarak taşıyan bir EmbeddedFile akışı yazar; boyutu, bir değişiklik tarihini ve bir MD5 sağlama toplamını taşıyan bir /Params sözlüğüyle. Hem /F hem /UF'i, açıklamayı /Desc olarak ve ilişki adını taşıyan bir FileSpec kurar. Sonra o FileSpec'i iki kez kaydeder: Catalog /AF dizisine ve EmbeddedFiles ad ağacına. İki kayıt da zorunludur. Yalnızca bir yerde kayıtlı bir dosya, bir görüntüleyicinin ek listesinden yine de açılabilir; bir arşiv hakkında görsel bir yoklama denetiminin pek az şey kanıtlamasının nedeni budur
İlişki dizgisi Source, Data, Alternative, Supplement ya da Unspecified'den biri olmalıdır; başka her şey, tek bir bayt yazılmadan fırlatır ve boş bir MIME türü de öyle yapar. Profil de denetlenir. AddPDFAssociatedFile PDF/A-3, PDF/A-4e ve PDF/A-4f kabul eder ve taban '4' profili, ilişkili dosyalara izin vermediği için fırlatır. Ters yönde AddDocumentAttachment, PDF/A-3, 4e ve 4f altında çalışmayı reddeder; çünkü düz bir ek, o profillerin ihtiyaç duyduğu /AF kaydından yoksundur. PDF/A-3 için AddPDFA3AssociatedFile'e karşı yazılmış kod derlenmeye devam eder ve aynı davranır; AddPDFAssociatedFile, o yöntemin profilden bağımsız adıdır
İki uzantı, kalan izinleri aralarında bölüşür. '4F' altında EndDoc, Catalog'un bir EmbeddedFiles ad ağacı taşıdığından emin olur ve AddFileAttachmentAnnotation kabul edilir; taban profil ile 4e onu reddeder. '4E' altında Add3DAnnotation ve AddRichMediaAnnotation ile SetOCGState ve GoTo3DView eylemleri izinlidir. Diğer her PDF/A profili dördünü de reddeder
Ad alanlı özel bir rol neden hâlâ PDF/UA-2'de başarısız olur?
Özel bir rol, yalnızca iki koşul sağlandığında PDF/UA-2'den geçer: /NS girdisi, yapı ağacı kökünde listelenmiş bir ad alanı sözlüğüne işaret eder ve rol, o ad alanının RoleMapNS'i üzerinden standart bir ad alanındaki bir role çözümlenir. HotPDF işi üç yönteme yayar. RegisterStructureNamespace, dolaylı bir /Type /Namespace sözlüğü oluşturur, onu StructTreeRoot /Namespaces'e ekler ve döndürür; aynı URI ile ikinci bir çağrı, yenisini eklemek yerine var olan sözlüğü döndürür. AddStructureElementNS öğeyi oluşturur, /NS'sini atar ve yol üzerinde ad alanını kaydeder. AddStructRoleMapNS eşlemeyi yazar
const
PDF2NS = 'http://iso.org/pdf2/ssn';
WidgetNS = 'https://example.com/ns/widgets';
var
DocElem, Group: THPDFDictionaryObject;
begin
Pdf.PDFUACompliance := True;
Pdf.PDFUAPart := 2; // PDF/UA-2, PDF 2.0 ister
Pdf.Title := 'Widget catalog'; // boş Title, EndDoc'ta fırlatır
Pdf.Lang := 'en-US';
Pdf.BeginDoc;
DocElem := Pdf.AddStructureElement('Document', nil); // PDF2NS'e bağlı
Group := Pdf.AddStructureElementNS('WidgetGroup', WidgetNS, DocElem);
// bu eşleme olmadan EndDoc ua2.rolemap.unresolved fırlatır
Pdf.AddStructRoleMapNS('WidgetGroup', WidgetNS, 'Div', PDF2NS);
Pdf.CurrentPage.SetFont('Arial', [], 18);
Pdf.CurrentPage.BeginMarkedContentMCID('H1', 0);
Pdf.CurrentPage.TextOut(50, 760, 0, 'Widgets');
Pdf.CurrentPage.EndMarkedContent;
Pdf.AddStructureElement('H1', Group, 0, 0); // sayfa indeksi 0, MCID 0
Pdf.EndDoc;
end;
O kaydın bir kısmı istenmeden olur. PDFUAPart 2'ye ayarlıyken AddStructureElement('Document', nil) öğeyi PDF 2.0 standart ad alanına, http://iso.org/pdf2/ssn'e bağlar; çünkü bir PDF/UA-2 ağacının kökü, o ad alanındaki bir Document olmak zorundadır. Aynı bağlama, PDF 2.0 söz dağarcığındaki her rol için olur; dolayısıyla H1 ile P bir ad alanı bağımsız değişkeni istemez. Yalnızca PDF 1.7 kümesinde bulunan roller — Note, Code ya da BlockQuote gibi — 1.7 ad alanında kalır
Eşleme hedefi gerçek bir seçimdir. AddStructRoleMapNS'e boş bir hedef ad alanı geçmek kısa biçimi yazar: PDF 1.7 kümesine göre çözümlenen çıplak bir rol adı. PDF 2.0 URI'sini geçmek, iki öğeli [role namespace] biçimini yazar. Bir PDF/UA-2 belgesinde PDF 2.0 ad alanına eşleyin: bütün ağaç o zaman tek bir söz dağarcığına çözümlenir ve 1.7 karşılığı olmayan Aside ya da FENote gibi hedefler kullanılabilir olur. İki eşleme biçimi EndDoc'ta tamamen reddedilir: kendine geri dolanan bir zincir ve açık bir ad alanındaki rolün, o aynı ad alanındaki başka bir role eşlenmesi
Çağrı sırası göründüğünden gevşektir. Eşleme, öğeden önce de sonra da eklenebilir; çünkü ağaç bitene kadar onu denetleyen hiçbir şey yoktur. Anında başarısız olan şey, PDFUAPart 1 iken AddStructureElementNS'dir. Parça 1'in ad alanı yoktur ve çağrı, yerinde "structure namespaces require PDFUAPart=2" fırlatır
EndDoc, dosya yazılmadan önce neyi denetler
PDFUACompliance açıkken ve PDFUAPart 2 iken EndDoc, hiçbir şey serileştirmeden önce eksiksiz bir PDF/UA-2 yapı doğrulaması çalıştırır ve ilk hata bir istisna olur. Dört denetimi ağacın iskeletini korur. Kök, tam olarak bir üst düzey Document öğesi taşımalıdır. Her ad alanı sözlüğü dolaylı olmalı, Namespace yazılmalı ve yalnızca bir kez görünen boş olmayan bir URI taşımalıdır. Her /NS başvurusu, kök /Namespaces dizisinde listelenmiş bir sözlüğe çözümlenmelidir. Ve her rol, doğrudan ya da RoleMap ve RoleMapNS üzerinden standart bir ad alanına çözümlenmelidir. Document öğesinden ulaşılamayan öğeler de raporlanır
Aynı geçiş, belge düzeyi gereksinimleri de kapsar: boş olmayan geçerli bir Lang; true'ya çekilmiş MarkInfo /Marked; açık DisplayDocTitle; ve boş olmayan bir dc:title ile pdfuaid:part 2 taşıyan bir XMP paketi. HotPDF, PDF/UA açıldığında işaretli bayrağını, görüntüleyici tercihini ve XMP kimlendirmesini kendisi atar ve parça numarasının yanına pdfuaid:rev 2024 yazar. Başlık ve dil sizin sağlamanız gereken şeydir; ikisinden biri için boş bir değer, doğrulayıcı daha başlamadan fırlatır. Başlıklar, listeler, tablolar, ek açıklamalar ve formlar aynı çalışmada denetlenir
EndDoc'a kadar beklemek bilinçlidir. Üretimin ortasında bir ağaç haklı olarak eksiktir: Document öğesinin henüz çocuğu olmayabilir, bir ad alanı hiçbir öğe kullanmadan kaydedilmiş olabilir, bir eşleme öğesinden sonra gelebilir. Her çağrıda denetlemek o durumları reddederdi. EndDoc'ta ağaç bitmiştir ve hâlâ bellektedir; istisna bir kural kodunu ve onu bozan öğenin yolunu adlandırır
try
Pdf.EndDoc;
except
on E: Exception do
begin
// E.Message şu biçimdedir
// PDF/UA-2 validation failed [ua2.heading.skipped at ...]:
// Başlık düzeyi H2'den H4'e atlar
LogBuildFailure(Pdf.FileName, E.Message); // kendi günlükçünüz
raise;
end;
end;
PDF/A-4 çıktısı, kendi doğrulayıcısından aynı işlemi görür. PDFACompliance bir parça 4 değeri taşıdığında EndDoc, üretilen grafiği ISO 19005-4'e karşı denetler ve kural koduyla fıkra birlikte fırlatır: geçerli bir ilişkisi olmayan bir FileSpec için pdfa4.filespec.relationship, başıboş bir Info anahtarı için pdfa4.trailer.info_entries gibi. Enterprise sürümü iki doğrulayıcıyı da ValidatePDFUA2Structure ve ValidatePDFA4 olarak açar; bunlar ilk hatada durmak yerine, korunan her tanıyı bir sorun dizisine doldurur. Eksiksiz bir liste basması gereken bir CI adımı için daha iyi giriş noktası odur
PDF 2.0 rolleri ve H6 ötesi başlıklar
Türlü THPDFStandardStructureType numaralandırması sstDocumentFragment, sstAside, sstTitle, sstFENote, sstSub, sstEm, sstStrong ve sstArtifact kazanır. Üçü, sıradan iş belgelerinin nasıl etiketlendiğini değiştirir. Aside, kenar çubuklarına ve dikkat çekici alıntılara, yanlış kullanılmış bir Sect olmayan bir yurt verir. FENote dipnotları ve son notları not olarak işaretler; böylece bir ekran okuyucu onları cümlenin ortasında okumak yerine talep üzerine sunabilir. Em ve Strong vurguyu yapı olarak taşır; bir PDF 1.7 ağacı onu yalnızca bir Span içine sarabiliyordu
Başlıklar PDF 2.0'da açık uçludur. Numaralandırma sstH6'da durur ama AddStructureElement'in string overloadu H7, H8 ve ötesini kabul eder; çünkü PDF 2.0 kümesi, 1'den yukarı her n için Hn tanımlar. PDF 1.7 H6'da durdu; bu, derin teknik belgeleri taslaklarını düzleştirmeye ya da düzeyleri yeniden kullanmaya zorladı. Standart belgeleri, hukuk metinleri ve parça katalogları için tek başına bu, çıktıyı PDF 2.0'a taşımayı gerekçelendirebilir
Daha derin başlıklar daha sıkı bir kuralla gelir. EndDoc içindeki PDF/UA-2 geçişi, başlık düzeylerinin ardışık olmasını ister: ilk başlık H1 olmalıdır ve H2'den H4'e atlama ua2.heading.skipped ile fırlatır. Bir hiyerarşi yalnızca bir DocumentFragment içinde — daha büyük bir belgeden alınmış içerik için düşünülmüş rol — daha aşağıdan başlayabilir. Bir Markdown ya da HTML taslağını doğrudan Hn rollerine eşleyen bir üretici, yazarın görsel boyut için atladığı her düzeyi devralır; dolayısıyla taslağı etiketlemeden önce normalleştirin
Üretim çıktısını PDF 2.0'a geçirmeden önce
PDF 2.0 bir başlık değişikliğiyle başlar ve başlık, eski tüketicilerin ilk okuduğu şeydir. Yalnızca PDF 1.7'ye kadar kabul eden bir alma hizmeti, baskı iş akışı ya da işletme görüntüleyicisi, dosyanın geri kalanı ne kadar temiz olursa olsun %PDF-2.0'da durur. Herhangi bir PDF/A-4 profili ya da PDF/UA parça 2 seçmek, isteseniz de istemeseniz de PDF 2.0 seçer; dolayısıyla her tüketen sistemi geçişten önce doğrulayın, sonrasında değil
En düşük riskli yayılım, kontrol etmediğiniz okuyuculara giden belgeler için PDF/A-3'ü tutar, alma tarafının sizde olduğu iç arşivler için PDF/A-4f kullanır ve bir erişilebilirlik politikasının adını geçtiği yerde PDF/UA-2'yi benimser. StrictVersionLock geçiş sırasında bir bekçi olarak da iş görür: 1.7'de kalması gereken kod yollarında Version'u pdf17'ye çekin ve kilidi açın; yanlışlıkla yapılan bir PDF/A-4 seçimi, dosyayı açamayacak bir sisteme ulaşmak yerine EndDoc'ta bir iletle başarısız olur. PDF/A, PDF/X ve PDF/UA doğrulaması ile PDF/A-3 üzerinde ZUGFeRD ve Factur-X hibrit faturaları rehberleri, sürüm numarasından önce gelen profil kararlarını kapsar; otomatik preflight raporlama notları ise kararı bir derleme adımına çevirmeyi gösterir
Bunun tamamı, Delphi ve C++Builder bileşeninin içinde yerel VCL kodu olarak çalışır; dolayısıyla PDF/A-4 ve PDF/UA-2 çıktısı yürütülebilirinizle birlikte gelir ve harici bir motora ihtiyaç duymaz. Desteklenen profiller ve RAD Studio sürümleri HotPDF Delphi bileşen sayfasında listelenir