Articolo tecnico

Convertire un PDF in un unico spazio colore in Delphi

Un'intestazione riempita con 1 0 0 rg, rosso puro, esce da una conversione in grigio come 0.299 g. Il blu puro atterra a 0.114 g, così vicino al nero che un logo blu navy e il testo del corpo diventano difficili da distinguere. Quei numeri non sono un incidente di arrotondamento: sono i pesi di luminanza BT.601, gli stessi che un viewer applica quando mostra una pagina RGB su un dispositivo in grigio, e decidono quanto contrasto sopravvive quando una brochure a colori deve diventare in grigio

Che cosa calcola RecolorDocument per ogni colore

PDF Library for Delphi espone la conversione come RecolorDocument(TargetSpace) su TPDFlib, con 1 per il grigio, 2 per RGB e 3 per CMYK. Restituisce il numero di cose convertite, e geometria, posizioni del testo e collocazione delle immagini restano dove erano. Cambiano solo i valori del colore e i nomi degli spazi colore attorno a essi

Ogni colore passa per RGB sulla via verso il target. Il grigio è la somma di luminanza 0.299 R + 0.587 G + 0.114 B. Il CMYK arriva a RGB con la semplice regola device di ISO 32000-1 §10.3, dove ogni canale è uno meno la somma del suo inchiostro e del nero, limitata a zero. L'RGB arriva al CMYK tirando la componente maggiore nel nero: K = 1 - max(R, G, B), e gli inchiostri rimanenti vengono scalati da ciò che resta. Qualche risultato da conoscere prima di inviare qualsiasi cosa a una stampante:

  • Un grigio di 0.4 g convertito in CMYK diventa 0 0 0 0.6 k, solo inchiostro nero, che è ciò che una stampa vuole per testo e filetti
  • RGB 1 0.5 0 (arancione) diventa 0 0.5 1 0 k, senza nessun nero
  • Un nero ricco come 0.6 0.4 0.4 1 k diventa 0 g sulla via verso il grigio, quindi gli inchiostri extra semplicemente scompaiono
  • Il ciano puro, 1 0 0 0 k, si trasforma in 0.701 g, più chiaro di quanto la maggior parte delle persone si aspetti da un inchiostro saturo

Gli operandi convertiti vengono scritti con al massimo tre cifre decimali e sempre con il punto come separatore decimale, qualunque sia il locale della macchina che esegue il job. ISO 32000-1 §7.3.3 non consente altro in un content stream, e un convertitore che formatta i numeri con il locale di sistema scriverà 0,5 g su una workstation tedesca, che un reader interpreta come il numero 0 seguito da un operatore sconosciuto

var
  Lib: TPDFlib;
  Converted: Integer;
begin
  Lib := TPDFlib.Create;
  try
    if Lib.LoadFromFile('brochure.pdf', '') <> 1 then
      raise Exception.Create('Could not open brochure.pdf');
    Converted := Lib.RecolorDocument(1);   // 1 = grigio, 2 = RGB, 3 = CMYK
    WriteLn(Format('%d colors, images and gradients converted', [Converted]));
    Lib.SaveToFile('brochure-gray.pdf');
  finally
    Lib.Free;
  end;
end;

Una pagina i cui colori sono già nello spazio target conserva i propri byte di contenuto originali. RecolorDocument conta solo le conversioni che attraversano davvero uno spazio verso un altro, quindi un documento già in grigio riporta 0 e niente al suo interno viene riscritto. Su un file grande conta: un diff di input e output mostra esattamente le pagine cambiate

Perché RecolorPage lascia la foto di copertina a colori?

RecolorPage converte gli operatori di colore sulla pagina selezionata e si ferma lì. Le immagini e i gradienti sono oggetti a livello di documento, spesso condivisi da più pagine, e solo RecolorDocument li converte. Così il modo ovvio di convertire solo una copertina, selezionare la pagina 1 e chiamare RecolorPage(3), vi dà intestazioni e filetti CMYK sopra una fotografia RGB

Questa divisione è deliberata. Un image XObject disegnato sulla copertina può essere disegnato anche alla pagina 12, e convertirlo per una pagina lo converte per entrambe. Una chiamata limitata alla pagina che arrivasse silenziosamente nelle risorse condivise cambierebbe pagine che il chiamante non ha mai selezionato. È la sorpresa peggiore delle due, quindi la chiamata per pagina tocca solo ciò che appartiene alla pagina

// Solo operatori sulla copertina; immagini e gradienti non vengono convertiti qui
Lib.SelectPage(1);
Changed := Lib.RecolorPage(3);
if Changed = 0 then
  WriteLn('Cover already uses CMYK operators (or the target was out of range)');
Lib.SaveToFile('cover-cmyk-operators.pdf');

// Per un documento che deve stare in un unico spazio ovunque, convertite tutto
Lib.RecolorDocument(3);
Lib.SaveToFile('all-cmyk.pdf');

Guardate il valore di ritorno in quello snippet. Entrambe le chiamate restituiscono 0 per un target fuori dall'intervallo 1-3, e 0 è anche la risposta per una pagina che non aveva bisogno di modifiche, quindi un typo nel target sembra esattamente un documento già corretto. Validate l'argomento da voi prima della chiamata. RecolorDocument ripristina la pagina selezionata prima di girare, il che significa che potete chiamarlo in mezzo a un job pagina per pagina senza riselezionare dopo

Un altro dettaglio di scope. Il passaggio sugli operatori legge il content stream di ciascuna pagina, le istruzioni di disegno elencate sulla pagina stessa, e converte g, rg, k, le loro forme di tratto, e sc / scn quando lo spazio corrente è uno spazio device. Quando /DeviceRGB cs precede uno sc convertito, anche il nome dello spazio viene riscritto verso il target. Saltate quel rinominare in un convertitore scritto a mano e un reader resta a cercare tre componenti RGB dove ora siede un valore grigio

Immagini e palette: quali cambiano, quali conservano i byte

Le immagini contano quanto gli operatori. Una brochure convertita in grigio con i soli operatori gestiti mostra ancora fotografie a colori su pagine in grigio, il che si legge peggio sia dell'originale sia di una conversione completa. RecolorDocument converte un'immagine quando ha otto bit per componente, uno spazio colore device, e nessuna compressione oppure Flate semplice senza parametri decode

Le immagini indicizzate prendono una via diversa. I loro campioni sono indici in una tabella di colori, quindi la tabella viene convertita e ogni campione conserva l'indice che già aveva. Uno screenshot o un grafico salvato come immagine a palette di 256 colori si converte in questo modo indipendentemente da come i suoi campioni sono impacchettati o compressi, perché i campioni non vengono mai decodificati. La base della palette deve essere uno spazio device; una palette sopra un profilo ICC conserva la sua tabella

Diagramma che mostra quali parti di un PDF riscrive RecolorDocument in Delphi e quali parti come immagini JPEG e soft mask passano attraverso identiche al byte
Un passaggio RecolorDocument riscrive operatori, immagini idonee, palette indicizzate e coppie di gradienti mentre i byte JPEG, le maschere e i colori vincolati a profili viaggiano intatti

Un'immagine JPEG o codificata fax conserva i propri byte. Cambiarne i colori significherebbe decodificarla, convertirla e ricomprimerla, il che aggiunge una seconda generazione di artefatti JPEG e cambia la dimensione del file in modi che nessuno ha chiesto. Se un job ha davvero bisogno di ogni foto in grigio, la ricodifica è una decisione a parte con la sua impostazione di qualità, e preferiamo che la faccia esplicitamente il chiamante invece di una conversione di colore che la faccia per lui

Le soft mask e le stencil mask restano come sono per un motivo diverso. Una soft mask di solito è un'immagine DeviceGray, ma i suoi valori significano copertura. Convertitene una in RGB a tre componenti e un viewer smette di trattarla come trasparenza; finisce dipinta come artwork in grigio sopra il contenuto che avrebbe dovuto modellare. RecolorDocument raccoglie ogni oggetto referenziato attraverso /SMask o /Mask prima di convertire qualsiasi immagine, e un'immagine con /ImageMask true non ha colore da convertire in partenza

I colori impostati attraverso spazi ICCBased, Separation, Indexed o Pattern al livello degli operatori vengono lasciati anche loro come scritti. Trasformare un colore spot o un valore taggato con profilo in grigio device senza leggere il suo profilo cambierebbe l'aspetto della pagina, e un passaggio il cui compito è conservare l'aspetto non può mettersi a indovinare cosa intendeva un profilo

Gradienti: lo spazio colore e gli stop si muovono insieme

Uno shading dichiara il proprio spazio colore una volta sola e consegna ogni stop a una funzione. Rinominate /ColorSpace da DeviceRGB a DeviceGray mentre la funzione restituisce ancora tre numeri, e un reader prende il primo di essi, il canale rosso, come livello di grigio. Un gradiente da blu a bianco si renderizza allora come una banda che parte dal nero. RecolorDocument percorre l'intero albero delle funzioni in sola lettura prima e non converte nulla a meno che ogni nodo al suo interno possa essere convertito; solo dopo riscrive gli stop e rinomina lo spazio

Le funzioni che riscrive sono il tipo esponenziale, i cui due colori finali /C0 e /C1 sono semplici array, e le funzioni stitching costruite su di esse. Un colore /Background viene convertito con il resto. Uno shading definito da una tabella campionata o da un programma PostScript conserva la propria definizione, così come i tipi mesh da 4 a 7, che conservano i colori dentro uno stream invece che in una funzione. Riscrivere l'uno o l'altra è rigenerazione, un lavoro diverso dal convertire un colore

Diagramma PDF Library for Delphi della conversione delle sfumature che accoppia uno spazio colore shading con la sua funzione stop attraverso oggetti autonomi, pattern e risorse shading di pagina, mentre shading campionati, PostScript e mesh conservano la propria definizione
Convertire un gradiente significa spostare insieme il suo spazio colore e gli stop che lo compongono, ovunque risieda lo shading, mentre gli shade a mesh o a funzione campionata conservano la propria definizione

Trovare i gradienti è una trappola a sé. Gli strumenti di design raramente danno a uno shading un oggetto tutto suo; di solito siede inline dentro il dizionario di pattern che lo dipinge, o dentro le risorse /Shading di una pagina. Quando abbiamo costruito il passaggio sui gradienti, una scansione degli oggetti di primo livello non ne ha trovati quasi nessuno. La ricerca ora segue il grafo degli oggetti, limitata a dodici livelli di profondità e visitando ogni numero di oggetto una volta, così una risorsa condivisa da tutte le pagine viene convertita una volta sola e un file che punta un oggetto a sé stesso non può ciclare

Dove si colloca la ricolorizzazione prima di una spedizione alla stampa

Trattate RecolorDocument come normalizzazione. Converte valori tra spazi device; non applica un profilo ICC, non onora un rendering intent e non simula una condizione di stampa. Per un lavoro che deve combaciare con una prova contrattuale, il flusso guidato dal profilo è lo strumento giusto, e un passaggio di ricolorizzazione è al massimo il passo che porta tutto in un unico spazio prima che quel flusso giri

Dove guadagna il suo pane è il caso di produzione quotidiano: una spedizione che deve essere in grigio e arriva con un logo RGB, un report interno che non deve stampare a colori, un batch che un passo di preflight rifiuterà finché ogni file usa uno spazio unico. Qualsiasi cosa esca dall'edificio dovrebbe passare per RecolorDocument, mai per il solo RecolorPage, perché un file mezzo convertito passa un controllo visivo rapido e poi fallisce alla stampante. Fate girare il verdetto nello stesso job: le note su preflight PDF/A e PDF/UA e sulla workbench di conformità e firma mostrano come

// IsValidRecolorTarget è un helper che aggiungete voi; la libreria non ha una chiamata simile
function IsValidRecolorTarget(Space: Integer): Boolean;
begin
  Result := (Space >= 1) and (Space <= 3);
end;

procedure NormalizeFile(const Src, Dst: string; Space: Integer);
var
  Lib: TPDFlib;
begin
  if not IsValidRecolorTarget(Space) then
    raise EArgumentException.CreateFmt('Bad recolor target %d', [Space]);
  Lib := TPDFlib.Create;
  try
    if Lib.LoadFromFile(Src, '') <> 1 then
      raise Exception.CreateFmt('Could not open %s', [Src]);
    if Lib.RecolorDocument(Space) = 0 then
      WriteLn(Src, ': already in the target space, copied unchanged');
    Lib.SaveToFile(Dst);
  finally
    Lib.Free;
  end;
end;

Un controllo rapido da fare sull'output: aprite il contenuto di una pagina convertita in qualsiasi PDF inspector dopo una conversione in grigio. Il contenuto della pagina stessa non dovrebbe più contenere operatori rg, RG, k o K. Il colore che sopravvive al passaggio viene dai casi sopra: una JPEG, un colore taggato con profilo o spot, un gradiente campionato o mesh, o artwork disegnato dentro un form XObject, le cui istruzioni vivono in uno stream proprio anziché nel contenuto della pagina

Se state anche costruendo la grafica vettoriale anziché solo convertirla, la guida sulla grafica vettoriale, shader e gradienti mostra come shading e pattern vengono creati in partenza, il che rende le regole sui gradienti di sopra più facili da prevedere

Il passaggio sul colore gira in-process: PDF Library for Delphi è Pascal nativo per Delphi, C++Builder e Lazarus, quindi normalizzare un batch di file in arrivo non richiede convertitori esterni né strumenti a riga di comando sul server. La pagina prodotto PDF Library for Delphi elenca il resto della sua API di elaborazione dei documenti