Műszaki cikk

PDF/X ellenőrzés Delphi-ben a PDFium Component segítségével

A Delphi-hez készült PDFium Component a TPdf.ValidatePdfX metóduson keresztül ellenőrzi a nyomtatásra kész PDF/X dokumentumokat, amely az ISO 15930 ellenőrzést két rétegben valósítja meg: nyolc bájtszintű tartalomellenőrzés (tiltott LZW tömörítés, JavaScript, űrlapmezők, OPI hivatkozások, hiányzó TrimBox, beállítatlan Trapped kulcs és egyebek) plusz egy PDFium objektummodell-lépés, amely a FPDFFont_GetIsEmbedded függvényt használja a betűtípus-beágyazás ellenőrzésére minden oldal minden szöveges objektumán; az eredmény egy TPdfXValidationResult rekord, amely megnevezi az észlelt megfelelőségi szintet, és listázza az egyes szabálysértéseket típusos enumként, így a Delphi alkalmazása pontosan meg tudja mondani az ügyfélnek, hogy miért fog visszautasításra kerülni a fájl a nyomdában, még mielőtt bárki lemezt készítene

Ha valaha is küldött már munkát egy kereskedelmi nyomdába, és egy egysoros visszautasítással kapta vissza — „no TrimBox”, „fonts not embedded”, „Trapped not set” —, akkor ismeri a késői észlelés költségét; a PDF/X a nyomdai előkészítés megfelelője a PDF/A-nak: míg az archiválásra szánt PDF/A garantálja, hogy a dokumentum évtizedek múlva is azonosan jelenik meg, a PDF/X garantálja, hogy a dokumentum holnap reggel valaki más RIP-jén azonosan válik szét, rajzolódik ki és vágódik le; a két szabvány megosztja az alapokat (XMP azonosítás, OutputIntents, beágyazott ICC profilok), de különböző kérdésekre válaszolnak, ezért a komponens mindegyikhez külön validátort biztosít — a PDF/A oldallal a PDF/A előzetes ellenőrzésről szóló cikk foglalkozik

Mit követel meg valójában az ISO 15930 a nyomtatásra kész PDF-től?

Az ISO 15930 azért létezik, hogy lehetővé tegye a vak cserét (blind exchange): a tervező átad egy fájlt egy olyan nyomdásznak, akivel soha nem beszélt, a nyomdász pedig helyes kimenetet tud készíteni telefonhívás, hiányzó betűtípus miatti e-mail és a tervező laptopján maradt csatolt kép nélkül; a szabvány minden szabálya ezt a célt szolgálja; a betűtípusokat be kell ágyazni, mert a fogadó RIP-ről nem tételezhető fel, hogy birtokolja azokat; a külső hivatkozások tiltottak, mert a fájlnak önmagában teljesnek kell lennie; az interaktív funkciók tiltottak, mert a tintának nincs onclick kezelője

A PDFium Component három megfelelőségi családot ismer fel, és ezeket a TPdfXConformance enumon keresztül jelenti az ellenőrzési eredményben: pxc1a a PDF/X-1a:2001-hez (ISO 15930-1, a szigorú CMYK-plusz-spot alapvonal a PDF 1.3/1.4-en), pxc3 a PDF/X-3:2002-höz (ISO 15930-3, amely beengedi az RGB, Lab és ICC-kezelt színeket) és pxc4 a PDF/X-4:2010-hez (ISO 15930-7, amely végül lehetővé teszi az élő átlátszóságot és rétegeket a PDF 1.6-os alapon); az a fájl, amely egyáltalán nem hordoz PDF/X azonosítást, pxcNone értékkel tér vissza, ami önmagában is hasznos válasz: a dokumentum soha nem állította, hogy nyomtatásra kész, és minden más, amit a validátor jelent, elmagyarázza, hogy mi kellene a cél eléréséhez

A tiltásoknak van értelme, ha úgy gondolkodik, mint egy RIP-szolgáltató; az /LZWDecode minden PDF/X variánsban tiltott, így a megfelelő szoftver soha nem függ egy olyan szűrőtől, amelynek kompatibilitási és licencelési története van; a Flate ugyanezt a munkát elvézi a sallangok nélkül; a JavaScript, az AcroForm mezők és az /AA további műveleti szótárak tiltottak, mert a nyomtatási fájlnak a papíron lévő jelölések fix leírásának kell lennie — bármi, ami megnyitáskor megváltoztathatja a megjelenést, megsérti a garanciát, hogy az kerül kinyomtatásra, amit ellenőriztek; az OPI (Open Prepress Interface) helyőrzők tiltottak, mert tervezésüknél fogva valahol máshol tárolt nagy felbontású képekre való hivatkozások, a „valahol máshol” pedig pontosan az, amit a vak csere tilt

Miért utasítják el a nyomdák a TrimBox nélküli PDF-eket?

A TrimBox a kész oldal — a téglalap, amely a vágógép vágása után megmarad; a MediaBox, amellyel minden PDF oldal rendelkezik, csupán az ív: magában foglalja a kifutót (bleed), a vágójeleket, az illesztőjeleket és a színskálákat; a kilövő szoftver a TrimBox-uk alapján pozicionálja az oldalakat a nyomóíven; ennek hiányában a gépkezelőnek ki kell találnia, hol végződik valójában a névjegykártyája, a rossz tippelés pedig levágja a kifutót, vagy fehér csíkot hagy az egyik élen; ezért követeli meg az ISO 15930 a TrimBox (vagy ArtBox) meglétét minden oldalon, és ezért ad a ValidatePdfX pvxiMissingTrimBox hibát, ha a dokumentum egyetlen oldalán sem található meg a /TrimBox kulcs

A /Trapped kulcs egy másik gyártási kérdésre válaszol; a trapping (túltöltés) az a nyomdaelőkészítési technika, amely a szomszédos színeket kissé átfedi egymással, hogy a nyomógép apró illeszkedési pontatlanságai ne hagyjanak fehér réseket közöttük; a nyomdásznak tudnia kell, hogy ezt a munkát elvézték-e már: a már trappingelt fájl újbóli trappingelése megduplázza az átfedéseket, a trapping kihagyása egy kezeletlen fájlnál pedig látható réseket kockáztat; a PDF/X ezért megköveteli, hogy az Info szótár kifejezetten tartalmazza a /Trapped /True vagy /Trapped /False értékek egyikét — a hiányzó kulcs vagy az /Unknown emberi ellenőrzést tesz szükségessé, ami pontosan az a beszélgetés, amelyet a vak csere el akart kerülni; a komponens ezt pvxiTrappedNotSet hibaként jelöli

A kétlépesős ellenőrzés futtatása a TPdf.ValidatePdfX segítségével

A TPdf.ValidatePdfX nem fogad el paramétereket, és egy TPdfXValidationResult rekorddal tér vissza, amelynek három tagja van: Conformance (az észlelt PDF/X típus), Issues (a TPdfXValidationIssue értékek Pascal halmaza) és az IsCompliant segédlet; belsőleg a betöltött dokumentumot memóriafolyamba sorosítja, lefuttatja rajta a bájtszintű ellenőrzést, majd végigjárja a PDFium objektummodellt a betűtípus-beágyazás ellenőrzéséhez; egy minimális előzetes ellenőrzési folyamat így néz ki:

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('ELUTASÍTVA: nincs /TrimBox az oldalakon');
      if pvxiTrappedNotSet in Res.Issues then
        Writeln('ELUTASÍTVA: a /Trapped hiányzik vagy /Unknown');
      if pvxiPdfiumFontNotEmbedded in Res.Issues then
        Writeln('ELUTASÍTVA: az egyik oldal nem beágyazott betűtípust használ');
      if pvxiLzwForbidden in Res.Issues then
        Writeln('ELUTASÍTVA: LZWDecode szűrő van jelen');
    end;
  finally
    Pdf.Free;
  end;
end;

Mivel az Issues egy normál Pascal halmaz, úgy oszthatja fel, ahogy a munkafolyamata megkívánja — kezelheti a strukturális problémákat kemény elutasításként, a pvxiMissingTitle hibát (ami a szabványban SHOULD és nem MUST) figyelmeztetésként, a többit pedig naplózhatja; ugyanez a rekordtípus táplálja a komponens jelentésgenerátorát is, így ha inkább ember által olvasható dokumentumot szeretne kiadni az enumok alapján történő elágazás helyett, a kötegelt előzetes ellenőrzési jelentés készítéséről szóló cikkben bemutatott minta változatlanul érvényes a PDF/X-re is

Amit a bájtszintű réteg elkap — és amit kihagy

A bájtszintű réteg egy token-alapú vizsgálat a dokumentum strukturális bájtai felett, ahol az adatfolyamok törzse üres, így az a JPEG, amely történetesen tartalmazza a /JavaScript bájtmintát, nem válthat ki téves riasztást; a jelölők ellenőrzésén felül (XMP pdfxid:GTS_PDFXVersion, OutputIntent beágyazott ICC profillal, trailer /ID, a titkosítás tilalma), a tartalomellenőrzés nyolc vizsgálatot tesz hozzá, mindegyiket saját enum értékkel:

  • pvxiLzwForbidden — a /LZWDecode szűrő megjelenik valahol a fájlban (tiltott minden PDF/X variánsban)
  • pvxiJavaScriptForbidden — a /JavaScript művelet vagy névfa van jelen
  • pvxiFormFieldsForbidden — egy /AcroForm szótár vagy /XFA bejegyzés létezik
  • pvxiAdditionalActions — egy /AA további műveleti szótár van jelen
  • pvxiEmbeddedFilesForbidden — a /EmbeddedFiles vagy a /FileAttachment megjegyzés van jelen
  • pvxiOpiForbidden — egy /OPI vagy /Alternates bejegyzés cserélhető képtartalomra hivatkozik
  • pvxiMissingTrimBox — nem található /TrimBox egyik oldalon sem
  • pvxiTrappedNotSet — a /Trapped hiányzik vagy /Unknown értékre van állítva

A bájt alapú vizsgálat gyors és nem igényel renderelő motort, de van egy veleszületett vakfoltja a betűtípusokkal kapcsolatban: ezen a szinten a vizsgáló csak egy durva heurisztikát tud alkalmazni — megjelöli a dokumentumot, ha egyáltalán nem talál beágyazott betűtípus-programot; az a fájl, amelyben kilenc betűtípus be van ágyazva, egy rendszerszintű pedig becsúszott, jónak tűnik a bájt alapú vizsgálat számára; éppen ezen egyetlen rés miatt létezik a második réteg

Karakterenkénti beágyazás a PDFium objektummodellen keresztül

A PDFium Component objektummodell-rétege pontosan megválaszolja a betűtípus kérdését; a bájtszintű lépés után a TPdf.ValidatePdfX végigjárja az összes oldalt, lekéri az objektumlistát a FPDFPage_CountObjects segítségével, és minden szöveges objektumnál feloldja a betűtípus-kezelőt a FPDFTextObj_GetFont-on keresztül, majd lekérdezi az FPDFFont_GetIsEmbedded-et; egyetlen nem beágyazott betűtípus a dokumentumban hozzáadja a pvxiPdfiumFontNotEmbedded hibát a problémakészlethez; a bejárás két szinten zárható rövidre — leállítja az objektumok vizsgálatát a lapon, és leállítja a további lapok betöltését, amint a hiba megerősítést nyer —, így egy szabálysértő 300 oldalas katalógus esetén az eredmény gyakran már az első oldal után megérkezik

Két megjegyzést érdemes tudni a határokkal kapcsolatban; először is, ez a réteg igényli a PDFium könyvtár betöltését, és olyan verziókat követel meg, amelyek exportálják az FPDFFont_GetIsEmbedded-et; ha az exportálás hiányzik, az ellenőrzés inkább kimarad, semmint meghiúsul, így egy régebbi DLL soha nem produkál fantom elutasításokat; másodszor, az ellenőrzés a „beágyazott vagy sem” kérdésre válaszol, és semmi másra — nem tesz különbséget a teljes beágyazás és a részhalmaz (subsetting) között, és nem vizsgálja a karakterlefedettséget sem; ha a fájl elbukik, és tudnia kell, hogy melyik lapon melyik betűtípus hibás, a PDF betűtípus-tulajdonságainak PDFium segítségével Delphi-ben történő elemzéséről szóló cikkben található bejárási technikák pontosan ott folytatják, ahol a validátor logikai értéke abbahagyta

Adatfolyamok ellenőrzése dokumentum vagy DLL betöltése nélkül

A bájtszintű vizsgáló önálló függvényként is elérhető: ValidatePdfXCompliance(Source: TStream) az FPdfPdfx egységben, és ez tiszta Object Pascal kód, amely nem függ a PDFium DLL-töl; ezáltal olyan helyeken is használható, ahol a renderelő motor nemkívánatos: egy könnyű feltöltési kapuként a webszerveren, egy CI feladatban, amely ellenőrzi a generált grafikákat, vagy egy Lazarus szolgáltatásban olyan platformon, ahová inkább nem szállítana natív binárisokat; táplálja bármilyen pozicionálható (seekable) adatfolyammal:

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;

A kompromisszum egyértelmű: az önálló útvonal lefuttatja a jelölőellenőrzéseket és mind a nyolc tartalomellenőrzést, pero nem futtatja le a betűtípusonkénti PDFium réteget, így a betűtípus-eredmény a durva heurisztikára esik vissza; a célszerű architektúra a ValidatePdfXCompliance-t használja olcsó első kapuként, a teljes TPdf.ValidatePdfX-et pedig a rajta átmenő fájlok számára tartja fenn

Ahol ez a validátor véget ér, és a teljes preflight (előzetes ellenőrzés) elkezdődik

A preflight eszközöknél fontos az őszinteség, így itt van a határ; a ValidatePdfX ellenőrzi az azonosító jelölőket, a strukturális tilalmakat, az oldalgeometriai kulcsokat, a Trapped deklarációt és a betűtípus-beágyazást egészen az egyes szöveges objektumokig; nem méri a teljes tintafedettséget (TAC), nem ellenőrzi, hogy minden színtér legális-e az igényelt változathoz (például az X-1a CMYK-only szabálya), nem ellenőrzi a képfelbontást a rácssűrűséggel szemben, és nem értékeli a felülnyomási és átlátszósági lapítási viselkedést — ezekhez színkezelt preflight motor szükséges, és a modul saját dokumentációja is azt javasolja, hogy a végső tanúsításhoz párosítsa egy ilyen szoftverrel; amit a kétrétegű ellenőrzés nyújt Önnek, az az elutasítások 80%-a, amelyek strukturálisak és korán észlelhetők, és ezredmásodpercek alatt elcsíphetők a saját Delphi kódján belül, ahelyett, hogy holnap érkezne meg a nyomdász e-mailje

Mindkettő ellenőrzési réteg, a megfelelő kimenet előállításához szükséges PDF/X jelölő-befecskendező API-k, valamint az ugyanezen architektúrát megosztó PDF/A, PDF/UA, PDF/E és PDF/VT validátorok a Delphi és C++Builder rendszerekhez készült PDFium Component részét képezik — egyetlen komponens a rendereléstől a nyomdai előkészítés ellenőrzéséig