Articolo tecnico

Proofing overprint CMYK e render device in HotPDF

Un box ciano sta su uno sfondo giallo, e lo stato grafico che lo dipinge porta /op true e /OPM 1. A schermo la sovrapposizione è ciano piatto. Sul foglio di stampa la stessa sovrapposizione viene fuori verde, perché il RIP ha lasciato stare la lastra gialla e ha posato il ciano sopra. Entrambi i risultati seguono il file alla lettera; il render a schermo semplicemente non ha mai chiesto cosa fa un device di imaging con quel flag, quindi un'approvazione data a schermo copre un colore che non arriva mai alla carta

Perché una pagina si stampa diversamente da come si vede in anteprima?

L'overprint è un'istruzione al device di output sulle lastre, e uno schermo RGB non ha lastre. ISO 32000-1 §8.6.7 definisce cosa succede quando la voce OP (stroke) o op (fill) di un ExtGState è true: i coloranti che la vernice corrente non nomina conservano ciò che era già sulla loro lastra, invece di essere cancellati a zero. Un oggetto ciano dipinto così non toglierebbe nulla dalla lastra gialla. Un viewer che compone in RGB non ha dove tenere il giallo, quindi dipinge il ciano sopra e va avanti

Nessuno dei due output è rotto. Il disaccordo è costruito dentro i due modelli di imaging, e resta invisibile finché qualcuno non tiene una prova stampata accanto al monitor. È tardi nel lavoro per un designer che ha impostato il testo nero in knockout per sbaglio, o che ha lasciato un logo bianco in overprint aspettandosi che si vedesse

Confronto overprint CMYK in HotPDF in cui un viewer ordinario fa il knockout del giallo sotto il ciano mentre l'anteprima di overprint prova entrambi gli inchiostri di processo e smaschera testo bianco overprint accidentale
Un viewer ordinario dipinge da solo l'inchiostro in cima mentre il proof di overprint compone entrambi gli inchiostri, smascherando trappole come testo bianco overprint per accidente

Attivare il proof con RenderOverprintPreview

HotPDF espone il proof come un Boolean sul documento, THotPDF.RenderOverprintPreview, che vale False per predefinito. Con esso impostato, il render bitmap tiene traccia delle lastre di processo sotto ogni vernice in overprint e applica le regole del §8.6.7 prima di scrivere il pixel, così le sovrapposizioni mostrano la combinazione di inchiostri che la stampa produrrebbe. Lasciare il predefinito spento è giusto per la visualizzazione ordinaria, perché i lettori di un PDF d'ufficio si aspettano che sembri come lo mostra il loro viewer abituale. Accendetelo nel passo il cui output va a un operatore di prestampa

var
  Pdf: THotPDF;
  Device: THPDFBitmapRenderDevice;
  Proof: TBitmap;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.LoadFromFile('cover-cmyk.pdf');
    Pdf.RenderOverprintPreview := True;    // prova le lastre, il predefinito è False
    Device := THPDFBitmapRenderDevice.Create;
    try
      if Pdf.RenderLoadedPageToDevice(0, 150, Device) then
      begin
        Proof := Device.TakeBitmap;        // il chiamante possiede Proof da qui
        try
          Image1.Picture.Assign(Proof);
        finally
          Proof.Free;
        end;
      end;
    finally
      Device.Free;
    end;
  finally
    Pdf.Free;
  end;
end;

Le regole delle lastre dipendono dallo spazio colore della vernice in overprint, e valgono la pena essere conosciute perché ognuna spiega una sorpresa diversa su una prova:

  • DeviceCMYK sotto /OPM 1: una componente a zero lascia la sua lastra intatta, una componente non nulla la sostituisce. Ciano 100 / giallo 0 su uno sfondo giallo conserva il giallo e diventa verde
  • DeviceGray: la vernice nomina solo la lastra nera, quindi ciano, magenta e giallo sotto sopravvivono. Un logo DeviceGray bianco impostato in overprint su un fondo CMY quindi sparisce
  • Separation e DeviceN: un colorante chiamato Cyan, Magenta, Yellow o Black sostituisce direttamente quella lastra di processo, /None non dipinge affatto, e /All scrive ogni lastra di processo
  • Coloranti spot: ogni altro nome è trattato come inchiostro spot; la sua tint transform dà un colore alternativo, che viene moltiplicato sulle lastre di processo sotto, così lo spot scurisce ciò che copre invece di sostituirlo

Il proof si applica ai path riempiti e tracciati e al testo, incluso il testo in modalità di render stroke, dove decide il flag stroke OP invece di op. Immagini, immagini inline e shading sh continuano per le loro pipeline colore raster complete e dipingono con la loro opacità ordinaria. In pratica copre ciò che i report di preflight segnalano più spesso: regole di overprint, keyline, testo e tinte piatte

La trasparenza resta viva mentre il proof è acceso. Un gruppo di trasparenza con knockout impostato fa comunque knockout, e il gruppo viene composto nello stesso passaggio delle regole di overprint, quindi un file pronto stampa con artwork trasparente sopra uno sfondo nero in overprint prova entrambi gli effetti insieme. Centrarne uno e sbagliare l'altro produrrebbe solo un proof sbagliato diverso

L'impostazione partecipa anche all'identità dei render in cache. RenderLoadedPageToBitmapCached chiava le sue voci su pagina, DPI e una variante costruita dalle impostazioni di render che includono RenderOverprintPreview, e il tier su disco usa la stessa variante. Un viewer che oscilla tra anteprima normale e proof prende due voci di cache separate e non ha mai bisogno di una chiamata a InvalidateRenderedPageCache solo perché il toggle è cambiato

La trappola /OPM 0 nei file DeviceCMYK

Ecco il caso che manda la gente a cercare un bug. Un file imposta /op true su un riempimento DeviceCMYK ciano ma lascia OPM al suo predefinito di 0, e poi il proof mostra comunque il ciano che fa knockout del giallo. Il proof è corretto. Sotto modalità overprint 0, il §8.6.7 dice che una vernice DeviceCMYK imposta tutti e quattro i coloranti di processo, zeri inclusi, quindi la componente gialla a zero sostituisce la lastra gialla esattamente come farebbe un knockout. La modalità overprint 0 protegge solo le lastre che la vernice non nomina, che per un colore CMYK a quattro componenti significa lastre spot e nient'altro

Sulle lastre di processo che un proof composito mostra, modalità 0 e un knockout semplice danno gli stessi pixel, quindi HotPDF dipinge quegli oggetti DeviceCMYK come knockout ordinari. Perché un oggetto CMYK faccia overprint degli altri inchiostri di processo, l'ExtGState che porta /op true vuole anche /OPM 1. Quando un proof e l'intenzione del designer sono in disaccordo su un oggetto DeviceCMYK, aprite quell'ExtGState e cercate la voce OPM prima di sospettare qualunque altra cosa

La sorpresa inversa esiste pure. Sotto /OPM 1, il bianco CMYK è quattro zeri, e quattro zeri in overprint lasciano ogni lastra com'era. Il testo bianco impostato in overprint sparisce sul foglio, e il proof lo mostra sparire, che è tutta la ragione per farne uno

Tre device dietro a RenderLoadedPageToDevice

THotPDF.RenderLoadedPageToDevice(PageIndex, DPI, Device) è un punto d'ingresso unico la cui destinazione è decisa dall'oggetto device che passate. Genera un'eccezione per un device nil, tratta un DPI sotto 1 come 150, e restituisce ciò che il device riporta dal suo metodo Execute. THPDFRenderDevice è la base astratta con due membri virtuali: Kind, che restituisce rdkBitmap, rdkDeviceContext o rdkEnhancedMetafile, e Execute(Document, PageIndex, DPI)

I tre device spediti possiedono il loro output in modi diversi:

  • THPDFBitmapRenderDevice tiene un TBitmap nella sua proprietà Bitmap e lo libera nel suo distruttore a meno che TakeBitmap ve lo abbia prima consegnato
  • THPDFDeviceContextRenderDevice.Create(ADC, AWidth, AHeight) disegna in un HDC che avete già e non possiede nulla; il costruttore genera un'eccezione su un DC a zero o una dimensione non positiva, e nessuno sfondo viene dipinto, quindi un DC di schermo vuole il suo riempimento bianco
  • THPDFMetafileRenderDevice tiene un TMetafile finché TakeMetafile non lo trasferisce. Testo e path vettoriali restano scalabili nel metafile enhanced, mentre gli image XObject arrivano rasterizzati
Architettura di render HotPDF che dirama una chiamata RenderLoadedPageToDevice verso device di render bitmap, device context di stampa e metafile enhanced con i loro valori Kind e regole di ownership
Una chiamata di rendering si dirama verso device bitmap, device context e metafile il cui Kind riporta la destinazione e i cui metodi Take consegnano l'ownership

L'ownership segue le regole Delphi ordinarie. Leggete la proprietà Bitmap o Metafile quando l'oggetto deve vivere solo quanto il device; chiamate il metodo Take quando deve sopravvivergli. Prendete la bitmap, liberate il device, dimenticate la bitmap, e il leak cresce col numero di pagine: invisibile su un campione di cinque pagine, ovvio su un batch da cinquecento. Date a ogni oggetto il suo try/finally e la domanda di chi libera cosa si risponde da sola

Comutate su Kind nel codice applicativo invece di testare is THPDFBitmapRenderDevice. Dato che Execute e Kind sono metodi virtuali pubblici, potete scrivere la vostra classe device, e il codice che controlla il kind continua a funzionare quando una delle vostre fa il verso a un device spedito

Perché un render su stampante atterra in un angolo?

Il percorso device context dimensiona la pagina dall'argomento DPI, non dalla larghezza e altezza che passate. Il renderer scala le unità PDF per DPI / 72 e usa l'altezza solo per ancorare l'origine pagina al fondo della superficie. Fissate 300 nel codice e mandate il job a una stampante da 600 dpi, e la pagina viene fuori a metà dimensione, spinta in basso a sinistra del foglio. La risoluzione della stampante è una proprietà del driver, quindi chiedetela al driver:

procedure PrintPage(Pdf: THotPDF; PageIndex: Integer);
var
  Device: THPDFDeviceContextRenderDevice;
  PrinterDPI, W, H, Tmp: Integer;
  L, B, R, T: Single;
begin
  if not Pdf.GetLoadedPageVisibleBox(PageIndex, L, B, R, T) then
    Exit;
  Printer.BeginDoc;
  try
    // chiedete al driver; 300, 600 e 1200 dpi sono tutti comuni
    PrinterDPI := GetDeviceCaps(Printer.Handle, LOGPIXELSX);
    W := Round((R - L) * PrinterDPI / 72);
    H := Round((T - B) * PrinterDPI / 72);
    if (Pdf.GetLoadedPageRotation(PageIndex) mod 180) = 90 then
    begin
      Tmp := W; W := H; H := Tmp;            // scambio per pagine 90 / 270
    end;
    Device := THPDFDeviceContextRenderDevice.Create(
      Printer.Canvas.Handle, W, H);
    try
      Pdf.RenderLoadedPageToDevice(PageIndex, PrinterDPI, Device);
    finally
      Device.Free;
    end;
  finally
    Printer.EndDoc;
  end;
end;

Larghezza e altezza arrivano dal box visibile della pagina, il CropBox ritagliato al MediaBox, alla risoluzione della stampante stessa, e il chiamante li scambia per le pagine ruotate prima di creare il device. Questo corrisponde a ciò che il renderer si aspetta quando ancora l'origine

Il proofing overprint non viaggia giù per questo percorso. I device device-context e metafile emettono chiamate GDI dritte nella destinazione, e le regole delle lastre vogliono i pixel già presenti sotto ogni vernice, cosa che solo il render bitmap ha. Per una prova su carta, renderizzate la bitmap del proof e stendetela sulla canvas della stampante. Un piccolo device custom tiene tutto dietro lo stesso punto d'ingresso:

type
  // un device vostro: bitmap del proof, poi stesa sulla stampante
  TPrinterProofDevice = class(THPDFRenderDevice)
  public
    function Kind: THPDFRenderDeviceKind; override;
    function Execute(Document: THotPDF; PageIndex, DPI: Integer): boolean;
      override;
  end;

function TPrinterProofDevice.Kind: THPDFRenderDeviceKind;
begin
  Result := rdkDeviceContext;
end;

function TPrinterProofDevice.Execute(Document: THotPDF;
  PageIndex, DPI: Integer): boolean;
var
  Proof: TBitmap;
  OldProof: Boolean;
begin
  OldProof := Document.RenderOverprintPreview;
  Document.RenderOverprintPreview := True;
  try
    Proof := Document.RenderLoadedPageToBitmap(PageIndex, DPI);
  finally
    Document.RenderOverprintPreview := OldProof;  // lascia l'impostazione del chiamante com'era
  end;
  Result := Proof <> nil;
  if Result then
  try
    Printer.Canvas.StretchDraw(
      Rect(0, 0, Printer.PageWidth, Printer.PageHeight), Proof);
  finally
    Proof.Free;
  end;
end;

Chiamatelo tra Printer.BeginDoc e Printer.EndDoc con un DPI che dia alla bitmap abbastanza pixel per il foglio, intorno a 200-300 per una prova in formato lettera. Il rettangolo di stretch sopra riempie l'area stampabile; conservateci il rapporto d'aspetto della pagina se pagina e carta differiscono nella forma

Dove il proofing sta accanto al preflight

Il proof risponde a una domanda: quali inchiostri sopravvivono dove gli oggetti si sovrappongono. Viene composto sulla stessa bitmap RGB a 24 bit di qualunque altro render, quindi i suoi colori mostrano la combinazione di inchiostri piuttosto che un colore di foglio misurato. Le condizioni di stampa sono una questione di color management, e il documento le gestisce tramite ConfigureRenderICCWorkflow, che prende un profilo proof, un profilo output e un flag di black point compensation, e ClearRenderICCWorkflow per toglierli di nuovo

Il posto più produttivo per un render di proof è accanto ai controlli che già eseguite. Un passaggio di preflight riporta che il testo nero è impostato in overprint; il render del proof mostra all'operatore cosa fa sulla pagina; entrambi atterrano nello stesso report. Il lavoro di packaging tende a mescolare overprint con inchiostri spot, e la guida di rendering di colori spot Separation e DeviceN copre il lato coloranti della stessa pagina, incluse le viste a lastra singola tramite RenderLoadedPageSeparationPlate. Gli articoli su renderizzare una pagina PDF in una bitmap e stampare un PDF caricato attraverso TPrinter coprono le due destinazioni device nella loro forma semplice

Tutto questo gira in codice Delphi e C++Builder nativo senza alcuna DLL di rendering da spedire accanto all'eseguibile. La pagina del componente Delphi HotPDF elenca le funzionalità di rendering e prestampa e ha una build di prova per eseguire questi esempi sui vostri file di stampa