Articolo tecnico

Output PDF/X-1a, X-3 e X-4 in Delphi con PDFlibPas

PDFlibPas produce file di stampa PDF/X da Delphi con una sola chiamata, TPDFlib.SetPDFXMode, che seleziona PDF/X-1a:2003, PDF/X-3:2003 o PDF/X-4. La chiamata scrive l'identificazione XMP pdfxid, aggiunge un output intent GTS_PDFX, dichiara /Trapped /False, fissa la versione del PDF e da quel momento in poi rifiuta le chiamate di disegno che la parte scelta vieta. CheckFileCompliance con test 3 controlla il file salvato

Questa è la risposta breve. Quella lunga conta, perché le tipografie non scartano i file perché sono brutti, li scartano in preflight, di solito di venerdì, di solito per qualcosa che il codice generatore avrebbe potuto intercettare: un logo RGB in un lavoro X-1a, un font referenziato invece che incorporato, una pagina senza formato di trancio. PDF/X esiste per rendere quei fallimenti strutturali e non visivi, e il compito della libreria è impedirvi di produrli in partenza

Quale parte PDF/X deve puntare un'applicazione Delphi?

Scegliete PDF/X-4 salvo che il workflow di destinazione dica altro, e scegliete PDF/X-1a quando lo dice. Le tre parti differiscono lungo esattamente tre assi, colore, trasparenza e versione base del PDF, e ogni asse corrisponde a un gate dentro PDFlibPas:

  • PDF/X-1a:2003 (ISO 15930-4): sintassi PDF 1.4, solo CMYK, grigio e colori spot, niente colore basato su ICC, niente trasparenza. Il file è già separato nei colori di stampa, così la tipografia non fa alcuna conversione di colore
  • PDF/X-3:2003 (ISO 15930-6): sempre PDF 1.4 e sempre niente trasparenza, ma gli spazi con gestione del colore sono ammessi, e l'RGB device viene accettato quando l'output intent stesso descrive una condizione RGB
  • PDF/X-4 (ISO 15930-7): sintassi PDF 1.6, con trasparenza viva e optional content (layer). È la parte per cui sono costruiti i RIP moderni, perché appiattire la trasparenza in uscita è da dove veniva gran parte del vecchio dolore di X-1a

X-1a è ancora ovunque nella consegna pubblicitaria e nei workflow di stampa più vecchi che vogliono un file da esporre senza pensarci sopra. X-4 è la scelta di ingegneria migliore ogni volta che la tipografia lo accetta: la trasparenza sopravvive intatta, e le decisioni di colore avvengono al RIP con il vero profilo di stampa invece che nel vostro codice. I numeri di modalità sono 1, 2 e 3 rispettivamente, e GetInformation(203) riporta la modalità attiva. PDFlibPas definisce anche numeri di modalità più alti per la famiglia PDF/X-6 su PDF 2.0, che seguono regole di versione e di output intent diverse e qui non sono trattati

Cosa scrive SetPDFXMode nel file

TPDFlib.SetPDFXMode gestisce l'identificazione e il boilerplate che un lettore PDF/X controlla per primo, così il codice applicativo deve fornire soltanto il vero profilo di stampa e il contenuto. In concreto, una chiamata riuscita fa quattro cose:

  • Scrive la stringa di edizione in XMP come pdfxid:GTS_PDFXVersion (PDF/X-1a:2003, PDF/X-3:2003 o PDF/X-4). Le edizioni 2003 identificano la parte completamente tramite quella stringa, quindi non viene scritta una proprietà di conformità separata
  • Aggiunge un output intent se il documento non ne ha uno e ne imposta il sottotipo a /S /GTS_PDFX, il sottotipo che i lettori PDF/X cercano (ISO 32000-1 §14.11.5 definisce il dizionario di output intent)
  • Imposta /Trapped /False nel dizionario informativo del documento, perché lo scrittore non fa mai trapping e PDF/X richiede che lo stato di trapping sia dichiarato invece che lasciato sconosciuto
  • Fissa la versione base: 1.4 per X-1a e X-3, 1.6 per X-4

Due altre riparazioni avvengono al salvataggio. Ogni pagina senza TrimBox ne riceve una uguale al suo MediaBox, dato che PDF/X richiede un formato finale dichiarato su ogni pagina (ISO 32000-1 §14.11.2 tratta i confini di pagina), e le annotazioni ricevono il flag di stampa che lo standard si aspetta. Il ripiego sul TrimBox è una rete di sicurezza, non una decisione di layout. Un lavoro a piena pagina senza margini ha bisogno di un vero trim box dentro un media box più grande, e la guida ai valori predefiniti di TrimBox, BleedBox e CropBox in PDFlibPas spiega come impostare quei box e come l'ereditarietà può riservarvi sorprese

uses
  PDFlibrary;

procedure BuildX1aAd(const OutFile, PressProfile: string);
var
  Pdf: TPDFlib;
begin
  Pdf := TPDFlib.Create;
  try
    if Pdf.SetPDFXMode(1) <> 1 then          // 1 = PDF/X-1a:2003
      raise Exception.CreateFmt('PDF/X mode refused (error %d)',
        [Pdf.LastErrorCode]);

    // Sostituite il profilo predefinito con la vera condizione di stampa
    if Pdf.LoadOutputIntentProfile(PressProfile, 'DeviceCMYK') <> 1 then
      raise Exception.Create('Could not load the CMYK output profile');

    // Embed = 1 incorpora il programma font completo; PDF/X non accetta 0
    if Pdf.AddTrueTypeFont('Arial {1252}', 1) = 0 then
      raise Exception.Create('Font could not be loaded for embedding');

    Pdf.SetTextSize(24);
    Pdf.SetFillColorCMYK(0, 1, 1, 0);           // rosso process, niente RGB
    Pdf.DrawText(72, 720, 'Spring catalog, page 1');

    Pdf.SaveToFile(OutFile);
  finally
    Pdf.Free;
  end;
end;

L'ordine in quello schema è voluto. LoadOutputIntentProfile sostituisce il profilo di un output intent che esiste già, quindi deve girare dopo che SetPDFXMode ne ha creato uno. La chiamata inoltre non può congiurare un intent dal nulla: su un documento senza output intent restituisce semplicemente 0

Perché l'output intent predefinito va sostituito?

L'output intent che SetPDFXMode aggiunge per le modalità da 1 a 3 dichiara DeviceCMYK, ma i byte ICC dietro di esso sono il profilo sRGB incluso nella libreria con il numero di componenti riscritto a 4. Quel predefinito esiste per compatibilità all'indietro e basta a rendere il file strutturalmente PDF/X, eppure il profilo incorporato non descrive alcuna stampa. Uno strumento di preflight che ispeziona l'intestazione del profilo vedrà un profilo RGB con l'etichetta CMYK, e una tipografia che fa affidamento sull'intent per la prova riceve assurdità

Quindi trattate LoadOutputIntentProfile(FileName, 'DeviceCMYK') come obbligatorio per il lavoro di stampa vero e passate il profilo ICC che il vostro stampatore indica per carta e macchina. TPDFlib.LoadOutputIntentProfile sostituisce lo stream DestOutputProfile e aggiorna /N, e lascia il resto del dizionario dell'intent come scritto. In particolare, l'identificatore della condizione di output e le stringhe descrittive dell'intent predefinito portano ancora valori in stile sRGB, quindi se la vostra tipografia confronta l'identificatore invece del profilo incorporato, confermate con lei cosa controlla prima di spedire. È una lacuna genuina dei predefiniti dell'era 2003, non un errore del vostro codice

Trappola numero uno: la versione che non avete scelto

Un nuovo documento PDFlibPas parte dal predefinito dello scrittore, PDF 1.5, che sta sopra la base 1.4 che PDF/X-1a e PDF/X-3 richiedono. TPDFlib.SetPDFXMode lo risolve in silenzio nel caso comune: se la versione di salvataggio non è stata bloccata, abbassa la versione del documento alla base della parte e va avanti. La prima implementazione rifiutava invece, e ogni SetPDFXMode(1) su un documento nuovo restituiva 0, buon esempio di regola corretta sulla carta e inutile nella pratica

Il rifiuto esiste ancora per l'unico caso in cui è giusto. Se qualcosa in precedenza ha chiamato LockSaveVersion con una versione sopra la base, SetPDFXMode restituisce 0 con LastErrorCode 601, perché chi chiamava ha fissato la versione apposta e la libreria non stravolgerà quella scelta. Nel codice di setup condiviso del documento, dove un helper blocca la versione per ragioni non correlate, è esattamente qui che un lavoro X-1a muore:

Pdf := TPDFlib.Create;
try
  Pdf.LockSaveVersion('1.7');     // impostato da un helper generico di «default di casa»
  if Pdf.SetPDFXMode(1) = 0 then
    // LastErrorCode = 601: 1.7 è bloccato, PDF/X-1a vuole 1.4.
    // Saltate il blocco per i lavori di stampa. PDF/X-4 non è una via di fuga qui:
    // la sua base 1.6 sta anch'essa sotto il blocco 1.7 e fallisce allo stesso modo.
    LogError('PDF/X refused, error %d', [Pdf.LastErrorCode]);
finally
  Pdf.Free;
end;

Lo stesso gate spiega un limite correlato. La modalità PDF/X è un interruttore da creazione documento: SetPDFXMode funziona su un documento nuovo e restituisce 0 su uno caricato da file, perché la libreria non può rispondere di contenuti che non ha scritto lei. Convertire un PDF esistente in PDF/X è un lavoro diverso che richiede ispezione e riparazione, non un flag

Trappola numero due: gate che rifiutano in silenzio

Quando una modalità PDF/X è attiva, PDFlibPas rifiuta ogni chiamata di contenuto che la parte scelta vieta, nel momento in cui la fate, invece di lasciare che il file fallisca il preflight più tardi. I rifiuti coprono riempimenti e tratti RGB device (tutta X-1a; X-3 e X-4 salvo che l'output intent sia RGB), colori basati su ICC in X-1a, trasparenza e blend mode in X-1a e X-3, optional content in X-1a e X-3, azioni JavaScript, allegati di file incorporati, il filtro immagini LZW, i font Standard 14 che resterebbero non incorporati e la cifratura a chiave pubblica. La cifratura con password tramite Encrypt non è bloccata alla creazione, ma il validatore segnala qualunque file cifrato, quindi tenete i lavori di stampa non cifrati

Ciò che i rifiuti non condividono è un codice di errore, ed è qui la trappola. I gate degli spazi colore in SetFillColor, SetLineColor, le chiamate di colore basato su ICC e EncryptPublicKey impostano LastErrorCode a 601, il codice di conformità della libreria che usano anche i gate PDF/A. I gate di trasparenza, JavaScript, allegati, LZW e AddStandardFont restituiscono semplicemente 0 e lasciano LastErrorCode intatto, il che significa che può ancora contenere un valore di una chiamata precedente. Il codice che controlla solo LastErrorCode dopo una serie di chiamate di disegno si perde metà dei rifiuti, quindi controllate ogni valore di ritorno:

procedure Require(Ok: Integer; const What: string);
begin
  if Ok = 0 then
    raise EPrintConformance.CreateFmt('%s refused under PDF/X', [What]);
end;

Require(Pdf.SetFillColorCMYK(0, 0, 0, 1), 'CMYK fill');
Require(Pdf.SetTransparency(40), 'transparency');   // 0 in X-1a / X-3
Require(Pdf.AddLinkToJavaScript(72, 72, 120, 20,
  'app.alert(1)', 0), 'JavaScript link');           // 0 in ogni parte

I font meritano un avvertimento a parte. AddStandardFont restituisce 0 sotto PDF/X perché la libreria non include i contorni dei Standard 14 e quindi non può incorporarli. AddTrueTypeFont è diverso: in modalità PDF/A promuove Embed = 0 a un incorporamento completo per voi, ma in modalità PDF/X non lo fa, quindi un font di sistema referenziato finisce nel file e solo il validatore lo becca. Passate 1 (completo) o 2 (subset) voi, oppure chiamate SetEmbedAllFonts(1) una volta al setup così uno 0 smarrito non può sgusciare attraverso

Come verificare un file PDF/X prima che vada in tipografia?

Eseguite CheckFileCompliance con ComplianceTest = 3 sul file salvato. TPDFlib.CheckFileCompliance restituisce 0 quando non ha registrato rilievi, e altrimenti un handle di string list i cui elementi sono righe con prefisso PDFX-. L'audit PDF/X legge il file attraverso lo stesso reader in streaming del preflight PDF/A e PDF/UA in PDFlibPas, e controlla le regole che un preflight di stampa controlla per primo:

  • Il file non è cifrato, l'XMP porta una GTS_PDFXVersion nota, e la versione del PDF non supera la base della parte
  • Esiste esattamente un output intent, il suo sottotipo è GTS_PDFX, e porta un profilo di destinazione e un identificatore di condizione di output
  • Il dizionario informativo dichiara /Trapped
  • Ogni pagina ha un TrimBox che sta dentro il MediaBox, e un BleedBox, quando presente, racchiude il TrimBox
  • Ogni font usato su una pagina ha un programma incorporato, con i font Type0 controllati attraverso il loro font discendente
  • Nessun tipo di annotazione vietato (RichMedia, Screen, 3D, Movie, Sound, FileAttachment), nessuna azione open JavaScript o JavaScript a livello di documento, nessun file incorporato, e nessun filtro LZWDecode da nessuna parte nel grafo degli oggetti
function PreflightPDFX(Pdf: TPDFlib; const FileName: string;
  Report: TStrings): Boolean;
var
  ListId, I: Integer;
begin
  ListId := Pdf.CheckFileCompliance(FileName, '', 3, 0);  // 3 = PDF/X
  Result := ListId = 0;
  if Result then
    Exit;
  try
    for I := 1 to Pdf.GetStringListCount(ListId) do       // gli elementi partono da 1
      Report.Add(Pdf.GetStringListItem(ListId, I));
  finally
    Pdf.ReleaseStringList(ListId);
  end;
end;

Siate chiari su cosa significa quel superamento. Il validatore PDFlibPas controlla le regole strutturali elencate sopra, e sono quelle che beccano gli errori del generatore. Non è un preflight colorimetricamente accurato: non controlla la copertura totale di inchiostro, la risoluzione delle immagini o se il profilo incorporato corrisponde alla condizione che la vostra tipografia usa. Usatelo come gate nella vostra pipeline di build o esportazione, e lasciate i controlli specifici di macchina al profilo di preflight della tipografia, che tanto eseguiranno comunque

FAQ

PDFlibPas supporta la trasparenza di PDF/X-4?

Sì. Con SetPDFXMode(3), PDFlibPas consente trasparenza, blend mode e gruppi di optional content e scrive un file PDF 1.6. Le stesse chiamate restituiscono 0 sotto PDF/X-1a e PDF/X-3

PDFlibPas può convertire un PDF esistente in PDF/X?

No. SetPDFXMode si applica solo ai documenti creati nella sessione TPDFlib corrente e restituisce 0 per un file caricato. Potete comunque controllare qualunque PDF esistente con CheckFileCompliance e test 3

Quale codice di errore restituisce una chiamata PDF/X rifiutata?

I rifiuti legati agli spazi colore e alla cifratura a chiave pubblica impostano LastErrorCode a 601. Altri gate, come trasparenza, JavaScript e font Standard 14, restituiscono solo 0, quindi testate sempre il risultato della funzione

L'output PDF/X, il validatore PDF/X e il resto del set di strumenti di stampa e archiviazione fanno parte di PDFlibPas, la libreria PDF native Object Pascal di losLab per Delphi. Vedete la pagina di prodotto di PDFlibPas Delphi PDF Library per edizioni, piattaforme e il riferimento API completo