Tehnički članak

PDF/VT varijabilni ispis podataka u Delphiju uz PDFium VCL

Transakcijska tiskara vraća vam seriju obračuna od 80,000 stranica s odbijenicom od jedne rečenice: "not PDF/VT, RIP cannot cache." Datoteka se normalno otvara u svakom pregledniku na radnom stolu, boje su točne, podaci su se ispravno spojili. Ništa od toga nije ono što je digitalna preša tražila. Brzi ispis varijabilnih podataka živi ili umire o tome može li preša prepoznati da je blok s logotipom kupca na stranici 1 bajt-po-bajt isti objekt kao i onaj na stranici 40,000, jednom ga iscrtati i ponovno upotrijebiti. PDF/VT je standard koji tu tvrdnju čini strojno provjerljivom, a "izgleda ispravno" upravo je zamka, jer je struktura koju RIP čita nevidljiva na ekranu

PDFiumPas tu strukturu izlaže kroz malo sučelje na TPdf: SaveAsPdfVT zapisuje, ValidatePdfVT provjerava. Ovaj članak govori o tome što ta dva metoda doista upisuju na disk i pregledavaju, gdje je ISO 16612-2 stroži nego što na prvi pogled izgleda, i koji su dijelovi poštena strukturna sidra, a ne potpuni preflight koji možete naplatiti klijentu

Što PDF/VT standardizira i zašto prvo dolazi PDF/X

PDF/VT (ISO 16612-2:2010) nije novi format datoteke. To je sloj optimizacijskih metapodataka nadograđen na PDF/X datoteku, i taj je redoslijed presudan. Standard definira tri razine usklađenosti, ali samo dvije od njih imenuju PDF datoteku: PDF/VT-1, a single self-contained document, and PDF/VT-2, a file-set model where pages reference shared external resources. The third token you may see, PDF/VT-2s, nije uopće vrijednost na razini datoteke; ona živi u MIME zaglavlju streama opisanom u Dodatku A. Ako pronađete kôd koji utiskuje GTS_PDFVTVersion = "PDF/VT-2s" u dokumentov XMP, taj je kôd pogrešan

Neupitan pravilnik za jednu datoteku jest PDF/X osnova. ISO 16612-2 §6.2.1 zahtijeva da svaka PDF/VT-1 datoteka također bude valjana PDF/X-4 datoteka. PDF/VT-2 skup datoteka, prema §6.2.2, umjesto toga mora stajati na PDF/X-4p, PDF/X-5g ili PDF/X-5pg. Zato pisac PDF/VT-a ne može samo dodati nekoliko identifikacijskih ključeva: mora ponijeti cijeli set oznaka PDF/X-4, što znači OutputIntent, ugrađeni ICC profil odredišta, odgovarajuće XMP i Info unose dokumenta, trailer /ID, a bez enkripcije. Preskočite bilo što od toga i imate datoteku koja tvrdi da je PDF/VT, a pada u trenutku kada je usklađeni potrošač provjeri prema osnovi. PDFiumPas tretira sloj PDF/X-4 kao dio spremanja PDF/VT-a, pa ne pozivate zaseban SaveAsPdfX prvo; injektor zapisuje oba sloja u jednom prolazu

Pisanje datoteke s SaveAsPdfVT

Minimalni poziv ne treba ništa osim aktivnog dokumenta, jer TPdfVTSaveOptions.Default isporučuje ugrađeni sRGB ICC profil i usklađenost pvc1. Spremanje interno prolazi kroz tri koraka: uklanja svaku zaštitu (ubacivanje čistog teksta u šifrirani object stream bi ga oštetilo), povezuje postojeći Info rječnik dokumenta i trailer /ID u set oznaka tako da se XMP i Info vrijednosti slažu, zatim dodaje PDF/X-4 i PDF/VT objekte kroz inkrementalni update

var
  Pdf: TPdf;
begin
  Pdf := TPdf.Create(nil);
  try
    if Pdf.LoadFromFile('statements-merged.pdf') then
    begin
      // Default options: built-in sRGB OutputIntent, PDF/VT-1, synthesised DPart
      if Pdf.SaveAsPdfVT('statements-pdfvt.pdf') then
        Writeln('PDF/VT-1 written')
      else
        Writeln('Save failed (document not active?)');
    end;
  finally
    Pdf.Free;
  end;
end;

Za stvarni produkcijski izlaz gotovo uvijek želite nadjačati OutputIntent karakterizacijom svoje preše, a ne generičkim sRGB fallbackom. Dostavite ICC bajtove i identifikatore uvjeta kroz TPdfVTSaveOptions:

var
  Pdf: TPdf;
  Opt: TPdfVTSaveOptions;
  Icc: TBytes;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.LoadFromFile('directmail-merged.pdf');
    Icc := LoadIccProfile('GRACoL2013_CRPC6.icc');  // your own loader

    Opt := TPdfVTSaveOptions.Default;
    Opt.Conformance := pvc1;            // pvc2 is normalised to pvc1 on write
    Opt.IccProfileData := Icc;
    Opt.OutputConditionIdentifier := 'CGATS21_CRPC6';
    Opt.OutputCondition := 'Commercial print, coated, CRPC6';
    Opt.RegistryName := 'http://www.color.org';
    Opt.Title := 'Spring 2026 Direct Mail Run';
    Opt.Trapped := ptvFalse;           // PDF/X Info /Trapped state

    Pdf.SaveAsPdfVT('directmail-pdfvt.pdf', Opt);
  finally
    Pdf.Free;
  end;
end;

Jedan detalj u tom isječku namjerno je ograničenje, a ne mana o kojoj možete raspravljati. Postavljanje Opt.Conformance := pvc2 ne proizvodi PDF/VT-2 datoteku. Pisac normalizira svaki zahtjev koji nije pvc1 natrag na pvc1, jer PDF/VT-2 je format skupa datoteka, a pisac za jednu datoteku koji dodaje jedan izlazni dokument fizički ne može sastaviti skup vanjskih resursa koji traži §6.2.2. Vrijednost pvc2 postoji za put čitanja, pa ValidatePdfVT može prepoznati i prijaviti postojeći dokument skupa datoteka; to nije cilj pisanja

DPart stablo: struktura koju RIP doista čita

Srce PDF/VT-a je hijerarhija Document Part (DPart). Ona omogućuje tiskari da dugačak run podijeli u zapise, grupira zapise u primatelje ili poštanske pakete, i priloži Document Part Metadata tako da downstream oprema može usmjeravati i naplaćivati svaki komad. ISO 16612-2 §6.5 opisuje povezivanje: katalog nosi /DPartRoot, korijenski DPart čvor nosi /DPartRootNode i /NodeNameList koji imenuje svaku razinu hijerarhije, leaf DPartovi pokrivaju raspon stranica stabla, a svaka stranica koja pripada dijelu pokazuje natrag na svoj list preko unosa /DPart na razini stranice

Kad izvorni dokument već sadrži upotrebljivu hijerarhiju, SaveAsPdfVT je čuva. Kad ne sadrži, pisac sintetizira minimalnu: jedan DPart na razini dokumenta koji obuhvaća trenutno stablo stranica redom, s /DPart povratnom referencom dodanom svakom živom objektu stranice i jednorazinskim /NodeNameList [/Document]. Budite iskreni prema sebi o tome što je to minimalno stablo. To je strukturno sidro koje zadovoljava zahtjeve oblika iz §6.5; nije poslovni metapodatak. Ono ne može izmišljati primatelje, granice poštanskih jedinica ni proizvodne serije, jer te informacije nikad nisu bile u izvoru. Ako imate podatke po primatelju, očekuje se da sami izgradite dublje DPart stablo i proširite /NodeNameList tako da odgovara razinama koje stvarate

Provjera koja ide dalje od prisutnosti ključeva

ValidatePdfVT vraća TPdfVTValidationResult zapis s tri stvari: otkrivenom Conformance, skupom Issues, i pomoćnikom IsCompliant koji je true samo kada je conformance stvarna razina i skup problema prazan. Enumeracija problema namjerno je specifična, pa neuspjeli rezultat kaže koji ste članak propustili, a ne samo "invalid":

var
  Pdf: TPdf;
  Res: TPdfVTValidationResult;
begin
  Pdf := TPdf.Create(nil);
  try
    Pdf.LoadFromFile('statements-pdfvt.pdf');
    Res := Pdf.ValidatePdfVT;

    if Res.IsCompliant then
      Writeln('PDF/VT compliant: ', VTLevelName(Res.Conformance))
    else
    begin
      if pvviMissingDPartRoot in Res.Issues then
        Writeln('DPart hierarchy missing or unusable');
      if pvviMissingPdfXIdentifier in Res.Issues then
        Writeln('PDF/X-4 base identifier absent');
      if pvviMissingOutputIntent in Res.Issues then
        Writeln('OutputIntent / ICC profile missing');
      if pvviEncryptionPresent in Res.Issues then
        Writeln('Encrypted - PDF/X forbids this');
    end;
  finally
    Pdf.Free;
  end;
end;

Dvije provjere koje vrijedi dubinski razumjeti su uparivanje usklađenosti i DPart prolaz, jer su obje prije bile preblage i pooštrene su da odgovaraju specifikaciji. Na strani uparivanja, validator radi točno podudaranje, a ne "bilo koji PDF/X će proći": PDF/VT-1 datoteka prihvaća se samo na PDF/X-4 PDF/X-4 bazu, i PDF/VT-2 datoteka samo na PDF/X-4p, PDF/X-5g, ili PDF/X-5pg. Marker PDF/VT-1 na PDF/X-1a bazi prijavljuje se, ne prolazi dalje

DPart prolaz je mjesto gdje leži većina strogosti. Nije dovoljno da katalog ima /DPartRoot ključ, jer krivotvoreni prazan objekt ili onaj bez veza na stranice i dalje ne može biti potrošen.HasValidDPartHierarchy i rekurzivni ValidateDPartNode prate cijelu strukturu: prate roditeljske veze, odbacuju duple djece i cikluse, nameću da /Start i /DParts budu međusobno isključivi, i zahtijevaju da rasponi stranica lista pokriju stablo stranica u dubinskom redoslijedu, a svaka stranica mora imati svoj /DPart koji pokazuje na list koji je sadrži. Svi ti unutarnji kvarovi slijevaju se u jedan jedini bit problema pvviMissingDPartRoot umjesto da šire javnu enumeraciju, pa taj jedan zastavicu tretirajte kao "DPart hijerarhija je neupotrebljiva", a ne doslovno "root ključ nedostaje"

Tri sintaktičke zamke koje validator sada provjerava

Uzastopni prolazi kroz §6.5 Tablicu 4 otkrili su oblike koje su ranije verzije prihvaćale, a standard ne. To su stvari koje ručno građeno DPart stablo lako pogriješi, pa ih vrijedi izričito navesti:

  • /DParts je niz nizova, a ne ravni niz. Svaki element vanjskog niza mora sam biti niz indirektnih referenci. Ravni /DParts [9 0 R] se odbacuje; usklađeni oblik je /DParts [[9 0 R] [10 0 R]]. Time se ne-hijerarhijska struktura sprječava da se prerušava u valjanu razinu
  • /End samo označava stvarni višestranični raspon. List DPart može nositi /End samo kada također ima /Start, i /End mora dolaziti kasnije od /Start u redoslijedu stabla stranica. Degenerirani /Start 3 0 R /End 3 0 R sada čini hijerarhiju neupotrebljivom umjesto da se čita kao jednodijelni dio
  • /NodeNameList nazivi moraju proći PDF name unescaping kao XML NMTOKEN-ovi. Naziv poput /Bad#20Name se proširi u onaj koji sadrži razmak, a to nije valjani token. Implementacija radi laganu ASCII provjeru (slova, znamenke, ., -, _, :, plus non-ASCII bytes) that catches whitespace and delimiter mistakes without rejecting legitimate localized or vendor-specific names

XMP oznake: dva načina za zapis iste osobine

PDF/VT identifikacija živi u XMP-u pod pdfvtid namespaceom, konkretno GTS_PDFVTVersion i GTS_PDFVTModDate, uz standardne xmp:CreateDate i xmp:ModifyDate. Suptilnost koja uzrokuje lažna izvješća o "missing" u naivnim čitačima jest da se bilo koji od njih može serijalizirati na dva načina: kao tekst elementa (<pdfvtid:GTS_PDFVTVersion>PDF/VT-1</pdfvtid:GTS_PDFVTVersion>) ili kao RDF atribut na description elementu. PDFiumPas čita oba oblika, pa datoteka koju je drugi alat zapisao u atributskom stilu nije kažnjena. Također provodi §6.3 pravilo dosljednosti da GTS_PDFVTModDate mora biti jednak xmp:ModifyDate; neusklađenost podiže pvviModDateMismatch

Još jedno pravilo iz istog članka: nepoznata vrijednost GTS_PDFVTVersion čuva se kao pvcUnknown umjesto da se preklopi natrag na pvcNone. Ta razlika operativno je važna. pvcNone znači "nema PDF/VT oznake uopće, običan PDF", dok pvcUnknown znači "nešto je utisnulo verziju koju ovaj validator ne prepoznaje" (među njima i slučaj PDF/VT-2s). Spajanje ta dva sakrilo bi neispravnu datoteku u istu ladicu kao običan dokument

Gdje završava jamstvo

Vrijedi precizno odrediti granicu onoga što ove metode obećavaju, jer usklađenost varijabilnog ispisa nosi stvaran novac. Provjere DPart i pairinga su strukturna provjera na razini bajtova. One potvrđuju da su optimizacijski kostur, osnovni PDF/X-4 markeri, OutputIntent i XMP prisutni i međusobno dosljedni. To nije preflight PDF/X-4 na razini sadržaja: ne provjeravaju je li svaka boja unutar deklariranih izlaznih uvjeta, jesu li svi fontovi ugrađeni ili je li se provukao neki zabranjeni rubni slučaj blending prozirnosti. Za posao koji idete staviti na ugovornu prešu, spojite strukturnu provjeru PDFiumPasa s namjenskim PDF/X preflight engineom i probnim otiskom, isto kao što biste provjerili bilo koju drugu tvrdnju o usklađenosti. Strukturni sloj hvata kvarove koji tiho razbijaju RIP cache; to je jedna polovica potpune provjere, ne cijela

Ako te provjere ugrađujete u širi release gate, isti pristup skeniranja na razini bajtova stoji iza ostalog standardskog rada biblioteke, uključujući provjeravanje object i cross-reference streamova prije nego što datoteka ikad dođe do preflighta, i disciplinu zajedničkih objekata iza ponovno upotrebljivi pečati stranica s Form XObjectima koji čini dokument pogodnim za RIP od samog početka. API-ji za spremanje i provjeru PDF/VT-a i PDF/X-a opisani ovdje dio su PDFium VCL komponente za Delphi i C++Builder, čija stranica proizvoda sadrži potpunu referencu usklađenosti