Eine Überschrift, gefüllt mit 1 0 0 rg, reinem Rot, kommt aus einer Graustufen-Konvertierung als 0.299 g zurück. Reines Blau landet bei 0.114 g, nah genug an Schwarz, dass ein Marineblaues Logo und der Fließtext schwer auseinanderzuhalten sind. Diese Zahlen sind kein Rundungsunfall: Sie sind die BT.601-Luminanz-Gewichte, dieselben, die ein Viewer anwendet, wenn er eine RGB-Seite auf einem grauen Gerät zeigt, und sie entscheiden, wie viel Kontrast überlebt, wenn eine Farbbroschüre grau werden muss
Was RecolorDocument für jede Farbe berechnet
Die PDF Library for Delphi stellt die Konvertierung als RecolorDocument(TargetSpace) auf TPDFlib bereit, mit 1 für Graustufen, 2 für RGB und 3 für CMYK. Sie gibt zurück, wie viele Dinge sie umgewandelt hat, und Geometrie, Textpositionen und Bildplatzierung bleiben, wo sie waren. Nur die Farbwerte und die Farbraum-Namen um sie herum ändern sich
Jede Farbe geht auf dem Weg ins Ziel über RGB. Grau ist die Luminanz-Summe 0.299 R + 0.587 G + 0.114 B. CMYK erreicht RGB über die schlichte Geräte-Regel aus ISO 32000-1 §10.3, bei der jeder Kanal eins minus der Summe aus seiner Tinte und Schwarz ist, geklemmt auf null. RGB erreicht CMYK, indem die größte Komponente nach Schwarz gezogen wird: K = 1 - max(R, G, B), und die übrigen Inks werden aus dem Rest skaliert. Ein paar Ergebnisse, die man kennen sollte, bevor irgendetwas zu einer Druckerei geht:
- Ein Grau von
0.4 gwird zu CMYK konvertiert zu0 0 0 0.6 k, nur Schwarz-Tinte, was eine Druckmaschine für Text und Linien will - RGB
1 0.5 0(Orange) wird0 0.5 1 0 k, ganz ohne Schwarz - Ein Tiefschwarz wie
0.6 0.4 0.4 1 kwird auf dem Weg nach Grau zu0 g, die zusätzlichen Inks verschwinden also einfach - Reines Cyan,
1 0 0 0 k, wird zu0.701 g, heller, als die meisten von einer gesättigten Tinte erwarten
Konvertierte Operanden werden mit höchstens drei Dezimalstellen geschrieben und immer mit einem Punkt als Dezimaltrenner, ganz gleich, welche Locale die Maschine hat, auf der der Job läuft. ISO 32000-1 §7.3.3 erlaubt nichts anderes in einem Content-Stream, und ein Konverter, der Zahlen mit der System-Locale formatiert, schreibt auf einem deutschen Arbeitsplatz 0,5 g, was ein Reader als die Zahl 0 gefolgt von einem unbekannten Operator parst
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 = Grau, 2 = RGB, 3 = CMYK
WriteLn(Format('%d colors, images and gradients converted', [Converted]));
Lib.SaveToFile('brochure-gray.pdf');
finally
Lib.Free;
end;
end;
Eine Seite, deren Farben bereits im Zielraum liegen, behält ihre ursprünglichen Content-Bytes. RecolorDocument zählt nur Konvertierungen, die tatsächlich von einem Raum in einen anderen überqueren, ein bereits graues Dokument meldet also 0, und nichts darin wird neu geschrieben. Bei einer großen Datei zählt das: Ein Diff von Eingabe und Ausgabe zeigt genau die Seiten, die sich geändert haben
Warum lässt RecolorPage das Cover-Foto in Farbe?
RecolorPage konvertiert die Farboperatoren auf der gewählten Seite und stoppt dort. Bilder und Verläufe sind Objekte auf Dokumentebene, oft von mehreren Seiten geteilt, und nur RecolorDocument wandelt sie um. Der naheliegende Weg, nur ein Deckblatt zu konvertieren – Seite 1 wählen und RecolorPage(3) aufrufen –, liefert also CMYK-Überschriften und Linien über einem RGB-Foto
Diese Aufspaltung ist mit Absicht. Ein Bild-XObject, das auf dem Deckblatt gezeichnet wird, kann auch auf Seite 12 gezeichnet werden, es für eine Seite umzuwandeln wandelt es für beide um. Ein seitenbezogener Aufruf, der stillschweigend in gemeinsame Ressourcen greifen würde, änderte Seiten, die der Aufrufer nie gewählt hat. Das ist die schlimmere der beiden Überraschungen, also fasst der Pro-Seite-Aufruf nur das an, was der Seite gehört
// Operatoren nur auf dem Deckblatt; Bilder und Verläufe werden hier nicht umgewandelt
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');
// Für ein Dokument, das durchgehend ein Raum sein muss, alles umwandeln
Lib.RecolorDocument(3);
Lib.SaveToFile('all-cmyk.pdf');
Achten Sie in dem Snippet auf den Rückgabewert. Beide Aufrufe liefern 0 für ein Ziel außerhalb von 1 bis 3, und 0 ist auch die Antwort für eine Seite, die keine Änderung brauchte, ein Tippfehler im Ziel sieht also genau aus wie ein bereits korrektes Dokument. Prüfen Sie das Argument selbst vor dem Aufruf. RecolorDocument stellt die Seite, die vor seinem Lauf gewählt war, wieder her, man kann es also mitten in einem Seite-für-Seite-Job aufrufen, ohne danach neu zu wählen
Noch ein Umfangs-Detail. Der Operator-Durchlauf liest den eigenen Content-Stream jeder Seite, die Zeichnungsanweisungen, die auf der Seite selbst gelistet sind, und wandelt g, rg, k, ihre Strichformen und sc / scn um, wenn der aktuelle Raum ein Geräte-Raum ist. Wenn /DeviceRGB cs einem konvertierten sc vorangeht, wird der Raum-Name ebenfalls ins Ziel umgeschrieben. Lässt ein handgeschriebener Konverter diese Umbenennung aus, bleibt ein Reader damit zurück, drei RGB-Komponenten zu suchen, wo jetzt ein Grauwert sitzt
Bilder und Paletten: welche sich ändern, welche ihre Bytes behalten
Bilder sind so wichtig wie Operatoren. Eine zu Grau konvertierte Broschüre, bei der nur die Operatoren behandelt wurden, zeigt weiterhin Farbfotos auf grauen Seiten, was schlechter wirkt als das Original oder als eine vollständige Konvertierung. RecolorDocument wandelt ein Bild um, wenn es acht Bits pro Komponente trägt, einen Geräte-Farbraum hat und entweder keine Komprimierung oder schlichtes Flate ohne Decode-Parameter aufweist
Indizierte Bilder nehmen einen anderen Weg. Ihre Abtastungen sind Indizes in eine Farbtabelle, also wird die Tabelle umgewandelt und jede Abtastung behält den Index, den sie ohnehin hielt. Ein Screenshot oder Diagramm, als 256-Farb-Palettenbild gespeichert, konvertiert auf diese Weise unabhängig davon, wie seine Abtastungen gepackt oder komprimiert sind, denn die Abtastungen werden nie dekodiert. Die Basis der Palette muss ein Geräte-Raum sein; eine Palette über einem ICC-Profil behält ihre Tabelle
Ein JPEG- oder Fax-kodiertes Bild behält seine Bytes. Seine Farben zu ändern würde bedeuten, es zu dekodieren, umzuwandeln und neu zu komprimieren, was eine zweite Generation von JPEG-Artefakten hinzufügt und die Dateigröße in Weisen ändert, die niemand verlangt hat. Wenn ein Job wirklich jedes Foto in Grau braucht, ist Re-Kodieren eine separate Entscheidung mit eigener Qualitätseinstellung, und wir lassen lieber den Aufrufer sie explizit treffen, als dass eine Farbkonvertierung sie für ihn trifft
Soft-Masken und Schablonen-Masken bleiben aus einem anderen Grund, wie sie sind. Eine Soft-Maske ist meist ein DeviceGray-Bild, aber ihre Werte bedeuten Abdeckung. Wandelt man eine in dreikomponentiges RGB um, hört ein Viewer auf, sie als Transparenz zu behandeln; sie landet als graue Grafik gemalt über dem Inhalt, den sie formen sollte. RecolorDocument sammelt jedes Objekt, das über /SMask oder /Mask referenziert wird, bevor es irgendein Bild umwandelt, und ein Bild mit /ImageMask true hat gar keine Farbe, die sich umwandeln ließe
Farben, die auf Operatorebene über ICCBased-, Separation-, Indexed- oder Pattern-Räume gesetzt wurden, bleiben ebenfalls, wie sie geschrieben sind. Eine Spot-Farbe oder einen profilmarkierten Wert ohne das Lesen ihres Profils in Geräte-Grau zu überführen würde ändern, wie die Seite aussieht, und ein Durchlauf, dessen Job die Bewahrung ist, kann nicht anfangen, zu erraten, was ein Profil gemeint hat
Verläufe: Der Farbraum und die Stops wandern gemeinsam
Ein Shading deklariert seinen Farbraum einmal und übergibt jeden Stop an eine Funktion. Benennt man /ColorSpace von DeviceRGB in DeviceGray um, während die Funktion weiterhin drei Zahlen liefert, nimmt ein Reader die erste davon, den Rot-Kanal, als Graustufe. Ein Blau-Weiß-Verlauf rendert dann als ein Band, das bei Schwarz beginnt. RecolorDocument läuft zuerst den ganzen Funktionsbaum lesend durch und wandelt nichts um, es sei denn, jeder Knoten darin lässt sich umwandeln; erst dann schreibt es die Stops neu und benennt den Raum um
Die Funktionen, die es neu schreibt, sind der exponentielle Typ, dessen zwei Endfarben /C0 und /C1 schlichte Arrays sind, und Stitching-Funktionen, die daraus gebaut sind. Eine /Background-Farbe wird mit umgewandelt. Ein durch eine Sampled-Tabelle oder ein PostScript-Programm definiertes Shading behält seine Definition, ebenso die Mesh-Typen 4 bis 7, die ihre Farben in einem Stream statt in einer Funktion tragen. Beide neu zu schreiben ist Regeneration, ein anderer Job als eine Farbe zu konvertieren
Die Verläufe zu finden ist eine eigene Falle. Design-Werkzeuge geben einem Shading selten ein eigenes Objekt; es sitzt meist inline im Pattern-Dictionary, das es malt, oder in den /Shading-Ressourcen einer Seite. Als wir den Verlaufs-Durchlauf bauten, fand ein Scan der Top-Level-Objekte fast keine. Die Suche folgt jetzt dem Objektgraphen, begrenzt auf zwölf Ebenen Tiefe und mit jeder Objektnummer nur einmal, sodass eine von jeder Seite geteilte Ressource einmal umgewandelt wird und eine Datei, die ein Objekt auf sich selbst zeigen lässt, nicht in eine Schleife geraten kann
Wo Umfärben vor einer Druck-Einreichung hingehört
Behandeln Sie RecolorDocument als Normalisierung. Es konvertiert Werte zwischen Geräte-Räumen; es wendet kein ICC-Profil an, berücksichtigt keinen Rendering Intent und simuliert keine Druckmaschinen-Bedingung. Für einen Job, der einen verbindlichen Proof treffen muss, ist ein profilgetriebener Workflow das richtige Werkzeug, und ein Umfärbungs-Durchlauf ist höchstens der Schritt, der vorher alles in einen Raum bringt
Seinen Platz verdient es beim gewöhnlichen Produktionsproblem: einer Einreichung, die grau sein muss und mit einem RGB-Logo ankommt, einem internen Bericht, der nicht in Farbe drucken darf, einem Batch, den ein Preflight-Schritt zurückweist, bis jede Datei einen Raum verwendet. Alles, was das Haus verlässt, sollte durch RecolorDocument, nie durch RecolorPage allein, denn eine halb konvertierte Datei besteht eine schnelle Sichtprüfung und fällt dann beim Drucker auf. Lassen Sie das Urteil im selben Job laufen: Die Hinweise zu PDF/A- und PDF/UA-Preflight und zur Compliance- und Signatur-Workbench zeigen, wie
// IsValidRecolorTarget ist ein Helfer, den Sie selbst ergänzen; die Bibliothek hat keinen solchen Aufruf
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;
Eine schnelle Prüfung der Ausgabe: Öffnen Sie den Inhalt einer konvertierten Seite nach einer Graustufen-Konvertierung in einem beliebigen PDF-Inspektor. Der eigene Content der Seite sollte keine rg-, RG-, k- oder K-Operatoren mehr enthalten. Farbe, die den Durchlauf überlebt, kommt aus den Fällen oben: ein JPEG, eine profilmarkierte oder Spot-Farbe, ein Sampled- oder Mesh-Verlauf, oder Grafik, die in einem Form-XObject gezeichnet wurde, deren Anweisungen in einem eigenen Stream leben statt im Seiteninhalt
Wenn Sie die Vektor-Grafik auch selbst aufbauen statt nur zu konvertieren, zeigt der Walkthrough zu Vektor-Grafik, Shadern und Verläufen, wie Shadings und Patterns überhaupt erst erzeugt werden, was die Verlaufs-Regeln oben leichter vorhersehbar macht
Der Farb-Durchlauf läuft im Prozess: Die PDF Library for Delphi ist natives Pascal für Delphi, C++Builder und Lazarus, eine Batch eingehender Dateien zu normalisieren braucht also keinen externen Konverter oder Befehlszeilen-Werkzeug auf dem Server. Die PDF Library for Delphi-Produktseite listet den Rest der Dokument-Verarbeitungs-API