Teknisk artikkel

PDF/X-validering i Delphi med PDFium Component

PDFium Component for Delphi validerer trykklare PDF/X-dokumenter gjennom TPdf.ValidatePdfX, som implementerer ISO 15930-kontroll i to lag: åtte innholdskontroller på byte-nivå (forbudt LZW-komprimering, JavaScript, skjema-felt, OPI-referanser, en manglende TrimBox, en udefinert Trapped-nøkkel, med mer) pluss en PDFium objektmodell-gjennomgang som bruker FPDFFont_GetIsEmbedded for å verifisere skrifttypeinnbygging på hvert tekstobjekt på hver side. Resultatet er en TPdfXValidationResult-post (record) som navngir det oppdagede samsvarsnivået og lister opp hvert brudd som en type-definert enum, slik at Delphi-applikasjonen din kan fortelle en kunde nøyaktig hvorfor en fil verdt å bli avvist i trykkeriet før noen bruker tid og ressurser på å lage en trykkplate

Hvis du noen gang har sendt en jobb to et kommersielt trykkeri og fått den i retur med en avvisning på én linje — "ingen TrimBox", "skrifttyper ikke innebygd", "Trapped ikke angitt" — kjenner du kostnaden ved å finne det ut for sen. PDF/X er pre-press-motstykket til PDF/A: der arkiverings-PDF/A garanterer at et dokument rendres identisk tiår fra nå, PDF/X garanterer at et dokument separeres, avbildes og beskjæres identisk på andres RIP i morgen tidlig. De to standardene deler mekanismer (XMP-identifikasjon, OutputIntents, innebygde ICC-profiler), men besvarer ulike spørsmål, og det er grunnen til at komponenten leveres med separate validatorer for hver — PDF/A-siden dekkes i PDF/A-preflightvalidering med PDFium Component

Hva krever ISO 15930 egentlig av en trykklar PDF?

ISO 15930 eksisterer for å gjøre blind utveksling (blind exchange) mulig: en designer overleverer en fil til et trykkeri de aldri har snakket med, og trykkeriet kan produsere riktige utskrifter uten telefonsamtaler, e-poster om manglende skrifttyper eller koblede bilder som ble liggende igjen på designerens bærbare datamaskin. Enhver regel i standarden tjener dette målet. Skrifttyper må bygges inn fordi det mottakende RIP-en ikke kan antas å eie dem. Eksterne referanser er forbudt fordi filen må være komplett i seg selv. Interaktive funksjoner er forbudt fordi blekk ikke har noen onclick-behandler

PDFium Component gjenkjenner tre samsvarsfamilier og rapporterer dem gjennom TPdfXConformance-enum i valideringsresultatet: pxc1a for PDF/X-1a:2001 (ISO 15930-1, den strenge CMYK-pluss-spot-grunnlinjen på PDF 1.3/1.4), pxc3 for PDF/X-3:2002 (ISO 15930-3, som tillater RGB, Lab og ICC-styrt farge), og pxc4 for PDF/X-4:2010 (ISO 15930-7, som endelig tillater live transparens og lag på en PDF 1.6-base). En fil som ikke bærer noen PDF/X-identifikasjon i det hele tatt kommer tilbake som pxcNone, noe som i seg selv er et nyttig svar: dokumentet hevdet aldri å være trykklart, og alt annet validatoren rapporterer forklarer hva som skal til for å komme dit

De forbudt-reglene gir mening når du tenker som en RIP-leverandør. /LZWDecode er forbudt i enhver PDF/X-variant, slik at en samsvarende konsument aldri avhenger av et filter med en kompatibilitets- og lisenshistorie; Flate gjør samme jobb uten denne bagasjen. JavaScript, AcroForm-felt og /AA-ordbøker for tilleggshandlinger er forbudt fordi en utskriftsfil må være en fast beskrivelse av merker på papir — alt som kan endre utseende ved åpningstidspunktet bryter garantien om at det som ble godkjent er det som trykkes. OPI-plassholdere (Open Prepress Interface) er forbudt fordi de etter design er referanser til høyoppløselige bilder lagret et annet sted, og "et annet sted" er nøyaktig hva blind utveksling forbyr

Hvorfor avviser trykkerier PDF-er uten TrimBox?

TrimBox er den ferdige siden — rektangelet som gjenstår etter at giljotinen har kuttet. MediaBox, som hver PDF-side har, er bare selve arket: det inkluderer utfall (bleed), skjæremerker, registreringsmerker og fargestriper. Utskytingsprogramvare (imposition software) plasserer sider på et trykkark etter deres TrimBoxer; uten en må operatøren gjette hvor visittkortet ditt faktisk slutter, og en feil gjetting kutter av utfallet ditt eller etterlater en hvit stripe på en kant. Det er grunnen til at ISO 15930 krever en TrimBox (eller en ArtBox) på hver side, og hvorfor ValidatePdfX utløser pvxiMissingTrimBox når ingen /TrimBox-nøkkel finnes på noen side i dokumentet

Nøkkelen /Trapped besvarer et annet produksjonsspørsmål. Trapping er en førtrykksteknikk (prepress) der man overlapper tilstøtende farger litt, slik at små feiljusteringer i pressen ikke åpner hvite mellomrom mellom dem. Trykkeriet må vite om det arbeidet allerede er gjort: trapping av en allerede trappet fil dobler overlappene, og å hoppe over trapping på en utrappet fil risikerer synlige mellomrom. PDF/X krever derfor at Info-ordboken oppgør /Trapped /True eller /Trapped /False eksplisitt — en manglende nøkkel eller /Unknown tvinger et menneske til å inspisere filen, noe som er nøyaktig den samtalen blind utveksling var ment å eliminere. Komponenten flagger dette som pvxiTrappedNotSet

Kjøre tolagsvalidering med TPdf.ValidatePdfX

TPdf.ValidatePdfX tar ingen argumenter og returnerer en TPdfXValidationResult-post (record) med tre medlemmer: Conformance (den oppdagede PDF/X-varianten), Issues (et Pascal-sett med TPdfXValidationIssue-verdier) og en IsCompliant-hjelper. Internt serialiserer den det lastede dokumentet til en minnestrøm, kjører inspektøren på bytenivå over det, og går deretter gjennom PDFiums objektmodell for innbyggingssjekk per skrifttype. En minimal preflight-port ser slik ut:

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;

Fordi Issues er et vanlig Pascal-sett, kan du dele det opp slik arbeidsflyten din krever — behandle strukturelle problemer som harde avvisninger, behandle pvxiMissingTitle (en bør-regel i standarden, ikke en må-regel) som en advarsel, og logg resten. Den samme posttypen mater også komponentens rapportgenerator, så hvis du heller vil generere et menneskelig lesbart dokument enn å forgrene deg på enum-verdier, gjelder mønsteret i oppbygging av en batch-preflight-rapport-CLI med PDFium Component uendret for PDF/X

Hva laget på bytenivå fanger opp — og hva det går glipp av

Laget på bytenivå er en skanning av dokumentets strukturelle byte med strøm-kropper fjernet (blanket out), slik at en JPEG som tilfeldigvis inneholder byte-mønsteret /JavaScript ikke kan utløse et falskt positivt resultat. På toppen av markør-kontrollene (XMP pdfxid:GTS_PDFXVersion, OutputIntent med en innebygd ICC-profil, trailer /ID, forbudet mot kryptering), legger innholdsgjennomgangen til åtte kontroller, hver med sin egen enum-verdi:

  • pvxiLzwForbidden — et /LZWDecode-filter vises hvor som helst i filen (forbudt i alle PDF/X-varianter)
  • pvxiJavaScriptForbidden — en /JavaScript-handling eller et navnetre er til stede
  • pvxiFormFieldsForbidden — en /AcroForm-ordbok eller /XFA-oppføring eksisterer
  • pvxiAdditionalActions — en /AA-ordbok for tilleggshandlinger er til stede
  • pvxiEmbeddedFilesForbidden/EmbeddedFiles eller en /FileAttachment-annotasjon er til stede
  • pvxiOpiForbidden — en /OPI- eller /Alternates-oppføring refererer til utskiftbart bildeinnhold
  • pvxiMissingTrimBox — ingen /TrimBox funnet på noen side
  • pvxiTrappedNotSet/Trapped mangler eller er satt til /Unknown

Byteskanning er raskt og trenger ingen renderingsmotor, men det har en iboende blindsone med skrifttyper: på det nivået kan inspektøren bare bruke en grov heuristikk — den flagger et dokument når den ikke finner noe innebygd skrifttypeprogram i det hele tatt. En fil med ni innebygde skrifttyper og én systemskrifttype sneket inn ser fin ut for en byteskanning. Det ene gapet er grunnen til at det andre laget eksisterer

Innbygging per skrifttype gjennom PDFium-objektmodellen

PDFium Components objektmodell-lag besvarer skrifttyspørsmålet presist. Etter gjennomgangen på bytenivå, itererer TPdf.ValidatePdfX gjennom hver side, ber om objektlisten via FPDFPage_CountObjects, og for hvert tekstobjekt løser den opp skrifttypehåndtaket via FPDFTextObj_GetFont og spør FPDFFont_GetIsEmbedded. Én ikke-innebygd skrifttype hvor som helst i dokumentet legger til pvxiPdfiumFontNotEmbedded i problemsettet. Traverset avbrytes tidlig (short-circuits) på to nivåer — det slutter å skanne objekter på en side, og slutter å laste flere sider i det øyeblikket problemet er bekreftet — så på en katalog med brudd på 300 sider, ankommer dommen ofte etter side én

To grensemerknader verdt å vite. For det første trenger dette laget at PDFium-biblioteket er lastet og krever bygg som eksporterer FPDFFont_GetIsEmbedded; når eksporten mangler hoppes sjekken over i stedet for å feile, slik at en eldre DLL aldri produserer falske avvisninger. For det andre besvarer sjekken "innebygd eller ikke" og ingenting mer — den skiller ikke fullstendig innbygging fra subsetting, og den sjekker heller ikke glyph-dekning. Når en fil feiler og du trenger å vite hvilken skrifttype på hvilken side, fortsetter tellingsteknikkene i analyse av PDF-skrifttypeegenskaper med PDFium i Delphi akkurat der validererens boolske verdi slipper taket

Validere strømmer uten å laste inn et dokument — eller DLL-en

Inspektøren på bytenivå er også eksponert som en frittstående funksjon, ValidatePdfXCompliance(Source: TStream) i FPdfPdfx-enheten, og er ren Object Pascal uten avhengighet av PDFium DLL-en. Det gjør den distribuerbar på steder der en renderingsmotor er uønsket: en lett opplastingsport på en webserver, en CI-jobb som sjekker generert grafikk, eller en Lazarus-tjeneste på en plattform der du helst ikke vil sende med native binærfiler. Mat den med en hvilken som helst søkbar strøm:

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;

Avveiningen er eksplisitt: den frittstående banen kjører markørkontrollene og alle de åtte innholdskontrollene, men ikke PDFium-laget per skrifttype, så dens skrifttypedom faller tilbake på den grove heuristikken. En fornuftig arkitektur bruker ValidatePdfXCompliance som den rimelige første porten, og reserverer den fullstendige TPdf.ValidatePdfX for filer som passerer den

Hvor denne validatoren slutter og en fullstendig preflight begynner

Ærlighet er viktig i preflight-verktøy, så her er grensen. ValidatePdfX verifiserer identifikasjonsmarkører, strukturelle forbud, sideoppsett-nøkler, Trapped-deklarasjonen og skrifttypeinnbygging ned til individuelle tekstobjekter. Den måler ikke total blekkdekning (TOC), validerer ikke at alle fargerom er lovlige for den påståtte varianten (for eksempel X-1as CMYK-bare-regel), sjekker ikke bildens oppløsning mot linjeskjermen, og evaluerer heller ikke overtrykk (overprint) og transparens-flating — disse trenger en fargestyrt preflight-motor, og enhetens egen dokumentasjon sier at den bør parres med en slik for endelig sertifisering. Det to-lags sjekken gir deg er de 80% av avvisningene som er strukturelle og kan oppdages tidlig, fanget på millisekunder i din egen Delphi-kode i stedet for i morgendagens e-post fra trykkeriet

Begge valideringslagene, PDF/X-markør-injeksjons-API-ene for å produsere samsvarende utskrifter, og PDF/A-, PDF/UA-, PDF/E- og PDF/VT-validatorene som deler samme arkitektur, leveres i PDFium Component for Delphi og C++Builder — én komponent, fra rendering til førtrykks-kontroll