Odborný článok

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

HotPDF kreslí vektorovú grafiku vytvorením cesty na aktuálnej stránke a následným požiadaním o jej vykreslenie (vymaľovanie). Medzi tým nie je žiadny bitmapový krok. Čiara, ktorú nakreslíte pomocou MoveTo a LineTo, končí ako operátory ciest PDF v prúde obsahu, takže zostáva skutočným vektorom: ostrým pri 50 % priblížení, ostrým pri 1600 % a veľkostne zlomkom toho, čo by stála zrastrovaná verzia. Pre diagramy, čiary tabuliek, osi grafov a dekorácie formulárov je to presne to, čo chcete, a príslušné rozhranie API je dostatočne malé na to, aby ste sa ho naučili na jedno posedenie

Celá kresliaca plocha žije v THotPDF.CurrentPage. Medzi BeginDoc a EndDoc na tomto objekte stránky nastavíte farbu a hrúbku čiary, zadáte geometriu a zavoláte operátor na vykreslenie, ktorý to potvrdí. Štyri primitívy, ktoré budete používať najviac, sú MoveTo a LineTo pre ľubovoľné cesty, Rectangle pre obdĺžniky (box), Circle pre kruhy (disk) a dva operátory maľovania Stroke a Fill

Súradnicový systém má počiatok vľavo dole

Toto je jediná vec, o ktorú zakopne každý, kto prichádza z VCL. Komponent TCanvas, s ktorým kreslíte ovládacie prvky na obrazovku, kladie počiatok do ľavého horného rohu, pričom Y rastie smerom nadol. PDF robí opak. HotPDF meria od ľavého dolného rohu stránky v bodoch (points, 1/72 palca), pričom Y sa s pohybom nahor zväčšuje. Bod na Y := 720 sedí blízko hornej časti stránky US Letter, ktorá je vysoká 792 bodov, a Y := 50 sedí blízko spodnej časti. Ak vaša prvá kresba vyjde vertikálne zrkadlovo, toto je ten dôvod: kód prenesený z grafiky na obrazovke predpokladá nesprávny smer a uteká mimo spodný okraj

Rovnaká konvencia riadi aj TextOut, takže text a tvary zdieľajú jeden mentálny model, len čo si ho osvojíte. Plánujte rozloženie tak, že sa rozhodnete, kde sa nachádza spodná časť každého prvku, nie horná časť, a zvyšok už nasleduje po tom

Cesty: MoveTo, LineTo, Stroke

Potiahnutá cesta (stroked path) predstavuje zdvihnuté pero, jeho umiestnenie a potiahnutie. MoveTo zdvihne pero a nastaví počiatočný bod bez toho, aby čokoľvek vyznačil. Každé LineTo rozširuje aktuálnu cestu na nový bod. Nič sa na stránke nezobrazí, kým nezavoláte Stroke, čo nakreslí nahromadenú cestu pomocou aktuálnej farby čiary (stroke color) a hrúbky čiary, potom cestu vymaže, aby ďalšie MoveTo začalo nanovo

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 ušetria skutočný čas pri ladení chýb. Hrúbka čiary je stav, nie argument: SetLineWidth ju nastaví raz a každý ďalší Stroke používa túto hodnotu, kým ju znova nezmeníte, a preto je lomená čiara (polyline) nakreslená vyššie hrubšia ako bežná vodorovná čiara. Ďalším je to, že sa cesta reštartuje po každom Stroke, takže zabudnutý Stroke znamená, že geometria, ktorú ste tak starostlivo vyskladali, sa vôbec nikdy nevykreslí. Ak vo výstupe chýba nejaký tvar, volanie maľovania je prvým miestom, kam by ste sa mali pozrieť

Súradnice sú body a body môžu byť desatinné. MoveTo a LineTo prijímajú hodnoty typu Single, takže najtenšia línia na hodnote 0.5 bodov alebo pozícia na hodnote 72.25 je povolená a má zmysel – nezaokrúhli sa preč na najbližšiu celú jednotku. Na tejto presnosti záleží v dvoch opačných smeroch. Šírka čiary menšia ako približne 0.5 sa dokáže vykresliť ako najtenšia možná čiara závislá od zariadenia, ktorá na obrazovke zmizne a objaví sa pri tlači, takže viditeľná čiara si vyžaduje hrúbku, ktorú nastavíte zámerne namiesto predvolenej hodnoty. Na druhom konci je prichytenie čiar pravidiel tabuľky a mriežky k celým bodovým súradniciam, čo zabraňuje tomu, aby hustá mriežka vyzerala mierne nerovnomerne v miestach, kde sa susediace čiary líšia v zaokrúhlení. Rozhodnite o odstupoch mriežky v bodoch (points) vopred a zvyšok rozloženia ju po nej zdedí

Vyplnené tvary a farby

Uzavreté primitívy možno vyplniť namiesto ich ohraničenia (obtiahnutia). Rectangle prevezme pozíciu a veľkosť, Circle prevezme stred a polomer a ktorákoľvek z nich sa odovzdá pomocou Fill, čo vnútro pomaľuje aktuálnou farbou výplne, alebo pomocou Stroke iba pre nakreslenie obrysu. Farba výplne (fill) a farba ohraničenia (stroke) sú dva samostatné stavy, nastavujú sa pomocou SetRGBFillColor a SetRGBStrokeColor, pričom obe preberajú jednotlivo typ TColor. Znamená to, že môžete znova priamo použiť konštanty farieb z Delphi, ako aj pomocníka 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;

Všimnite si profil argumentu v príkaze Rectangle. Je to pozícia plus veľkosť, X, Y, Width, Height, a nie dva protiľahlé rohy. Funkcia TCanvas.Rectangle, ktorú vývojári v Delphi poznajú, prijíma rohy (Left, Top, Right, Bottom), takže pamäť svalov odovzdá do HotPDF druhý roh tam, kde očakáva hodnoty šírky a výšky, čo spôsobí, že vygenerovaný obdĺžnik bude mať zlú veľkosť. Dvojica (X, Y) predstavuje ľavý dolný roh, zhodný s pôvodom (počiatkom) stránky. Pri kruhu ide pri dvojici (X, Y) o stred, a tretím argumentom je polomer meraný v bodoch (points)

Jedna voľba farieb, v ktorej sa pôvodná ukážka zmýlila

Staršia verzia tohto príkladu generovala farby pomocou Random($FFFFFF) pre každý tvar. Vyzerá to živo a je to zlý inštinkt pre generované dokumenty. PDF súbor zostavený z kódu je zvyčajne niečo, čo taktiež budete chcieť otestovať a vďaka náhodným farbám vo výplniach nemožno pri jednotlivých behoch porovnať žiaden výstup: overenie po bajtoch proti známemu bezchybnému súboru zakaždým zlyhá, a to bez akéhokoľvek reálneho dôvodu. Vyberte explicitné (presne stanovené) farby. Keď chcete rozmanitosť v celej sérii tvarov, spravujte ju zo svojich údajov alebo zo zoznamu predvolenej palety uloženej v matici, čím zabezpečíte, že tie isté vstupy vždy prinesú tie isté súbory. Determinizmus stojí za viac než len novosť, keď sa artefakt presúva cez reťazec vydávania

Spájanie primitív dohromady: Rámček s vysvetlivkou (popisok)

Každá primitíva je sama o sebe jednoduchá; vypláca sa to vtedy, keď sa ich zopár skomponuje do niečoho, čo skutočný report naozaj potrebuje. Vysvetlivka (rámček s popisom), teda okomentovaný obdĺžnik (box), ktorý ukazuje na nejaký obrazec a vysvetľuje ho, využíva všetko, čo bolo zatiaľ opísané: vyplnený obdĺžnik s orámovaním, natiahnutá ukazovacia (vodiaca) línia, bodka, aby sa ukotvil ukazovateľ, a samotný text rozložený v poli obdĺžnika pomocou rovnakých súradníc podľa ľavého dolného okraja, aké používajú tvary. Metóda FillAndStroke si na tomto mieste obháji svoje postavenie, vyfarbí vnútro obrysu jednej zadaných ciest a nanesie ich naraz, namiesto toho, aby ste dvakrát museli znova budovať rovnaký obdĺžnik

var
  Pdf: THotPDF;
begin
  Pdf := THotPDF.Create(nil);
  try
    Pdf.FileName := 'Callout.pdf';
    Pdf.BeginDoc;

    // 1. The box: pale fill plus a visible border, one path, one commit.
    //    Rectangle is lower-left corner plus size, Y measured from the bottom
    Pdf.CurrentPage.SetRGBFillColor(RGB(255, 244, 214));   // pale amber panel
    Pdf.CurrentPage.SetRGBStrokeColor(RGB(180, 130, 40));  // darker rim
    Pdf.CurrentPage.SetLineWidth(1);
    Pdf.CurrentPage.Rectangle(90, 600, 240, 70);
    Pdf.CurrentPage.FillAndStroke;

    // 2. The pointer: one stroked segment from the box edge down
    //    toward the thing being annotated
    Pdf.CurrentPage.SetLineWidth(1.5);
    Pdf.CurrentPage.MoveTo(90, 615);        // left edge of the box
    Pdf.CurrentPage.LineTo(66, 546);
    Pdf.CurrentPage.Stroke;

    // 3. A filled dot anchors the pointer at its target
    Pdf.CurrentPage.SetRGBFillColor(RGB(180, 130, 40));
    Pdf.CurrentPage.Circle(64, 542, 3);
    Pdf.CurrentPage.Fill;

    // 4. The label, positioned relative to the box's lower-left corner.
    //    Text and shapes share one coordinate system, so the offsets
    //    are plain arithmetic against (90, 600)
    Pdf.CurrentPage.SetFont('Arial', [fsBold], 10);
    Pdf.CurrentPage.TextOut(102, 645, 0, 'Check this total');
    Pdf.CurrentPage.SetFont('Arial', [], 9);
    Pdf.CurrentPage.TextOut(102, 628, 0, 'The rounding rule changed in the');
    Pdf.CurrentPage.TextOut(102, 616, 0, 'June release; verify against v2.1');

    Pdf.EndDoc;
  finally
    Pdf.Free;
  end;
end;

Všimnite si, aké malé množstvo správy stavu tento kompozit potrebuje. Farba výplne (fill color), farba ťahu čiary (stroke color) a hrúbka čiary (line width) sú nastavené bezprostredne pred tvarom, ktorý ich požíva, takže každý blok tohto nákresu pôsobí ako úplne sebestačná (nezávislá) jednotka a možno jeho usporiadanie modifikovať, rovnako ako vyňať ho a zapuzdriť do pomocného volania, pričom so sebou nenabaľuje žiaden ukrytý stav. Ak by ste tieto prvky dokázali zahrnúť do ucelenej procedúry prijímajúcej ukotvený bod a reťazce s textom, získate tak opakovane prístupnú (užitočnú) poznámku ku každému diagramu iba za „cenu" štyridsiatich riadkov

Kde sa vektorové kreslenie oplatí a kde nie

Siahnite po týchto volaniach ciest a tvarov vtedy, keď je geometria generovaná: mriežka grafu, pruhy na ňom, vodorovné linky (čiarové pravidlá) v tabuľke s faktúrou, upozornenia (callouts) označujúce informácie do diagramov či napr. ochranná značka v tvare loga tvorená obmedzeným spektrom nakreslených línií a kriviek. Toto všetko je škálovateľné bez rizika rozmazania, pričom táto pridaná vlastnosť nepredstavuje takmer žiadnu dodatočnú záťaž ohľadom konečnej veľkosti súboru – keďže každý obdĺžnik, štvorec a pod. si vnútorne nesie len zopár alfanumerických cifier, nie však tisíckami pixelov, ktoré so sebou obvykle ťahajú grafické obrazce. No aj pohľad na opačnú misku svedomito platí. Ak v skutočnosti disponujete rastrovanými portrétmi, fotografiami, skenmi a takisto obrázkami vyčlenenými presne na to, vložte ich formou „Image", s pomocou funkcií na import ako AddImage prípadne ShowImage, namiesto komplikovaného vektorového obťahovania; vo výsledku vám to totiž prinesie prakticky „nulu". Pripamočiare, lineárne úseky a priame i hranaté plochy vrátane polkruhových, ktorými sme obohatili článok pred momentom, vykonajú z celkovej činnosti spojených s reportingom najviac zásadnej a viditeľnej práce, zatiaľ čo ešte by bolo na mieste spomenúť azda trojicu dôležitých zlepšovákov, na ktoré si vývojári potrpia zväčša najvyššou možnou formou spovedania (otázok) po základných tvaroch, a patria k nim, samozrejme: krivky, formátovanie čiary vo význame rozloženia prerušovanej (tzv. "dash") vzorky plus otázka priesvitnosti – čiže všetko vedomosti sídliace u toho istého plátna

Krivky, pomlčky (dashes) a priehľadnosť v skratke

Voľne formované krivky rozširujú mechaniku existujúceho vektorizovaného obsahu s trasou pre kresbu. CurveToC(X1, Y1, X2, Y2, X3, Y3) slúži na uchytenie kubického, Bézierovho predlženého úseku vedúceho z dovtedajšieho začiatočného / východiskového umiestnenia na cieľový koncový cíp – (X3, Y3) v ohnutí (tzn. ohnutého za prítomnosti kontrolných – kontrolujúcich - bodov pre manipuláciu z tvarom na obidvoch koncoch). K podskupine dôležitých pomocných povelov rovnako patria funkcie ako CurveToV alebo inak povedané alternatívny operátor s priradeným "skratkovitým znením" nazvaný: CurveToY. Tento má ambície chopiť sa všetkých úloh a ujať sa príprav tam kde z predvolených vlastností vyplýva, že minimálne len niektorý z ovládacích (takzvaných) prispôsobovacích "Bézierových bodov", je totožný k začiatočnému (ale aj zakončujúcemu bodu trasy). Trasa môže kombinovať voľne segmenty pre spájanú kresbu (LineTo), spolu s CurveToC bez akéhokoľvek narušenia sémantiky, pričom by ste sa pred aplikáciou príkazu – s odovzdaním a záväzkom definitívneho zápisu operácie Stroke či sfarbovaniu prostredníctvom známeho príkazu (Fill), nemuseli nijako obmedzovať, ako už zaiste s odstupom i predtým vyplynulo (ide primárne z kontextu tohto "súhrnu" o vytváranie ladných krivkových grafových prvkov, ale platí i pre uhladenie hrán)

Prerušované ťahy („čiary" s bodkami alebo podobe pomlčiek) si svoje postavenie uplatňujú identickým prístupom ako hrúbka. SetDash([3, 3], 0) dokáže na komplet prekonvertovať línie každého ťahu zapojeného / realizovaného po zadaní takéhoto požiadavku, ktorý strieda – spúšťa po odznení určitého taktu naprogramovaného intervalu, respektíve s pridaním počítadla prislúchajúcemu trom-bodom – teda tri bodky spusteného ťahu, po ktorom by mal (po odpočte presne s toľkými ďalšími v bodoch/jednotkách pre formátovaný záznam, v zastúpení požiadavky na striedme / vypustené tiché pole pre medzeru / neviditeľný záznam - "voľnobežný"), započať rovnaký kolobeh od začiatku. Jednotlivo a v reťazcoch poľu dodané sekvenčne a oddelene formátovanou prítomnosťou priraďujúc pre formát argumenty. Ak hovoríme o druhej strane funkcie ako o doplnkovom druhom (poslednom príkaze), je rovnako zodpovedný najmä ohľadne synchronizačného aspektu – s cieľovým zámerom a možnosťou odloženého časového spúšťadla/cyklu pri fáze posunu. Zatiaľ na objasnenie funkčnej bázy ešte jedna vedomosť a spomenutý ekvivalent v tvare operátora NoDash za obvyklých okolností komplet udelené preferencie vyrovná do stabilného normálu s plnou / kontinuálnou čiarou pri pohybe bez akýchkoľvek výnimočností striedajúcich medzery s pomlčkami - kým, tak však neučiníte z pridelenej obmedzujúcej "kliatby", prejde (skrze ťah v pomlčkách), takýto „vzor", úplne všade kam je udelený – na všetky tvary či inak nadefinované objekty od spomínaných zlomových / neobvyklých pravidiel mriežky potvrdenej ťahom

Transparentnosť (Priehľadnosť). Prechádza skrz „špeciálne vyčlenenú inštanciu, respektíve takzvaný požiadavok so zaužívanou konvenciou/vysvetlivkou/vlastnou nomenklatúrou", skôr než len s poskytnutím zadefinovania farebným odtieňom pre priradenie jednému argumentu pri sfarbení, pretože „alfa kanál – transparentnosť z pohľadu vlastností generovaného PDF dokumentu", plne ovláda slovníkovú ponuku pre uchovanie dôležitého definujúceho zoskupenia/stavu u kresby/plátna v systéme knižnice s nákresmi. Uložte si tento prvok vo svojom generovanom systéme s RegisterExtGState. Presne pre poskytnutie/odovzdanie príkazov o transparentných výplniach, ak sa pokúsime nastaviť „alfa“ farbené povrchy či "alfa" obrysované/okrajované obťahové ťahy, obvykle zadávané pomocou limitu v rozhraní medzi (0) a najvyššie určenou transparentnosťou pre jeden (1), prenechajúc všetky opraty a delegujúc úlohu pre navrátený výsledok po vykonanej procedúre na už známu štandardizovanú ponuku pomenovaní cez CurrentPage.SetGraphicsState. Vypĺňanie oblastí ako i ohraničenia operátorom "stroke", priraďujú farebnú odozvu vo svojej stanovení presnejšie pridelenej registráciou pre zachovanú priehľadnosť (opacity). Na záver, akákoľvek dodatočná požiadavka s "ceremóniou/vyčleneným formalizovaním", predvedenými povelmi s udelením farby (farebných tónovaní a obťahov), dokážu obhájene demonštrovať zohľadnenú cennosť – napr. zvýraznený označený preblikujúci ukazovateľ – lišta v stĺpcoch zaoberajúcej sa informáciami s podtitulkami atď., s úplne plynulou prezentáciou nad textom - čiastočne odhaleným v sprievode priehľadného prekrývacieho odlesku bez akejkoľvek možnosti na jeho neželané prekrytie

Návykom z množiny pravidiel pre dodržiavanie, ktorý stojí na vrchole dôležitosti zosumarizovania si pred dokončovaním a úpravou kódu, je neomylné overovanie výstupnej presnosti pre testovanú platformu, najmä pre zachovanie čistého/správneho výsledku. Geometria vo vektoroch a objekty generované cez kreslenie môžu vyhovovať parametrom od vášho prednastaveného štandardu, na ktorom ste ho navrhli/testovali ale môžu fatálne (resp. neslávne) skolabovať na cudzom PC používateľa/cieľovej protistrany u koncového prijímateľa, zvyčajne s prihliadnutím na dôležitý zásah zo subtitúcií chýbajúcich rezov fontu v celom texte (a nielen jemu samotnom), ale rovnako s nabaľujúcimi konzekvenciami z pridružených chýbajúcich špecifikácií s veľkosťou a definovanými normami na rozmery stránky a i., ktoré si neudržali svoje dodané proporčné zarovnania a pod. Vygenerovaný a hotový súbor si skúšobne nechajte spustiť – vyobrazovať viackrát pod prislúchajúcim oknom v rozdielnych úrovniach (tzv. hladinách rozličných zobrazení pri zoome) s jasným očakávaním o udržaní ostrých línií na spravovanej a navrhnutej vektorizovanej štruktúre. Dodržte v overeniach istotu pri dodaných testoch a preskúmajte/uzavrite všetky kontrolované obrysy (čím presvedčivejšie sa presvedčíte ohľadne vopred naprogramovaných limitov pri zachovaní „bounding box"-ov pre jednotlivé hranice a medzere v zadaných mriežkach a nákresoch s okrajmi, tak rovnako dôležitejšie prispejete zaiste k priaznivejšiemu výsledku). Preukázanou deterministickou podrobne navrhnutou (resp. farebnou zoskupenou štruktúrou či odtieňom pridaným presným algoritmom), overovací cyklus bez zdĺhavých zaváhaní, dokáže poskytnúť plnú/čistú mieru na „zautomatizovanom overovacom móde", nasadenom presne na podrobne zdokladovaný porovnávaný referenčný súbor s generovaným výstupom (preukazujúcim zhody), bez ohľadu na nespoľahlivosť očí a subjektívne merania s odhadmi u kontrolóra (a testovača) s pohľadom, zaručene spoľahlivo a úspešne na úrovni PDF referencie

Volania MoveTo, LineTo, Stroke, Fill a farby, ktoré sú tu ukázané, sú súčasťou komponentu HotPDF pre Delphi a C++Builder