Eine PDF-Datei, deren Header einen Farbverlauf im Word- oder Excel-Stil als Hintergrund hat, wurde beim Rendern in ein Bild früher mit einem leeren weißen Rechteck an der Stelle der Farbmischung ausgegeben. Der Seiten-Renderer von HotPDF unterstützt nun den PDF-Operator sh (Shading) für axiale Schattierungen (Typ 2) und radiale Schattierungen (Typ 3): Er wertet die Funktion der Schattierung entlang der Verlaufsachse aus und zeichnet den Verlauf dort, wo frühere Versionen den Operator stillschweigend übersprangen und den Bereich papierweiß ließen. Die beiden Schattierungstypen, die fast jeden Verlauf in Office-Dokumenten abdecken, sind enthalten; die Mesh-Typen (Netzschattierungen) dagegen nicht, und dieser Artikel zeigt offen, wo diese Grenze verläuft
Warum wird ein PDF-Farbverlauf als weißes Feld gerendert?
Ein PDF-Verlauf ist ein Vektorkonstrukt und kein Bitmap, das der Renderer einfach kopieren kann. Ein Schattierungsverzeichnis (ISO 32000-1 §8.7.4.5) beschreibt die Geometrie — eine Achse oder ein Kreisepaar im Array Coords — sowie eine Farbfunktion, und der Operator sh zeichnet diese Schattierung über den aktuellen Schnittbereich (Clip Region). Das ist derselbe Graphen-Aufbau, den HotPDF bereitstellt, wenn Sie Pfade und Füllungen auf ein Seiten-Canvas zeichnen. Der Seiten-Renderer von HotPDF durchläuft den Content-Stream Operator für Operator, und jeder Operator, für den kein Codepfad vorhanden ist, wird übersprungen, damit der Rest der Seite dennoch gerendert wird. Vor der Version 2.335.0 gehörte sh zu dieser übersprungenen Gruppe: Ein Verlauf im Header oder auf der gesamten Seite trug nichts zur Ausgabe bei, sodass der Hintergrund einfach weiß blieb. Die Datei war niemals beschädigt. Dem Renderer fehlte lediglich ein Handler für diesen einen Operator, und ein weißes Feld ist genau das, wie sich ein fehlender Handler äußert
Die Funktionsauswertungskette hinter einer Schattierung
Eine Schattierung kann erst gezeichnet werden, wenn ihre Funktion ausgewertet wurde, weshalb diese Maschinerie zuerst implementiert werden musste. Axiale Schattierungen vom Typ 2 und radiale Schattierungen vom Typ 3 enthalten beide eine /Function, die eine parametrische Position entlang des Verlaufs auf eine Farbe abbildet (ISO 32000-1 §7.10), und der Funktionsauswerter von HotPDF ermittelt den FunctionType und führt die passende Operation aus. Die exponentielle Interpolation vom Typ 2 (§7.10.3) berechnet C0 + t^N * (C1 - C0) mit Domain- und Range-Begrenzung. Interpolierte Funktionen vom Typ 0 (§7.10.2) lesen ein Gitter aus 8-, 16- oder 32-Bit-Werten aus und wählen den nächstliegenden über das Encode-Verzeichnis. Dieser Fall wird ausführlich in unserer Anleitung zur Typ-0-Farbtabellensuche behandelt. Typ-3-Verknüpfungsfunktionen (§7.10.4) wählen eine Unterfunktion über das Bounds-Array aus und rufen diese rekursiv auf. Typ-4-PostScript-Berechnungsfunktionen (§7.10.5) werden nicht ausgewertet; sie stufen sich im Fehlerfall sauber zu einer fehlgeschlagenen Auswertung statt einer falschen Farbe herab, was der konservativen Strategie von HotPDF im gesamten Renderpfad entspricht. Dieser Auswerter wurde in Version 2.334.0 ausgeliefert, also eine Version vor dem Operator selbst, da der Schattierungszeichner nur so gut sein kann wie die darunter liegende Funktion
Wie verwandelt HotPDF einen kontinuierlichen Verlauf in Pixel?
HotPDF nähert den kontinuierlichen Verlauf durch eine endliche Reihe von einfarbigen Bändern an. Der Schattierungszeichner durchläuft die Verlaufsachse, teilt sie in bis zu 64 Bänder auf — weniger, wenn die Achse in Gerätepixeln kurz ist —, wertet die Funktion an der parametrischen Position jedes Bandes aus und füllt ein Rechteck senkrecht zur Achse mit einem einfarbigen Pinsel. Der Kompromiss liegt zwischen der Anzahl der Bänder und der Weichheit des Verlaufs: Mehr Bänder folgen dem Verlauf genauer, erfordern jedoch mehr Fülloperationen, und 64 Bänder sind die bewusste Obergrenze. Da die Anzahl auch mit der Achsenlänge in Gerätepixeln skaliert, spielt die Auflösung eine Rolle. Das Rendern derselben Seite mit einer höheren DPI-Zahl verlängert die Achse, sodass ein kurzer Verlauf an Bändern gewinnt und seine Streifen weicher werden, bis die Obergrenze erreicht ist
// The shading discretizer uses up to 64 color bands along the gradient
// axis, but never more than the axis is long in device pixels. Rendering
// the same page at a higher DPI lengthens that axis, so a short gradient
// gains bands and the banding softens until the 64-band ceiling is hit.
Thumb := Pdf.RenderLoadedPageToBitmap(PageIndex, 96); // coarser bands
Proof := Pdf.RenderLoadedPageToBitmap(PageIndex, 300); // up to 64 bands
Rendern einer schattierten Seite in eine Bitmap
Der öffentliche Einstiegspunkt bleibt unverändert: RenderLoadedPageToBitmap erwartet einen nullbasierten Seitenindex und einen DPI-Wert, gibt eine dem Aufrufer gehörende 24-Bit-Bitmap zurück und liefert bei Fehlern nil zurück, statt eine Exception auszulösen. Die Schattierungsunterstützung verhält sich also transparent zu Ihrem bestehenden Code. Eine Seite, deren Verlauf-Header früher weiß gerendert wurde, liefert nun dieselbe Bitmap mit gezeichnetem Farbverlauf zurück, ohne dass eine API-Änderung erforderlich ist. Das Einzige, woran man denken sollte, ist das Besitzverhältnis: Der Aufrufer gibt die zurückgelieferte Bitmap frei. Die gesamte Rendering-Pipeline — Content-Stream-Interpretation, eingebettete Glyphen und Seiten-Caching — wird im Leitfaden zum Rendern von PDF-Seiten in eine Bitmap in Delphi behandelt
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('quarterly-report.pdf') > 0 then
begin
// Page 1 (zero-based) at 150 DPI. A gradient-filled header that
// used to come back blank now carries its color blend.
Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
if Assigned(Bmp) then
try
Bmp.SaveToFile('page1.bmp');
finally
Bmp.Free;
end;
end;
finally
Pdf.Free;
end;
end;
Ein einfacher Regressionstest lässt sich leicht aus demselben Aufruf ableiten. Rendern Sie die Seite, nehmen Sie eine Pixelprobe dort, wo das Verlaufsband liegen sollte, und prüfen Sie, ob es sich nicht mehr um das Weiß des Papiers handelt, auf das der Renderer früher zurückgegriffen hat — eine einzeilige Prüfung, die eine stumme Rückkehr zum alten leeren Verhalten verhindert:
Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
if Assigned(Bmp) then
try
// True when the sh operator painted; False if the region fell back to white.
HeaderPainted := Bmp.Canvas.Pixels[Bmp.Width div 2, 8] <> clWhite;
finally
Bmp.Free;
end;
Was HotPDF nicht zeichnet: Mesh-Schattierungen und Schnittkanten
Die vier Mesh-Schattierungstypen liegen außerhalb des Leistungsumfangs, und das sollte man bei der Dokumentenverarbeitung im Hinterkopf behalten. Gouraud-Dreiecksnetze in freier Form (Typ 4), Gouraud-Netze in Gitterform (Typ 5), Coons-Netze (Typ 6) und Tensorprodukt-Netze (Typ 7), definiert in ISO 32000-1 §8.7.4.5.5 bis §8.7.4.5.8, werden nicht gerendert. Eine Seite, die diese nutzt, wird dennoch gerendert, nur bleibt der entsprechende Bereich unverändert. Zwei weitere Punkte sollten offen genannt werden: Die Bandrechtecke decken den Verlaufsstreifen ab und werden nicht exakt auf den aktuellen Schnittpfad (Clip Path) zugeschnitten. Daher wird ein Farbverlauf, der in eine abgerundete oder unregelmäßige Form maskiert ist, als sein umschreibendes Rechteck gezeichnet, bis in einer späteren Version ein präzises Bereichsschneiden implementiert wird. HotPDF geht zudem davon aus, dass die Funktionsausgabe in RGB oder Graustufen vorliegt. Eine in CMYK oder einem exotischen Farbraum definierte Schattierung wird daher angenähert statt farbverwaltet gezeichnet. Für die Dokumente, die die meisten Delphi- und C++Builder-Entwickler tatsächlich verarbeiten — Berichte, Rechnungen, Abrechnungen und als PDF exportierte Präsentationen —, decken axiale und radiale Verläufe fast alles ab, was Ihnen begegnen wird. Mesh-lastige Designentwürfe sollten eher als Vorschau und nicht als farbverbindlicher Proof behandelt werden
Der Seiten-Renderer von HotPDF, die hier beschriebene Unterstützung des Operators sh und der darunter liegende Funktionsauswerter für die Typen 0/2/3 werden als Teil der HotPDF-Komponente für Delphi und C++Builder ausgeliefert — eine native VCL-Bibliothek ohne externe DLL-Abhängigkeiten, die PDF-Erstellung, -Bearbeitung, -Textextraktion und das Rendern von Seiten in einem Paket vereint