HotPDF disegna grafica vettoriale creando un tracciato sulla pagina corrente e quindi richiedendone il disegno. Non c'è alcun passaggio bitmap intermedio. Una linea disegnata con MoveTo e LineTo si traduce in operatori di tracciato PDF nel flusso dei contenuti, rimanendo così un vero vettore: nitido al 50% di zoom, nitido al 1600% e a una frazione delle dimensioni che costerebbe una versione rasterizzata. Per diagrammi, regole di tabelle, assi di grafici e decorazioni di moduli, questo è esattamente ciò che si desidera, e l'API sottostante è abbastanza semplice da essere appresa in una sola sessione.
L'intera superficie di disegno risiede su THotPDF.CurrentPage. Tra BeginDoc e EndDoc si impostano il colore e lo spessore della linea su quell'oggetto pagina, si definisce la geometria e si chiama un operatore di disegno per applicarla. Le quattro primitive più utilizzate sono MoveTo e LineTo per tracciati arbitrari, Rectangle per i riquadri, Circle per i cerchi e i due operatori di disegno Stroke e Fill.
Il sistema di coordinate è in basso a sinistra
Questo è l'unico aspetto che mette in difficoltà chiunque provenga dalla VCL. Il TCanvas con cui si dipingono i controlli posiziona l'origine nell'angolo in alto a sinistra con la Y che cresce verso il basso. Il PDF fa il contrario. HotPDF misura a partire dall'angolo in basso a sinistra della pagina in punti (1/72 di pollice), con la Y che aumenta man mano che ci si sposta verso l'alto. Un punto a Y := 720 si trova vicino alla parte superiore di una pagina US Letter, che è alta 792 punti, mentre Y := 50 si trova vicino alla parte inferiore. Se il primo disegno risulta specchiato verticalmente, il motivo è questo: il codice convertito dalla grafica dello schermo presuppone la direzione errata e finisce oltre il bordo inferiore.
La stessa convenzione regola TextOut, in modo che testo e forme condividano un unico modello mentale una volta interiorizzato. Pianificate un layout decidendo dove posizionare la parte inferiore di ciascun elemento, non quella superiore, e il resto verrà da sé.
Tracciati: MoveTo, LineTo, Stroke
Un tracciato disegnato (stroked) equivale a una penna che viene sollevata, posizionata e trascinata. MoveTo solleva la penna e imposta il punto iniziale senza tracciare nulla. Ogni LineTo estende il tracciato corrente verso un nuovo punto. Nulla appare sulla pagina finché non si chiama Stroke, che disegna il tracciato accumulato utilizzando il colore di tratto e lo spessore di linea correnti, quindi cancella il tracciato in modo che il successivo MoveTo inizi da zero.
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'DrawPaths.pdf';
Pdf.BeginDoc;
// Line width is in points and applies until you change it.
Pdf.CurrentPage.SetLineWidth(1.5);
Pdf.CurrentPage.SetRGBStrokeColor(clBlack);
// A horizontal rule near the top of the page (Y measured from bottom).
Pdf.CurrentPage.MoveTo(72, 720);
Pdf.CurrentPage.LineTo(523, 720);
Pdf.CurrentPage.Stroke; // commit the path; nothing drew before this
// A thicker connected polyline: three segments in one path.
Pdf.CurrentPage.SetLineWidth(3);
Pdf.CurrentPage.SetRGBStrokeColor(RGB(30, 90, 200));
Pdf.CurrentPage.MoveTo(72, 640);
Pdf.CurrentPage.LineTo(172, 690);
Pdf.CurrentPage.LineTo(272, 620);
Pdf.CurrentPage.LineTo(372, 680);
Pdf.CurrentPage.Stroke;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Due dettagli consentono di risparmiare molto tempo in fase di debug. Lo spessore della linea è uno stato, non un argomento: SetLineWidth lo imposta una volta e ogni chiamata a Stroke successiva utilizzerà quel valore finché non lo si cambierà di nuovo; ecco perché la spezzata sopra è più spessa della linea orizzontale. Inoltre, il tracciato viene azzerato dopo ogni chiamata a Stroke, quindi un Stroke dimenticato significa che la geometria pianificata con tanta cura non verrà renderizzata affatto. Se una forma manca dall'output, la chiamata di disegno è la prima cosa da controllare.
Le coordinate sono in punti, e i punti sono frazionari. MoveTo e LineTo accettano valori di tipo Single, quindi una linea sottilissima a 0.5 punti o una posizione a 72.25 è valida e significativa, non arrotondata all'unità intera più vicina. Questa precisione è importante in due sensi opposti. Uno spessore di linea inferiore a circa 0.5 può essere renderizzato come la linea più sottile possibile dipendente dal dispositivo, che scompare sullo schermo e ricompare in stampa; quindi una linea visibile richiede uno spessore impostato intenzionalmente piuttosto che il valore predefinito. All'estremo opposto, l'allineamento delle regole di tabella e delle linee di griglia a coordinate di punti interi impedisce a una griglia fitta di apparire leggermente irregolare a causa di arrotondamenti diversi tra linee adiacenti. Stabilite lo spazio della griglia in punti fin dall'inizio e il resto del layout lo erediterà.
Forme piene e colore
Le primitive chiuse possono essere riempite anziché contornate. Rectangle accetta una posizione e una dimensione, Circle accetta un centro e un raggio, ed entrambi vengono applicati con Fill, che colora l'interno con il colore di riempimento corrente, o con Stroke per il solo contorno. Il colore di riempimento e il colore del tratto sono parti dello stato distinte, impostate con SetRGBFillColor e SetRGBStrokeColor, che accettano entrambi un singolo valore TColor. Ciò significa che è possibile riutilizzare direttamente le costanti di colore di Delphi e la funzione ausiliaria RGB.
// Rectangle(X, Y, Width, Height): X and Y are the lower-left corner.
Pdf.CurrentPage.SetRGBFillColor(RGB(220, 60, 60));
Pdf.CurrentPage.Rectangle(72, 500, 160, 90);
Pdf.CurrentPage.Fill;
// Circle(X, Y, Radius): X and Y are the center.
Pdf.CurrentPage.SetRGBFillColor(clNavy);
Pdf.CurrentPage.Circle(420, 545, 45);
Pdf.CurrentPage.Fill;
// Outline only: set a stroke color and a width, then Stroke.
Pdf.CurrentPage.SetLineWidth(2);
Pdf.CurrentPage.SetRGBStrokeColor(clBlack);
Pdf.CurrentPage.Rectangle(72, 400, 160, 60);
Pdf.CurrentPage.Stroke;
Una scelta di colore che l'esempio originale ha sbagliato
Una versione precedente di questo esempio generava i colori con Random($FFFFFF) per ogni forma. Sembra vivace, ma è l'approccio sbagliato per i documenti generati. Un PDF creato da codice solitamente deve essere testato, e i colori di riempimento casuali rendono l'output impossibile da confrontare tra un'esecuzione e l'altra: una differenza byte per byte rispetto a un file di riferimento valido fallirà ogni volta, senza un reale motivo. Scegliete colori espliciti. Quando desiderate variazioni tra una serie di forme, gestitele dai vostri dati o da un array di tavolozze fisso, in modo che lo stesso input produca sempre lo stesso file. Il determinismo vale più della novità quando il file passa attraverso una pipeline di rilascio.
Dove il disegno vettoriale ripaga e dove no
Utilizzate queste chiamate di tracciati e forme quando la geometria viene generata: linee di griglia e barre di grafici, linee di mezzeria di una tabella di fattura, riquadri esplicativi su un diagramma, o un marchio di logo espresso come una manciata di tracciati. Tutto questo scala senza sfocature e non aggiunge quasi nulla alla dimensione del file, perché un rettangolo è rappresentato da pochi numeri invece che da migliaia di pixel. Anche l'altra faccia della medaglia è vera: se ciò che avete effettivamente è una fotografia o uno screenshot, disegnatelo come immagine con AddImage e ShowImage; tracciare una bitmap con chiamate vettoriali non porta alcun vantaggio. Anche le curve complesse esulano dallo scopo di questa sezione. Le primitive sopra descritte sono segmenti rettilinei, rettangoli e cerchi, che coprono la stragrande maggioranza del lavoro di reportistica reale; qualsiasi elemento che richieda curve di Bezier a forma libera fa parte di un'altra sezione delle API.
L'ultima abitudine utile da mantenere è la verifica. La geometria generata può funzionare sulla vostra macchina e fallire sulla quella del cliente, di solito a causa della sostituzione dei caratteri nel testo inserito o di presupposti errati sulle dimensioni della pagina. Aprite il file finale a diversi livelli di zoom per verificare che i bordi rimangano nitidi e controllate che ogni forma si trovi all'interno del riquadro dei margini previsto. Con uno schema di colori deterministico, questo controllo può essere automatizzato rispetto a un PDF di riferimento, anziché essere eseguito a occhio.
Le chiamate MoveTo, LineTo, Stroke, Fill e quelle di colore mostrate qui fanno parte del componente HotPDF per Delphi e C++Builder.