Technischer Artikel

CMYK-Überdruck-Proofing und Render-Geräte in HotPDF

Eine Cyan-Box sitzt auf gelbem Hintergrund, und der Grafikzustand, der sie malt, trägt /op true und /OPM 1. Auf dem Bildschirm ist die Überlappung schlichtes Cyan. Auf dem Druckbogen kommt dieselbe Überlappung grün heraus, denn der RIP hat die gelbe Platte in Ruhe gelassen und Cyan obendrauf gelegt. Beide Ergebnisse folgen der Datei auf den Buchstaben genau; das Screen-Rendering hat nur nie gefragt, was ein Imaging-Gerät mit diesem Flag tut, also deckt eine auf dem Bildschirm erteilte Freigabe eine Farbe ab, die nie das Papier erreicht

Warum druckt eine Seite anders, als sie in der Vorschau erscheint?

Overprint ist eine Anweisung an das Ausgabegerät über Druckplatten, und ein RGB-Bildschirm hat keine Platten. ISO 32000-1 §8.6.7 definiert, was passiert, wenn der OP-Eintrag (Stroke) oder op-Eintrag (Fill) eines ExtGState auf true steht: Die Farbstoffe, die der aktuelle Farbauftrag nicht nennt, behalten, was bereits auf ihrer Platte liegt, statt auf null radiert zu werden. Ein so gemaltes Cyan-Objekt nimmt der gelben Platte nichts weg. Ein Viewer, der in RGB compositet, hat nirgendwo, um das Gelb zu behalten, also malt er Cyan obendrauf und macht weiter

Keine der beiden Ausgaben ist kaputt. Die Diskrepanz steckt in den beiden Imaging-Modellen, und sie bleibt unsichtbar, bis jemand einen gedruckten Proof neben den Monitor hält. Das ist spät im Job für einen Designer, der schwarzen Text versehentlich auf Knockout stellte, oder der ein weißes Logo auf Overprint ließ und erwartete, dass es erscheint

CMYK-Overprint-Vergleich in HotPDF: ein schlichter Viewer stanzt das Gelb unter Cyan aus, während die Overprint-Vorschau beide Prozessfarben prüft und versehentlich überdruckten weißen Text entlarvt
Ein schlichter Viewer malt nur die oberste Farbe, während der Overprint-Proof beide Farben zusammensetzt und Fallen wie versehentlich überdruckene weiße Schrift entlarvt

Den Proof mit RenderOverprintPreview einschalten

HotPDF legt den Proof als ein einziges Boolean am Dokument offen, THotPDF.RenderOverprintPreview, das auf False voreingestellt ist. Gesetztermaßen verfolgt das Bitmap-Rendering die Prozessplatten unter jedem überdruckenden Farbauftrag und wendet die §8.6.7-Regeln an, bevor es das Pixel schreibt, also zeigen Überlappungen die Tintenkombination, die die Druckmaschine erzeugen würde. Den Standard ausgeschaltet zu lassen ist für gewöhnliches Betrachten richtig, denn Leser eines Office-PDFs erwarten, dass es so aussieht, wie ihr gewohnter Viewer es zeigt. In dem Schritt einschalten, dessen Ausgabe zu einem Prepress-Betreiber geht

var
  Pdf: THotPDF;
  Device: THPDFBitmapRenderDevice;
  Proof: TBitmap;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.LoadFromFile('cover-cmyk.pdf');
    Pdf.RenderOverprintPreview := True;    // die Platten proofen, Standard ist False
    Device := THPDFBitmapRenderDevice.Create;
    try
      if Pdf.RenderLoadedPageToDevice(0, 150, Device) then
      begin
        Proof := Device.TakeBitmap;        // der Aufrufer besitzt Proof ab hier
        try
          Image1.Picture.Assign(Proof);
        finally
          Proof.Free;
        end;
      end;
    finally
      Device.Free;
    end;
  finally
    Pdf.Free;
  end;
end;

Die Platten-Regeln hängen vom Farbraum des überdruckenden Farbauftrags ab, und sie zu kennen lohnt sich, denn jede erklärt eine andere Überraschung auf einem Proof:

  • DeviceCMYK unter /OPM 1: eine Komponente null lässt ihre Platte unangetastet, eine Komponente ungleich null ersetzt sie. Cyan 100 / Gelb 0 über gelbem Hintergrund behält das Gelb und wird grün
  • DeviceGray: Der Farbauftrag nennt nur die schwarze Platte, also überleben Cyan, Magenta und Gelb darunter. Ein weißes DeviceGray-Logo auf Overprint gestellt verschwindet daher auf einem CMY-Hintergrund
  • Separation und DeviceN: Ein Farbstoff namens Cyan, Magenta, Yellow oder Black ersetzt diese Prozessplatte direkt, /None malt überhaupt nichts, und /All schreibt jede Prozessplatte
  • Spot-Farbstoffe: Jeder andere Name wird als Spot-Tinte behandelt; seine Tint-Transform liefert eine Alternativfarbe, die über die darunterliegenden Prozessplatten multipliziert wird, also verdunkelt der Spot, was er deckt, statt es zu ersetzen

Der Proof gilt für gefüllte und gestrichene Pfade und für Text, eingeschlossen Text im Stroke-Render-Modus, wo das Stroke-Flag OP statt op entscheidet. Bilder, Inline-Bilder und sh-Shadings laufen weiterhin durch ihre vollen Raster-Farb-Pipelines und malen mit ihrer gewöhnlichen Deckkraft. In der Praxis deckt das, was Preflight-Reports am meisten markieren: Overprint-Regeln, Keylines, Schriften und flache Tints

Transparenz bleibt live, während der Proof an ist. Eine Transparenzgruppe mit gesetztem Knockout stanzt weiterhin aus, und die Gruppe wird im selben Durchgang wie die Overprint-Regeln composited, also proofed eine druckfertige Datei mit transparenter Grafik über einem überdruckenden schwarzen Hintergrund beide Effekte zusammen. Einen richtig und den anderen falsch zu bekommen ergäbe nur einen anderen falschen Proof

Die Einstellung nimmt auch an der Identität gecachter Renderings teil. RenderLoadedPageToBitmapCached keyed seine Einträge auf Seite, DPI und eine Variante, gebaut aus Render-Einstellungen, die RenderOverprintPreview einschließen, und die Platten-Stufe verwendet dieselbe Variante. Ein Viewer, der zwischen normaler und Proof-Vorschau umschaltet, bekommt zwei getrennte Cache-Einträge und braucht nie einen Aufruf von InvalidateRenderedPageCache nur, weil der Schalter wechselte

Die /OPM-0-Falle in DeviceCMYK-Dateien

Hier ist der Fall, der Leute nach einem Bug suchen lässt. Eine Datei setzt /op true an einer DeviceCMYK-Cyan-Füllung, lässt aber OPM auf seinem Standard 0, dann zeigt der Proof weiterhin Cyan, das das Gelb ausstanzt. Der Proof ist korrekt. Unter Overprint-Modus 0 sagt §8.6.7, dass ein DeviceCMYK-Farbauftrag alle vier Prozessfarbstoffe setzt, Nullen eingeschlossen, also ersetzt die Nulldkomponente Gelb die gelbe Platte exakt so, wie ein Knockout es täte. Overprint-Modus 0 schützt nur Platten, die der Farbauftrag nicht nennt, was bei einer Vier-Komponenten-CMYK-Farbe Spot-Platten bedeutet und nichts sonst

Auf den Prozessplatten, die ein Composite-Proof zeigt, liefern Modus 0 und ein schlichter Knockout dieselben Pixel, also malt HotPDF diese DeviceCMYK-Objekte als gewöhnliche Knockouts. Damit ein CMYK-Objekt andere Prozessfarben überdruckt, braucht der ExtGState, der /op true trägt, zusätzlich /OPM 1. Wenn Proof und Design-Intent bei einem DeviceCMYK-Objekt auseinanderlaufen, diesen ExtGState öffnen und nach dem OPM-Eintrag suchen, bevor man irgendetwas anderes verdächtigt

Die umgekehrte Überraschung existiert ebenfalls. Unter /OPM 1 ist CMYK-Weiß ein Vierfach-Null, und vier Nullen unter Overprint lassen jede Platte, wie sie ist. Weißer Schriftzug auf Overprint gestellt verschwindet auf dem Bogen, und der Proof zeigt sein Verschwinden, was der ganze Grund ist, einen zu fahren

Drei Geräte hinter RenderLoadedPageToDevice

THotPDF.RenderLoadedPageToDevice(PageIndex, DPI, Device) ist ein einziger Einsprungpunkt, dessen Ziel das Geräteobjekt entscheidet, das man übergibt. Er löst für ein nil-Gerät eine Exception aus, behandelt einen DPI unter 1 als 150 und gibt zurück, was das Gerät aus seiner Execute-Methode meldet. THPDFRenderDevice ist die abstrakte Basis mit zwei virtuellen Membern: Kind, das rdkBitmap, rdkDeviceContext oder rdkEnhancedMetafile zurückgibt, und Execute(Document, PageIndex, DPI)

Die drei mitgelieferten Geräte besitzen ihre Ausgabe auf verschiedene Weise:

  • THPDFBitmapRenderDevice hält ein TBitmap in seiner Bitmap-Property und gibt es in seinem Destruktor frei, außer TakeBitmap hat es Ihnen zuerst übergeben
  • THPDFDeviceContextRenderDevice.Create(ADC, AWidth, AHeight) zeichnet in ein HDC, das Sie bereits haben, und besitzt nichts; der Konstruktor löst bei einem Null-DC oder einer nicht-positiven Größe aus, und kein Hintergrund wird gemalt, also braucht ein Screen-DC seine eigene weiße Füllung
  • THPDFMetafileRenderDevice hält ein TMetafile, bis TakeMetafile es überträgt. Text und Vektorpfade bleiben im Enhanced Metafile skalierbar, während Bild-XObjects rasterisiert ankommen
HotPDF-Renderarchitektur: ein RenderLoadedPageToDevice-Aufruf fächert zu Bitmap-, Drucker-Device-Context- und Enhanced-Metafile-Rendergeräten auf, mit ihren Kind-Werten und Eigentumsregeln
Ein einziger Rendering-Aufruf verzweigt zu Bitmap-, Device-Context- und Metafile-Geräten, deren Kind das Ziel meldet und deren Take-Methoden das Eigentum übergeben

Eigentum folgt schlichten Delphi-Regeln. Die Bitmap- oder Metafile-Property lesen, wenn das Objekt nur so lange leben muss wie das Gerät; die Take-Methode aufrufen, wenn es länger leben muss. Das Bitmap nehmen, das Gerät freigeben, das Bitmap vergessen, und das Leak wächst mit der Seitenzahl: unsichtbar an einer Fünf-Seiten-Probe, offensichtlich an einem Fünf-Hundert-Seiten-Batch. Jedem Objekt sein eigenes try/finally geben, und die Frage, wer was freigibt, beantwortet sich selbst

In Anwendungs-Code auf Kind dispatchen, statt is THPDFBitmapRenderDevice zu testen. Da Execute und Kind öffentliche virtuelle Methoden sind, kann man seine eigene Geräteklasse schreiben, und Code, der die Art prüft, funktioniert weiter, wenn eines von Ihren für ein mitgeliefertes Gerät einspringt

Warum landet ein Drucker-Rendering in einer Ecke?

Der Device-Context-Pfad nimmt die Seitengröße aus dem DPI-Argument, nicht aus der Breite und Höhe, die man übergibt. Der Renderer skaliert PDF-Einheiten mit DPI / 72 und verwendet die Höhe nur, um den Seiten-Ursprung am unteren Rand der Oberfläche zu verankern. 300 fest verdrahtet und den Job an einen 600-dpi-Drucker geschickt, kommt die Seite auf halber Größe heraus, geschoben in die linke untere Ecke des Bogens. Die Drucker-Auflösung ist eine Eigenschaft des Treibers, also beim Treiber nachfragen:

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
    // beim Treiber nachfragen; 300, 600 und 1200 dpi sind alle üblich
    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;            // tauschen für 90-/270-Grad-Seiten
    end;
    Device := THPDFDeviceContextRenderDevice.Create(
      Printer.Canvas.Handle, W, H);
    try
      Pdf.RenderLoadedPageToDevice(PageIndex, PrinterDPI, Device);
    finally
      Device.Free;
    end;
  finally
    Printer.EndDoc;
  end;
end;

Breite und Höhe kommen aus der sichtbaren Box der Seite, der CropBox, begrenzt auf die MediaBox, in der eigenen Auflösung des Druckers, und der Aufrufer tauscht sie für gedrehte Seiten, bevor er das Gerät erzeugt. Das entspricht dem, was der Renderer erwartet, wenn er den Ursprung verankert

Overprint-Proofing reist nicht diesen Weg entlang. Die Device-Context- und Metafile-Geräte geben GDI-Aufrufe direkt ins Ziel aus, und die Platten-Regeln brauchen die Pixel, die bereits unter jedem Farbauftrag liegen, was nur das Bitmap-Rendering hat. Für einen Papier-Proof das Proof-Bitmap rendern und auf den Drucker-Canvas gestreckt ausgeben. Ein kleines eigenes Gerät hält das hinter demselben Einsprungpunkt:

type
  // ein eigenes Gerät: Proof-Bitmap, dann gestreckt auf den Drucker
  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;  // die Einstellung des Aufrufers unverändert lassen
  end;
  Result := Proof <> nil;
  if Result then
  try
    Printer.Canvas.StretchDraw(
      Rect(0, 0, Printer.PageWidth, Printer.PageHeight), Proof);
  finally
    Proof.Free;
  end;
end;

Zwischen Printer.BeginDoc und Printer.EndDoc aufrufen, mit einem DPI, der dem Bitmap genug Pixel für den Bogen gibt, etwa 200 bis 300 für einen Proof in Letter-Größe. Das Streckungs-Rechteck oben füllt den bedruckbaren Bereich; behalten Sie das Seitenverhältnis der Seite darin bei, wenn sich Seite und Papier in der Form unterscheiden

Wo Proofing neben dem Preflight passt

Der Proof beantwortet eine Frage: Welche Tinten überleben, wo sich Objekte überlappen. Er wird auf dasselbe 24-Bit-RGB-Bitmap composited wie jedes andere Rendering, also zeigen seine Farben die Tintenkombination statt einer gemessenen Bogenfarbe. Druckbedingungen sind eine Farbmanagement-Frage, und das Dokument behandelt sie über ConfigureRenderICCWorkflow, das ein Proof-Profil, ein Ausgabeprofil und ein Black-Point-Compensation-Flag nimmt, und ClearRenderICCWorkflow, um sie wieder fallen zu lassen

Der ergiebigste Platz für ein Proof-Rendering ist neben den Prüfungen, die man ohnehin fährt. Ein Preflight-Durchgang meldet, dass schwarzer Text auf Overprint steht; das Proof-Rendering zeigt dem Operator, was das auf der Seite tut; beides landet im selben Report. Verpackungsarbeit mischt Overprint gern mit Spot-Tinten, und der Durchgang durch Separation- und DeviceN-Spot-Farb-Rendering deckt die Farbstoff-Seite derselben Seite, eingeschlossen Einzelplatten-Sichten über RenderLoadedPageSeparationPlate. Die Artikel zu eine PDF-Seite in ein Bitmap rendern und ein geladenes PDF über TPrinter drucken decken die beiden Geräte-Ziele in ihrer schlichten Form

Das alles läuft in nativem Delphi- und C++Builder-Code ohne Rendering-DLL, die neben der ausführbaren Datei mitgeliefert werden müsste. Die HotPDF-Delphi-Komponentenseite listet die Rendering- und Prepress-Features und hat einen Trial-Build, um diese Beispiele gegen Ihre eigenen Druckdateien zu fahren