Technical Article

Kreslenie na plátno HotPDF v Delphi: Vektorové cesty a farby

Knižnica HotPDF vykresľuje vektorovú grafiku tak, že zostaví cestu (path) na aktuálnej stránke a potom ju požiada o vykreslenie. Medzi týmito krokmi neprebieha žiadna bitmapová konverzia. Čiara, ktorú nakreslíte pomocou metód MoveTo a LineTo, skončí priamo ako operátor cesty PDF v dátovom toku obsahu, takže zostáva verným vektorom: je ostrá pri 50% priblížení, ostrá pri 1600% priblížení a zaberá len zlomok veľkosti, ktorú by vyžadovala jej rasterizovaná verzia. Pre diagramy, čiary tabuliek, osi grafov a ozdoby formulárov je to presne to, čo potrebujete, a súvisiace API je dostatočne malé na to, aby ste sa ho naučili na jedno posedenie.

Celá kresliaca plocha žije na objekte THotPDF.CurrentPage. Medzi volaniami BeginDoc a EndDoc nastavujete farbu a šírku čiary na objekte stránky, definujete geometrické tvary a zavoláte operátora kreslenia, ktorý ich vykreslí. Štyri základné prvky, ktoré budete používať najčastejšie, sú MoveTo a LineTo pre ľubovoľné cesty, Rectangle pre obdĺžniky, Circle pre kruhy a dva kresliace operátory Stroke (obrys) a Fill (výplň).

Súradnicový systém začína vľavo dole

Toto je vec, na ktorej sa popáli takmer každý, kto prechádza z prostredia VCL. Objekt TCanvas, ktorým kreslíte ovládacie prvky, umiestňuje počiatok do ľavého horného rohu a súradnica Y rastie smerom nadol. PDF robí presný opak. HotPDF meria súradnice od ľavého dolného rohu stránky v bodoch (1/72 palca), pričom Y rastie smerom nahor. Bod so súradnicou Y := 720 leží blízko horného okraja stránky formátu Letter (ktorá má výšku 792 bodov) a Y := 50 leží blízko spodného okraja. Ak je váš prvý nakreslený objekt vertikálne zrkadlovo obrátený, je to práve preto: kód prenesený z obrazovkovej grafiky predpokladá nesprávny smer a vykresľuje pod spodný okraj stránky.

Rovnaká konvencia platí aj pre metódu TextOut, takže text a tvary zdieľajú rovnaký mentálny model, keď si ho osvojíte. Navrhnite rozloženie stránky tak, že určíte spodnú hranicu každého prvku namiesto hornej, a zvyšok už pôjde sám.

Cesty: MoveTo, LineTo, Stroke

Obrysová cesta (stroked path) pripomína zdvihnutie, položenie a ťahanie pera. MoveTo zdvihne pero a nastaví počiatočný bod bez toho, aby čokoľvek nakreslil. Každé volanie LineTo predĺži aktuálnu cestu k novému bodu. Na stránke sa nič neobjaví, kým nezavoláte Stroke, ktorý vykreslí nahromadenú cestu s použitím aktuálnej farby obrysu a hrúbky čiary, a následne vymaže cestu, aby ďalšie volanie MoveTo začínalo od nuly.

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;

Dva detaily vám ušetria čas pri ladení. Hrúbka čiary je stavom, nie argumentom: SetLineWidth ju nastaví raz a každé ďalšie volanie Stroke použije túto hodnotu, kým ju znova nezmeníte, preto je lomená čiara vyššie hrubšia ako prvá linka. Cesta sa po každom volaní Stroke resetuje, takže zabudnuté volanie Stroke spôsobí, že vaša starostlivo navrhnutá geometria sa vôbec nevykreslí. Ak v grafe chýba nejaký tvar, toto volanie je prvým miestom, kam sa pozrieť.

Súradnice sú v bodoch a body sú desatinné čísla. Metódy MoveTo a LineTo prijímajú hodnoty typu Single, takže jemná čiara s hrúbkou 0.5 bodu alebo pozícia na 72.25 bodoch je platná a zmysluplná, nezaokrúhľuje sa na najbližšiu celú jednotku. Táto presnosť je dôležitá v dvoch opačných smeroch. Šírka čiary pod približne 0.5 bodu sa môže vykresliť ako najtenšia možná čiara závislá od zariadenia, ktorá zmizne na obrazovke a objaví sa až pri tlači, preto viditeľná linka vyžaduje hrúbku, ktorú nastavíte zámerne, a nie predvolenú hodnotu. Na druhej strane, zarovnanie čiar tabuliek a mriežok na celočíselné súradnice v bodoch zabraňuje tomu, aby hustá mriežka vyzerala mierne nerovnomerne v dôsledku odlišného zaokrúhľovania susediacich čiar. Definujte rozostupy mriežky v bodoch hneď na začiatku a zvyšok rozloženia to zdedí.

Vyplnené tvary a farby

Uzatvorené tvary môžu byť vyplnené namiesto samotného obrysu. Metóda Rectangle prijíma pozíciu a veľkosť, Circle stred a polomer, a obidve sa dokončujú buď volaním Fill (ktoré vyfarbí vnútro aktuálnou farbou výplne), alebo volaním Stroke (pre vykreslenie samotného obrysu). Farba výplne a farba obrysu sú samostatné stavy, ktoré sa nastavujú pomocou SetRGBFillColor and SetRGBStrokeColor. Obidve prijímajú typ TColor, čo znamená, že môžete priamo použiť delphiovské konštanty farieb aj pomocnú funkciu 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;

Dajte pozor na štruktúru argumentov metódy Rectangle. Ide o pozíciu plus veľkosť, teda X, Y, šírka, výška, nie o dva protiľahlé rohy. Metóda TCanvas.Rectangle, ktorú vývojári v Delphi dôverne poznajú, prijíma parametre (Left, Top, Right, Bottom), takže svalová pamäť môže HotPDF podhodiť súradnice druhého rohu namiesto šírky a výšky, čo spôsobí nesprávnu veľkosť obdĺžnika. Dvojica (X, Y) predstavuje ľavý dolný roh v súlade s počiatkom stránky. Pri kruhu (Circle) označujú (X, Y) stred a tretím argumentom je polomer v bodoch.

Chyba s výberom farieb v pôvodnom príklade

Staršia verzia tohto príkladu generovala farby pre každý tvar pomocou Random($FFFFFF). Vyzerá to síce pestro, no pri generovaných dokumentoch je to nesprávny prístup. PDF vytváraný kódom zvyčajne chcete testovať a náhodné farby výplne znemožňujú porovnávanie výstupov medzi jednotlivými spusteniami: binárny diff voči overenému referenčnému súboru zlyhá zakaždým bez reálneho dôvodu. Voľte explicitné farby. Keď potrebujete rôznorodosť tvarov, odvoďte ju z vašich dát alebo z fixného poľa palety, aby rovnaký vstup vždy vygeneroval identický súbor. Determinizmus má pri prechode artefaktu zostavovacou linkou oveľa väčšiu hodnotu než náhodná pestrosť.

Kde sa vektorové kreslenie vyplatí a kde nie

Po týchto volaniach ciest a tvarov siahnite vtedy, keď sa geometria generuje programovo: pomocné čiary a stĺpce grafov, linky tabuľky na faktúre, popisy v diagrame alebo logo vyjadrené niekoľkými cestami. Všetko sa prispôsobí bez rozmazania a takmer nezväčší súbor, pretože obdĺžnik je definovaný len niekoľkými číslami namiesto tisícov pixelov. Na druhej strane, ak reálne spracovávate fotografiu alebo snímku obrazovky, vykreslite ju ako obrázok pomocou AddImage a ShowImage; obkresľovanie bitmapy vektorovými volaniami vám neprinesie žiadny úžitok. Zložité krivky sú tu tiež mimo hry. Vyššie uvedené základné prvky sú len priame segmenty, obdĺžniky a kruhy, ktoré pokrývajú veľkú väčšinu bežnej práce na reportoch. Čikoľvek, čo vyžaduje voľné Bezierove krivky, patrí do inej časti API.

Zostávajúcim návykom, ktorý sa oplatí dodržiavať, je overovanie. Generovaná geometria môže na vašom počítači prejsť a u zákazníka zlyhať, najčastejšie kvôli nahrádzaniu písiem pri miešaní textu a tvarov alebo kvôli inému predpokladu o veľkosti stránky. Otvorte horúci súbor pri niekoľkých úrovniach priblíženia a skontrolujte, či okraje zostávajú ostré a či každý tvar leží v rámci plánovaného okraja. Pri deterministickej schéme farieb sa toto overenie dá zautomatizovať porovnaním voči referenčnému PDF súboru bez nutnosti vizuálnej kontroly.

Volania MoveTo, LineTo, Stroke, Fill a nastavenia farieb uvedené v tomto článku sú súčasťou komponentu HotPDF pre Delphi a C++Builder.