Bilgi sözlüğü /Title (Annual Maintenance Report) derken XMP paketinde hiç dc:title olmayan bir dosya alın. Görüntüleyiciler başlığı şikâyetsiz gösterir; ama bir PDF/A doğrulayıcısı dosyayı reddeder, çünkü iki metadata kopyası uyuşmaz. İkinci bir dosya aynı kadar sağlıklı görünür ama XMP'inde pdfaid:part girdisi yoktur ve doğrulayıcı onlarca hatayla yanıtlar: dosyayı sıradan bir PDF olarak yargılar ve iddia edilen bölümün her kuralını başarısız olarak raporlar; hepsi tek bir eksik satırdan
İki sorun, çok farklı miktarda iş ister. Metadata'yı uzlaştırmak, herhangi bir dosyada koşturabileceğiniz dar kapsamlı bir düzeltmedir. Rastgele bir PDF'i arşivlik bir PDF'e çevirmek ise hedef bölümün yasakladığını atmak, gerektirdiğini eklemek, iddiayı bildirmek ve sonra gerçekten çıkan baytları doğrulamak demektir. Dönüşümü bilinçli olarak o son adımın çevresinde kurduk: ConvertToPDFA, Enterprise sürümünün (TPDFlibEnterprise) parçasıdır ve GetPDFAConversionReport ile birlikte çalışır; dönüştürülen belgeyi belleğe kaydeder, o baytlar üzerinde PDF/A kontrolünü koşturur ve yalnızca kontrol geçince 1 döndürür
Sorunsuz açılan bir PDF neden PDF/A kontrolünde başarısız olur?
İki başarısızlık da metadata kurallarından gelir. Bir PDF, başlığını, yazarını, üreticisini ve tarihlerini belge bilgi sözlüğüne yazar ve PDF/A aynı gerçeklerin bir XMP metadata paketinde de olmasını ister; ISO 19005-1 §6.7.3 ikisinin eşdeğer olmasını sorar ve PDF/A-2 ile PDF/A-3 kuralı korur. Bu kuralda kalın dosyalarda alışılmış desen, bilgi sözlüğünü doldurup XMP yarısını hiç yazmamış bir üreticidir; doğrulayıcı bir yerde başlık bulur, ötekinde hiçbir şey
Eksik tanımlama daha sessizdir. pdfaid:part ile pdfaid:conformance girdileri, bir dosyayı PDF/A olarak işaretleyen şeydir ve bir dosya onsuz kusursuz biçimde iyi olabilir. En sık gördüğümüz durum, kendi XMP paketini tedarik eden, PDF yazıcının ürettiğinin yerine koyan bir uygulamadır; özel paket, uygulamanın kendi alanlarını taşır ve hiç pdfaid girdisi yoktur; dolayısıyla hata listesi gerçek nedene hiç yaklaşmayan bir yeri gösterir
RepairDocumentMetadata iki durumu da ele alır ve değiştirdiği girdi sayısını döndürür. Her sürümde mevcuttur; SetPDFAMode ile SanitizeDocument gibi. Onarım, Title, Author, Subject, Keywords, Creator, Producer, CreationDate ve ModDate'i gezer. Yalnızca XMP bir değer taşıyorsa bilgi sözlüğü ondan doldurulur; böylece hâlihazırda kayıtlı olan hiçbir şey çöpe gitmez. İkisi de değer taşıyorsa ve farklıysa, bilgi sözlüğü kazanır. Bu kararı verdik, çünkü bilgi sözlüğü üreticilerin en sık ayarladığı yarıdır; bu da onu, yazarın neyi kastettiğinin daha olası kaydı yapar
XMP'niz yetkili kopyaysa (bir belge yönetim sisteminde özenle düzenlenmişse diyelim), istediğiniz değerleri SetInformation ile bilgi sözlüğüne itin ve onarımı ancak ondan sonra koşturun. Ters yönde yapmak, özenle düzenlediğiniz XMP başlığınızı özgün üreticinin yazdığıyla üzerine yazar
Tanımlama onarımının sizden istediği tek şey var: belge, hangi bölümü hedeflediğini bilmelidir. Yeni yüklenmiş bir dosyada PDF/A kipi ayarlı değildir; dolayısıyla onarımdan önce SetPDFAMode çağırın. Kip yerindeyken RepairDocumentMetadata, eşleşen pdfaid girdilerini yazar, bölüm 4 kipleri için pdfaid:rev'i 2020 yılıyla ekler ve dc:format'ı doğrulayıcıların da kontrol ettiği application/pdf'e ayarlar
uses
SysUtils, PDFlibrary;
procedure RepairIncoming(const InName, OutName: string);
var
Lib: TPDFlib;
Fixed: Integer;
begin
Lib := TPDFlib.Create;
try
if Lib.LoadFromFile(InName, '') <> 1 then
raise Exception.CreateFmt('Cannot open %s', [InName]);
// Özenle düzenlenen başlık önce bilgi sözlüğüne gider,
// çünkü iki yarı uyuşmadığında kazanan o kopyadır
Lib.SetInformation(2, 'Annual Maintenance Report 2025');
Lib.SetPDFAMode(3); // 3 = PDF/A-2b, aşağıdaki tabloya bakın
Fixed := Lib.RepairDocumentMetadata;
Writeln(Format('%d metadata entries brought into agreement', [Fixed]));
Lib.SaveToFile(OutName);
finally
Lib.Free;
end;
end;
Hedef bölümü ve kip numarasını seçmek
Bölümü, belgenin ne taşıması gerektiğinden seçin ve numaraya ancak sonra bakın. SetPDFAMode ile ConvertToPDFA tek bir numaralandırma şeması paylaşır ve o şema, bölüm adlarının çağrıştırdığı sırada koşmaz
| Kip | Bölüm ve düzey | Notlar |
|---|---|---|
| 1 | PDF/A-1a | PDF 1.4, etiketli |
| 2 | PDF/A-1b | PDF 1.4 |
| 3 | PDF/A-2b | PDF 1.7 |
| 4 | PDF/A-2a | PDF 1.7, etiketli |
| 5, 6, 7 | PDF/A-3b, 3a, 3u | Her türden gömülü dosyaya izin verilir |
| 8 | PDF/A-2u | PDF 1.7, Unicode metni zorunlu |
| 9, 10, 11 | PDF/A-4, 4e, 4f | PDF 2.0; 4e 3B ile zengin medya ekler, 4f gömülü dosyalara izin verir |
Kip 3 PDF/A-2b, kip 4 PDF/A-2a'dır. O terslik, 1a'dan yukarı sayan ve a düzeyinin daima önde olduğunu varsayanları yakalar. PDF/A-2u, 3. bölüm kiplerinden sonra, 8'de belirir
Gerçek sonuçları olan seçim ek dosyalar hakkındadır. Bölüm 1 ile 2 gömülü dosyaları yasaklar; dolayısıyla ConvertToPDFA, 1'den 4'e kipler ve 8. kip için onları kaldırır. Bir Factur-X ya da ZUGFeRD faturasını 3. kipe çevirin ve yapılandırılmış XML fatura çıktıdan gider; dosyanın var olma nedeniyle birlikte. Ek dosya taşıyan her şey için 5. kipi (PDF/A-3b) hedefleyin — o fatura biçimleri de zaten o bölüm üzerinde tanımlıdır — ya da PDF 2.0 gerekiyorsa 11. kipi. 3. ya da 4. bölüm dosyasındaki her ek dosya, /AFRelationship aracılığıyla belgeyle nasıl ilişkili olduğunu bildirmek zorundadır ve PDF Library for Delphi, o kiplerde kaydettiği her gömülü dosya için o girdiyi yazar
4. bölüm kendini başka türlü tanıtır. Düz PDF/A-4 için uyumluluk harfi yoktur; iki uzantı için E ya da F kullanır ve bölümünün yayınlandığı yılı taşıyan bir pdfaid:rev ister. ConvertToPDFA, 9'dan 11'e kipler için dosyayı PDF 2.0'a yükseltir; çünkü ISO 19005-4 §6.1.2 bölümü PDF 2.0 üzerine kurar ve kontrol, böyle dosyaları 1.7 yerine PDF 2.0'a göre yargılar
ConvertToPDFA dosyada neyi değiştirir
ConvertToPDFA, sabit bir onarım dizisi uygular, her birini düz metin bir raporda not eder, sonucu belleğe kaydeder ve o baytları doğrular. Başarısız kontrol 0 döndürür; 1'den 11'e dışında bir kip de öyledir ve o, hiç rapor yazılmadan döner. Dizi bilmeye değerdir, çünkü her adım, çıktının kaybedeceği ya da kazanacağı bir şeydir:
- Parolası bilinmeyen şifreli bir belge, dönüşümü hemen bir
blocked:satırıyla durdurur; içindeki hiçbir şey okunamaz, onarılması hiç söz konusu değildir - Belge düzeyi JavaScript ve onu koşturan açık eylemi, her ek eylem girdisiyle ve hiçbir bölümün izin vermediği eylem türleriyle birlikte kaldırılır; aralarında Launch, Sound, Movie, ResetForm, ImportData, Hide, SetOCGState, Rendition ve Trans vardır. GoTo ile URI bağlantıları kalır; dolayısıyla gezinme çalışmaya devam eder
- Gömülü dosyalar, yasaklayan bölümler için kaldırılır; yukarıda anlatıldığı gibi
- Gizli ya da hiç gösterilmeyecek ayarlı ek açıklamalar silinir. PDF/A, ek açıklamaların görünür ve yazdırılabilir olmasını ister ve onları açığa çıkarmak sayfanın görünümünü değiştirirdi; kaldırmayı seçtik
- Form alanı görünümleri,
/NeedAppearancesbayrağı temizlenmeden önce yeniden üretilir; dolayısıyla doldurulmuş değerler görünür kalır - Kullanılan ama gömülmemiş fontlar, makinede kurulu fontlardan gömülür
- Artık hiçbir şeyin işaret etmediği nesneler düşürülür ve dosyada yoksa bir sRGB çıktı niyeti eklenir
- Son olarak bölüm ile düzey XMP'de bildirilir ve baytlar kontrol edilir
Bir sunucuda dönüşümü en çok tıkayan adım fonttur. Dönüşüm makinesinde hiçbir yerde bulunmayan bir fontu kullanan belge, dosyanın içinden onarılamaz ve rapor bunu söyleyecektir. Belgelerinizin gerçekten kullandığı fontları dönüşüm makinesine kurun; mevcut bir PDF'e eksik fontları gömme kılavuzu o adımı tek başına ele alır
Toplu iş, başarısız bir dönüşümü nasıl ele almalı?
Dönüş değerini bir geçit, raporu ise iş fişi sayın. 1 döndüren dosyaları arşive koyun; gerisini raporu da ekleyerek bir insana yönlendirin. Başarısız bir dönüşümün çıktısını, girdiden daha temiz göründüğü için arşive kaydetmeyin. O dosya artık kontrolün onaylamayı reddettiği bir PDF/A iddiası taşır; doğrulama adımının var olma amacı tam da bunu önlemektir
GetPDFAConversionReport'un raporu, her biri bir fiille başlayan satır başına tek eylemdir: removed:, regenerated:, repaired:, embedded:, added:, stated:, bir şey turu durdurduysa blocked: ve kapanışta bir result: satırı. Kararın arkasındaki kontrol ilk başarısızlıkta durur; dosya kalınca dönüşüm kontrolü tam olarak yeniden koşturur ve rapor bütün sorunları tek seferde gösterir. Ön ekler üzerinden yönlendirmek birkaç satır alır
uses
Classes, SysUtils, PDFlibrary, PDFlibEnterprise;
function ArchiveOne(const InName, ArchiveDir, ReviewDir: string): Boolean;
var
Lib: TPDFlibEnterprise;
Report: TStringList;
I: Integer;
Blocked: Boolean;
begin
Result := False;
Lib := TPDFlibEnterprise.Create;
Report := TStringList.Create;
try
if Lib.LoadFromFile(InName, '') <> 1 then
Exit;
Result := Lib.ConvertToPDFA(5) = 1; // 5 = PDF/A-3b, ek dosyaları korur
Report.Text := Lib.GetPDFAConversionReport;
if Result then
Lib.SaveToFile(IncludeTrailingPathDelimiter(ArchiveDir) + ExtractFileName(InName))
else
begin
Blocked := False;
for I := 0 to Report.Count - 1 do
if Pos('blocked:', Report[I]) = 1 then
Blocked := True;
// Özgün el değmeden kalır; yolculuğa yalnızca rapor çıkar
Report.SaveToFile(IncludeTrailingPathDelimiter(ReviewDir) +
ChangeFileExt(ExtractFileName(InName), '.pdfa-report.txt'));
if Blocked then
Writeln(InName, ': needs a password or a rebuilt source file');
end;
finally
Report.Free;
Lib.Free;
end;
end;
Bir blocked: satırı çoğunlukla çözümün PDF'in dışında olduğunu gösterir: bir parola, başka bir kaynak dosya ya da belgeyi üreten şeyin yeniden koşulması. Tam bir dönüşümden sonraki sıradan doğrulama sorunları ise çoğunlukla anlam taşıyan ve silinmeye elverişli olmayan içeriktir; bir insanın karar vermesi gereken durumlar da bunlardır
Bir dosyanın hâlihazırda taşıdığı PDF/A damgasını okumak
Dönüştürmeden önce belgenin kendi hakkında hâlihazırda ne iddia ettiğini kontrol edin. Tarayıcılardan, ofis paketlerinden ve öteki PDF araçlarından gelen dosyalar çoğunlukla hâlihazırda bir PDF/A damgası taşır; geçerli bir PDF/A-3 dosyasını 2. bölüme yeniden dönüştürmek, ek dosyalarını boş yere kaybettirir
Tuzak, XMP'nin aynı özelliği iki şekilde yazmaya izin vermesidir. Bazı üreticiler bölümü alt öğe olarak yazar: <pdfaid:part>2</pdfaid:part>; ötekiler rdf:Description üzerinde öznitelik olarak: pdfaid:part="2". İkisi de sıradan, geçerli XMP'dir. Yalnızca bir biçimi arayan bir algılayıcı, öteki üretici ailesinden gelen dosyaların damgasız olduğuna karar verir ve böyle kurulmuş bir denetleyici, geçerli bir PDF/A-2 dosyasını 1. bölüme göre yargılayıp damgasız ve PDF sürümü fazla yüksek olarak raporlar; gerçeğin tam tersi. PDF Library for Delphi'nin doğrulayıcısı iki biçimi de okur. Damgayı kendiniz koklarsanız, siz de aynısını yapın:
function DeclaredPDFAPart(Lib: TPDFlib): string;
const
ElemTag = '<pdfaid:part>';
AttrDQ = 'pdfaid:part="';
AttrSQ = 'pdfaid:part=''';
var
Packet: string;
P: Integer;
begin
Result := '';
Packet := Lib.GetDocumentMetadata;
P := Pos(ElemTag, Packet); // öğe biçimi
if P > 0 then
Exit(Copy(Packet, P + Length(ElemTag), 1));
P := Pos(AttrDQ, Packet); // öznitelik biçimi, çift tırnak
if P = 0 then
P := Pos(AttrSQ, Packet); // öznitelik biçimi, tek tırnak
if P > 0 then
Result := Copy(Packet, P + Length(AttrDQ), 1);
end;
Boş bir sonuç, iki biçimde de damga olmadığını gösterir. Bir rakam, üreticinin hangi bölümü kastettiğini söyler ve dönüşüme hiç girip girmeyeceğinize karar verirken hedefinizle karşılaştıracağınız sayı odur
Dosya arşive girmeden önce betikleri temizlemek
Arşiv dönüşümü ile güvenlik temizliği genellikle birlikte koşar; çünkü bir güvenlik politikasının gitmesini istediği içerik, PDF/A'nın yasakladığıyla ağır biçimde örtüşür. SanitizeDocument, bir SANITIZE_* bayrak maskesi alır ve kaç kaldırma uyguladığını döndürür. ConvertToPDFA kendisi ilk adım olarak onu SANITIZE_JAVASCRIPT or SANITIZE_OPEN_ACTION ile çağırır
Doğrulayıcılar açısından bir ayrıntı önem taşır. Belge düzeyi betikler, bir /JavaScript ad ağacında, kataloğun /Names sözlüğü altında yaşar ve son betik kaldırıldığında artık boş olan ağaç da onunla birlikte kaldırılır. Boş bir ağaç hâlâ belgenin betikleri olduğunu ilan eder ve bazı denetçiler altında ne oturduğunu saymadan anahtarın kendisine tepki verir
Temizliği hangi araç yaptıysa yapsın, kaydedilen dosyayı bellek içi duruma güvenmek yerine taze bir yüklemeyle doğrulayın. Yeniden açmak ucuzdur:
var
Lib, Check: TPDFlib;
begin
Lib := TPDFlib.Create;
Check := TPDFlib.Create;
try
Lib.LoadFromFile('upload.pdf', '');
Lib.SanitizeDocument(SANITIZE_JAVASCRIPT or SANITIZE_OPEN_ACTION);
Lib.SaveToFile('upload-clean.pdf');
Check.LoadFromFile('upload-clean.pdf', '');
if Check.GlobalJavaScriptCount > 0 then
raise Exception.Create('Document-level scripts survived the save');
finally
Check.Free;
Lib.Free;
end;
end;
Arşiv hattının komşu adımları için PDF/A ve PDF/UA preflight, gerçek karartma ve içerik kaldırma ve Factur-X için PDF/A-3 XMP uzantı şemaları kılavuzlarına bakın; arşivlenmiş bir belge yapılandırılmış fatura verisi de taşıdığında metadata tarafını son sözü o alır
Burada gösterilen dönüşüm, metadata onarımı ve doğrulamanın tamamı, zincirde harici bir dönüştürücü ya da komut satırı doğrulayıcı olmadan, kendi Delphi, C++Builder ya da Lazarus sürecinizin içinde koşar. PDF Library for Delphi ürün sayfası PDF/A bölümlerini, sürümleri ve hedef platformları listeler