In passato, un PDF la cui intestazione conteneva uno sfondo sfumato in stile Word o Excel, quando veniva renderizzato in un'immagine, mostrava un rettangolo bianco vuoto al posto della miscela di colori. Il renderer di pagine di HotPDF ora rispetta l'operatore PDF sh (shading) per i gradienti assiali di Tipo 2 e radiali di Tipo 3: valuta la funzione di sfumatura lungo l'asse del gradiente e disegna la miscela di colori, mentre le versioni precedenti scartavano silenziosamente l'operatore lasciando l'area del colore della carta (bianca). I due tipi di sfumatura che coprono quasi tutti i gradienti dei documenti d'ufficio sono supportati; i tipi mesh non lo sono, e questo articolo descrive chiaramente dove si colloca tale confine
Perché un gradiente PDF viene renderizzato come una casella bianca?
Un gradiente PDF è un costrutto vettoriale, non una bitmap che il renderer può trasferire direttamente. Un dizionario di sfumatura (shading dictionary) (ISO 32000-1 §8.7.4.5) descrive la geometria — un asse o una coppia di cerchi nell'array Coords — oltre a una funzione di colore, e l'operatore sh disegna tale sfumatura sull'attuale regione di ritaglio, lo stesso modello grafico che HotPDF espone quando disegni tracciati e riempimenti su un canvas di pagina. Il renderer di pagine di HotPDF percorre il content stream operatore per operatore, e qualsiasi operatore per cui non ha un percorso di codice viene saltato in modo che venga comunque eseguito il rendering del resto della pagina. Prima della versione 2.335.0, l'operatore sh faceva parte di questo insieme saltato: un gradiente di intestazione o a tutta pagina non forniva alcun contributo all'output, quindi lo sfondo appariva bianco. Il file non era mai corrotto. Il renderer semplicemente non disponeva di un gestore per quell'operatore, e una casella bianca è esattamente l'aspetto di un gestore mancante
La catena di valutazione della funzione dietro una sfumatura
Le sfumature assiali di Tipo 2 e radiali di Tipo 3 contengono entrambe una /Function che mappa una posizione parametrica lungo la sfumatura in un colore (ISO 32000-1 §7.10), e l'analizzatore di funzioni di HotPDF risolve il tipo FunctionType e lo gestisce. L'interpolazione esponenziale di Tipo 2 (§7.10.3) calcola C0 + t^N * (C1 - C0) con limitazioni di Domain e Range; le funzioni campionate di Tipo 0 (§7.10.2) leggono una griglia di campioni a 8, 16 o 32 bit e scelgono il più vicino tramite la mappa Encode, caso trattato in dettaglio nella nostra panoramica sulle ricerche di colori campionati di Tipo 0; le funzioni di unione di Tipo 3 (§7.10.4) selezionano una sottofunzione tramite l'array Bounds e vi ricorrono all'interno. Le funzioni di calcolo PostScript di Tipo 4 (§7.10.5) non vengono valutate — degradano in modo controllato a una valutazione fallita anziché a un colore errato, in linea con la politica conservativa adottata da HotPDF nel percorso di rendering. Questo valutatore è stato introdotto nella versione 2.334.0, una release prima dell'operatore stesso, poiché il disegnatore di sfumature è efficace solo quanto la funzione sottostante
In che modo HotPDF trasforma un gradiente continuo in pixel?
HotPDF approssima la miscela continua con una serie finita di bande di colore solido. Il disegnatore di sfumature percorre l'asse del gradiente, lo divide in un massimo di 64 bande — meno quando l'asse è corto nei pixel del dispositivo —, valuta la funzione nella posizione parametrica di ciascuna banda e riempie un rettangolo perpendicolare all'asse con un pennello solido. Il compromesso è tra il numero di bande e la fluidità: un numero maggiore di bande segue la miscela più da vicino ma richiede più riempimenti, e 64 è il limite massimo deliberato. Poiché il conteggio scala anche con la lunghezza dell'asse nei pixel del dispositivo, la risoluzione è importante. Eseguire il rendering della stessa pagina a un DPI più elevato allunga l'asse, quindi un gradiente corto guadagna bande e le sue strisce si ammorbidiscono fino a raggiungere il limite massimo
// Il discretizzatore di sfumature utilizza fino a 64 bande di colore lungo l'asse
// del gradiente, ma mai più di quanto sia lungo l'asse in pixel del dispositivo. Il rendering
// della stessa pagina a un DPI più elevato allunga l'asse, quindi un gradiente corto
// guadagna bande e l'effetto a bande si attenua fino a raggiungere il limite di 64 bande.
Thumb := Pdf.RenderLoadedPageToBitmap(PageIndex, 96); // bande più grossolane
Proof := Pdf.RenderLoadedPageToBitmap(PageIndex, 300); // fino a 64 bande
Rendering di una pagina sfumata in una bitmap
Il punto di ingresso pubblico è invariato: RenderLoadedPageToBitmap accetta un indice di pagina a base 0 e un valore DPI, restituisce una bitmap a 24 bit di proprietà del chiamante e restituisce nil in caso di errore anziché sollevare un'eccezione, in modo che il supporto delle sfumature sia trasparente per il codice esistente. Una pagina che in precedenza renderizzava vuota la sua intestazione sfumata ora restituisce la stessa bitmap con la sfumatura disegnata, senza richiedere modifiche alle API per ottenerla. L'unica cosa da ricordare è la proprietà: il chiamante libera la bitmap restituita. L'intera pipeline di rendering — interpretazione di content-stream, glifi incorporati e cache di pagina — è trattata nella guida al rendering di pagine PDF in bitmap in Delphi
var
Pdf: THotPDF;
Bmp: TBitmap;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('quarterly-report.pdf') > 0 then
begin
// Pagina 1 (a base 0) a 150 DPI. Un'intestazione con riempimento sfumato che
// in precedenza appariva bianca ora contiene la sua miscela di colori.
Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
if Assigned(Bmp) then
try
Bmp.SaveToFile('page1.bmp');
finally
Bmp.Free;
end;
end;
finally
Pdf.Free;
end;
end;
Dalla stessa chiamata deriva una semplice protezione contro le regressioni. Esegui il rendering della pagina, campiona un pixel dove si trova la banda del gradiente e conferma che non è più il bianco carta su cui il renderer era solito ripiegare — un controllo di una sola riga che rileva un ritorno silenzioso al vecchio comportamento privo di effetti
Bmp := Pdf.RenderLoadedPageToBitmap(0, 150);
if Assigned(Bmp) then
try
// True quando l'operatore sh ha disegnato; False se la regione è ripiegata sul bianco.
HeaderPainted := Bmp.Canvas.Pixels[Bmp.Width div 2, 8] <> clWhite;
finally
Bmp.Free;
end;
Cosa non disegna HotPDF: sfumature mesh e bordi di ritaglio
I quattro tipi di sfumatura mesh sono fuori portata, ed è bene saperlo prima di iniziare. I mesh a triangoli di Gouraud a forma libera (Tipo 4), i mesh di Gouraud a forma di reticolo (Tipo 5), i mesh a patch di Coons (Tipo 6) e i mesh a patch di prodotto tensoriale (Tipo 7), definiti in ISO 32000-1 da §8.7.4.5.5 a §8.7.4.5.8, non vengono renderizzati; una pagina che ne utilizza uno viene comunque renderizzata, ma tale regione viene lasciata inalterata. Vale la pena dichiarare chiaramente altri due limiti. I rettangoli di banda coprono la striscia della sfumatura anziché essere ritagliati esattamente sul tracciato di ritaglio corrente, quindi un gradiente mascherato in una forma arrotondata o irregolare viene disegnato come la sua striscia di delimitazione fino a quando non verrà introdotto un ritaglio preciso della regione in una fase successiva. HotPDF presuppone inoltre che l'output della funzione sia RGB o in scala di grigi, quindi una sfumatura creata in CMYK o in uno spazio colore insolito viene approssimata anziché gestita con gestione del colore. Per i documenti che la maggior parte dei team Delphi e C++Builder elabora effettivamente — relazioni, fatture, estratti conto e presentazioni esportate in PDF —, le sfumature assiali e radiali coprono essenzialmente ogni gradiente che si incontra, e le prove di progettazione ricche di mesh sono casi da trattare come anteprima piuttosto che come prova definitiva
Il renderer di pagine di HotPDF, il supporto dell'operatore sh descritto qui e il valutatore di funzioni di Tipo 0/2/3 sottostante sono forniti come parte di HotPDF Component per Delphi e C++Builder — una libreria VCL nativa senza dipendenze da DLL esterne, che copre creazione, modifica, estrazione del testo e rendering delle pagine in un unico pacchetto