Articol tehnic

Validarea PDF/X în Delphi cu componenta PDFium

Componenta PDFium pentru Delphi validează documente PDF/X gata pentru tipar prin intermediul TPdf.ValidatePdfX, care implementează verificarea standardului ISO 15930 pe două niveluri: opt verificări de conținut la nivel de octeți (interzicerea compresiei LZW, JavaScript, câmpuri de formular, referințe OPI, absența TrimBox, lipsa setării cheii Trapped și altele) plus o analiză bazată pe modelul de obiecte al PDFium care folosește FPDFFont_GetIsEmbedded pentru a verifica încorporarea fonturilor pentru fiecare obiect de text de pe fiecare pagină. Rezultatul este o înregistrare de tip TPdfXValidationResult care indică nivelul de conformitate detectat și listează fiecare abatere ca enum tipizat, astfel încât aplicația Delphi să poată comunica utilizatorului motivul pentru care un fișier va fi respins la tipografie înainte de începerea producției

Dacă ați trimis vreodată o lucrare la o tipografie comercială și ați primit-o înapoi cu un refuz scurt — „fără TrimBox”, „fonturile nu sunt încorporate”, „Trapped nesetat” — cunoașteți deja costurile depistării târzii a acestor probleme. PDF/X este echivalentul din faza de prepress al standardului PDF/A: în timp ce versiunea de arhivare PDF/A garantează că un document se va reda identic peste zeci de ani, standardul PDF/X garantează că documentul se va separa, procesa și tăia identic pe echipamentul RIP al altei tipografii. Cele două standarde partajează imagini comune (identificarea XMP, OutputIntents, profilurile ICC încorporate), dar răspund la întrebări diferite, motiv pentru care componenta oferă validatoare separate pentru fiecare — partea de PDF/A este prezentată în validarea PDF/A preflight cu componenta PDFium

Ce impune în realitate standardul ISO 15930 pentru un PDF gata de tipar?

Standardul ISO 15930 a fost creat pentru a permite un schimb independent (blind exchange): un designer trimite un fișier unei tipografii cu care nu a mai colaborat, iar tipografia poate realiza producția corect fără apeluri telefonice, e-mailuri despre fonturi lipsă și fără imagini legate care au rămas pe laptopul designerului. Fiecare regulă a standardului deservește acest scop. Fonturile trebuie încorporate deoarece nu se poate presupune că RIP-ul receptor le deține. Referințele externe sunt interzise deoarece fișierul trebuie să fie complet în sine. Elementele interactive sunt interzise deoarece cerneala nu are instrucțiuni de tratare a evenimentului onclick

Componenta PDFium recunoaște trei niveluri de conformitate și le raportează prin enum-ul TPdfXConformance în rezultatul validării: pxc1a pentru PDF/X-1a:2001 (ISO 15930-1, cerința strictă CMYK plus culori spot pe bază de PDF 1.3/1.4), pxc3 pentru PDF/X-3:2002 (ISO 15930-3, care admite culori gestionate prin RGB, Lab și profiluri ICC) și pxc4 pentru PDF/X-4:2010 (ISO 15930-7, care permite transparență activă și straturi pe o bază PDF 1.6). Un fișier care nu conține nicio identificare PDF/X va returna valoarea pxcNone, un răspuns util: documentul nu a fost definit ca fiind pregătit pentru tipar, iar elementele raportate de validator explică pașii necesari pentru a atinge acest obiectiv

Interdicțiile sunt logice atunci când priviți lucrurile din perspectiva unui furnizor de RIP. Filtrul /LZWDecode este interzis în toate variantele PDF/X pentru ca procesarea să nu depindă de un filtru cu istoric de compatibilitate și licențiere complex; Flate îndeplinește același rol fără aceste limitări. Limbajul JavaScript, câmpurile AcroForm și dicționarele de acțiuni suplimentare /AA sunt interzise deoarece un fișier de tipar trebuie să fie o descriere fixă a semnelor de pe hârtie — orice element care poate modifica aspectul la deschidere încalcă garanția că rezultatul tipărit va fi identic cu cel aprobat. Marcajele OPI (Open Prepress Interface) sunt interzise deoarece reprezintă referințe la imagini de înaltă rezoluție stocate în altă parte, iar acea localizare externă este exact lucrul pe care schimbul independent îl interzice

De ce resping tipografiile fișierele PDF fără TrimBox?

Parametrul TrimBox reprezintă pagina finită — dreptunghiul care rămâne după procesul de tăiere. Caseta MediaBox, pe care o are fiecare pagină PDF, reprezintă doar coala: aceasta conține marginile de tăiere (bleed), marcajele de tăiere, țintele de aliniere și benzile de culoare. Programele de impoziție poziționează paginile pe coala de tipar în funcție de TrimBox; în absența acesteia, operatorul trebuie să aproximeze unde se termină documentul, iar o estimare eronată poate tăia marginile utile sau poate lăsa o zonă albă pe o latură. Acesta este motivul pentru care standardul ISO 15930 impune o casetă TrimBox (sau ArtBox) pe fiecare pagină și de ce ValidatePdfX semnalează pvxiMissingTrimBox când nu se găsește cheia /TrimBox pe nicio pagină

Cheia /Trapped răspunde unei cerințe diferite de producție. Procesul de trapping este o tehnică de prepress ce constă în suprapunerea ușoară a culorilor adiacente pentru ca eventualele abateri de aliniere ale presei să nu lase spații albe între ele. Tipografia trebuie să știe dacă acest proces a fost deja realizat: aplicarea trapping-ului pe un fișier care îl are deja dublează suprapunerile, iar omiterea lui pe un fișier care nu îl are implică riscul unor goluri vizibile. De aceea, standardul PDF/X impune ca dicționarul Info să declare explicit /Trapped /True sau /Trapped /False — absența cheii sau valoarea /Unknown impun o verificare manuală, exact tipul de intervenție pe care schimbul independent încearcă să îl evite. Componenta marchează această situație ca fiind pvxiTrappedNotSet

Rularea validării pe două niveluri cu TPdf.ValidatePdfX

Metoda TPdf.ValidatePdfX nu preia argumente și returnează o înregistrare TPdfXValidationResult cu trei membri: Conformance (tipul de PDF/X detectat), Issues (un set Pascal de valori TPdfXValidationIssue) și o funcție asociată IsCompliant. Intern, aceasta serializează documentul într-un flux de memorie, rulează inspectorul la nivel de octeți peste el, apoi parcurge modelul de obiecte PDFium pentru verificarea încorporării fonturilor. O procedură minimă de validare se prezintă astfel:

uses PDFium, FPdfPdfx;

procedure CheckPrintReadiness(const FileName: string);
var
  Pdf: TPdf;
  Res: TPdfXValidationResult;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.FileName := FileName;
    Pdf.Active := True;

    Res := Pdf.ValidatePdfX;
    Writeln('Detected conformance: ',
      Ord(Res.Conformance)); // pxc1a, pxc3, pxc4, pxcNone...

    if Res.IsCompliant then
      Writeln('PDF/X checks passed')
    else
    begin
      if pvxiMissingTrimBox in Res.Issues then
        Writeln('REJECT: no /TrimBox on the pages');
      if pvxiTrappedNotSet in Res.Issues then
        Writeln('REJECT: /Trapped missing or /Unknown');
      if pvxiPdfiumFontNotEmbedded in Res.Issues then
        Writeln('REJECT: a page uses a non-embedded font');
      if pvxiLzwForbidden in Res.Issues then
        Writeln('REJECT: LZWDecode filter present');
    end;
  finally
    Pdf.Free;
  end;
end;

Deoarece Issues is an ordinary Pascal set, you can partition it however your workflow needs — treat structural problems as hard rejects, treat pvxiMissingTitle (a SHOULD in the standard, not a MUST) as a warning, and log the rest. The same record type also feeds the component's report generator, so if you would rather emit a human-readable document than branch on enums, the pattern in building a batch preflight report CLI with the PDFium Component applies to PDF/X unchanged

Ce detectează nivelul bazat pe octeți — și ce omite

Nivelul bazat pe octeți reprezintă o scanare a token-urilor din structura documentului, cu omiterea conținutului fluxurilor, astfel încât o imagine JPEG care conține întâmplător secvența /JavaScript nu poate declanșa un rezultat fals pozitiv. Pe lângă verificările marcajelor (XMP pdfxid:GTS_PDFXVersion, OutputIntent cu profil ICC încorporat, trailerul /ID, interzicerea criptării), analiza conținutului adaugă opt verificări, fiecare cu propria sa valoare enum:

  • pvxiLzwForbidden — prezența unui filtru /LZWDecode oriunde în fișier (interzis în toate variantele PDF/X)
  • pvxiJavaScriptForbidden — prezența unei acțiuni sau a unei structuri de tip /JavaScript
  • pvxiFormFieldsForbidden — existența unui dicționar /AcroForm sau a unei intrări /XFA
  • pvxiAdditionalActions — prezența unui dicționar de acțiuni suplimentare /AA
  • pvxiEmbeddedFilesForbidden — prezența intrării /EmbeddedFiles sau a unei adnotări de tip /FileAttachment
  • pvxiOpiForbidden — o intrare /OPI sau /Alternates indică conținut de imagine care poate fi înlocuit
  • pvxiMissingTrimBox — nu s-a găsit nicio casetă /TrimBox pe nicio pagină
  • pvxiTrappedNotSet — intrarea /Trapped lipsește sau are valoarea /Unknown

Scanarea la nivel de octeți este rapidă și nu necesită un motor de randare, dar are o limită implicită legată de fonturi: la acest nivel, inspectorul poate folosi doar o euristică simplă — marchează un document doar când nu găsește niciun program de font încorporat. Un fișier cu nouă fonturi încorporate și un font de sistem adăugat va părea corect la scanarea octeților. Această limitare reprezintă motivul care explică importanța celui de-al doilea nivel de verificare

Încorporarea fonturilor prin intermediul modelului de obiecte PDFium

Nivelul bazat pe modelul de obiecte al componentei PDFium oferă un răspuns exact privind fonturile. După analiza la nivel de octeți, TPdf.ValidatePdfX parcurge fiecare pagină, interoghează FPDFPage_CountObjects pentru a obține lista de obiecte, iar pentru fiecare obiect de text identifică fontul prin FPDFTextObj_GetFont și apelează FPDFFont_GetIsEmbedded. Un singur font neîncorporat în document adaugă valoarea pvxiPdfiumFontNotEmbedded în setul de probleme. Parcurgerea se oprește la două niveluri — se oprește scanarea obiectelor pe pagină și se oprește încărcarea paginilor următoare din momentul în care problema a fost confirmată — astfel încât, pentru un catalog cu 300 de pagini care prezintă abateri, rezultatul este furnizat frecvent după prima pagină

Două aspecte practice utile. În primul rând, acest nivel necesită biblioteca PDFium încărcată și build-uri care exportă funcția FPDFFont_GetIsEmbedded; când funcția exportată lipsește, verificarea este omisă, nu marcată ca eșuată, astfel încât o versiune DLL mai veche nu va genera respingeri false. În al doilea rând, verificarea indică doar dacă fontul este încorporat sau nu — nu face distincția între încorporarea completă și subsetting și nu analizează acoperirea glifelor. Când un fișier eșoează și doriți să aflați care font de pe care pagină are probleme, tehnicile de enumerare din analizarea proprietăților fonturilor PDF cu PDFium în Delphi vă oferă detaliile necesare

Validarea fluxurilor de date fără încărcarea documentului sau a DLL-ului

Inspectorul la nivel de octeți este de asemenea expus ca o funcție de sine stătătoare, ValidatePdfXCompliance(Source: TStream) în unitatea FPdfPdfx, și este scris în Object Pascal pur, fără dependențe de DLL-ul PDFium. Acest lucru îl face utilizabil în medii în care un motor de randare nu este recomandat: o procedură simplă de încărcare pe un server web, un job CI care verifică fișierele create sau un serviciu Lazarus pe o platformă pe care nu doriți să livrați fișiere binare native. Îi puteți furniza orice flux de date care acceptă operațiuni de poziționare (seekable stream):

uses Classes, FPdfPdfx;

function QuickPdfXGate(const FileName: string): Boolean;
var
  Fs: TFileStream;
  Res: TPdfXValidationResult;
begin
  Fs := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);
  try
    Res := ValidatePdfXCompliance(Fs);
    Result := Res.IsCompliant and (Res.Conformance <> pxcNone);
  finally
    Fs.Free;
  end;
end;

Compromisul este clar: calea independentă rulează verificările structurale și toate cele opt verificări de conținut, dar nu și nivelul bazat pe PDFium pentru fonturi, astfel încât evaluarea fonturilor se bazează doar pe euristica simplă. O arhitectură logică utilizează ValidatePdfXCompliance ca o metodă rapidă de control și rezervă utilizarea TPdf.ValidatePdfX doar pentru fișierele care trec de această primă etapă

Unde se încheie acest validator și unde începe un proces de preflight complet

Este util să fim exacți în ceea ce privește instrumentele de preflight, iată deci limitele acestei funcții. ValidatePdfX verifică marcajele de identificare, interdicțiile structurale, casetele de geometrie a paginii, declarația Trapped și încorporarea fonturilor la nivel de obiect de text. Nu măsoară acoperirea totală cu cerneală, nu validează dacă fiecare spațiu de culoare este permis pentru varianta selectată (de exemplu, regula strictă CMYK pentru X-1a), nu compară rezoluția imaginilor și nu evaluează comportamentul transparențelor — acestea necesită un motor de preflight complet cu management de culoare, iar documentația unității recomandă utilizarea unui astfel de instrument pentru certificarea finală. Ceea ce vă oferă verificarea pe două niveluri este depistarea a 80% din problemele structurale timpurii în câteva milisecunde direct în codul Delphi, evitând respingerea ulterioară a fișierului de către tipografie

Ambele niveluri de validare, interfețele API de adăugare a marcajelor PDF/X pentru generarea de fișiere conforme, precum și validatoarele PDF/A, PDF/UA, PDF/E și PDF/VT care partajează aceeași arhitectură sunt incluse în componenta standard PDFium Component pentru Delphi și C++Builder — o singură componentă, de la randare la controlul prepress