Technical Article

HotPDF drobės braižymas Delphi aplinkoje: vektoriniai keliai ir spalvos

HotPDF braižo vektorinę grafiką sukurdamas kelią dabartiniame puslapyje, o tada nurodydamas jį nupiešti. Tarp šių žingsnių nėra jokio taškinio paveikslėlio (bitmap) kūrimo etapo. Linija, kurią nubrėžiate su MoveTo ir LineTo, PDF turinio sraute virsta kelio operatoriais, todėl išlieka tikras vektorius: aiškus tiek esant 50% masteliui, tiek 1600% masteliui, ir užima tik dalį tos vietos, kurios reikėtų rasterizuotai versijai. Diagramoms, lentelių linijoms, grafikų ašims ir formų dekoracijoms tai yra būtent tai, ko jums reikia, o už to slypintis API yra pakankamai paprastas, kad jį išmoktumėte per vieną kartą.

Pati braižymo plokštuma yra pasiekiama per THotPDF.CurrentPage. Tarp BeginDoc ir EndDoc šiame puslapio objekte nustatote spalvą ir linijos plotį, išdėstote geometriją ir iškviečiate piešimo operatorių jai pritaikyti. Keturi primityvai, kuriuos naudosite dažniausiai, yra MoveTo ir LineTo laisvos formos keliams, Rectangle stačiakampiams, Circle apskritimams ir du piešimo operatoriai: Stroke (apvadas) bei Fill (užpildas).

Koordinačių sistema yra apatiniame kairiajame kampe

Tai yra vienintelis dalykas, kuris klaidina visus, ateinančius iš VCL aplinkos. TCanvas, kuriame piešiate valdiklius, koordinačių pradžią padeda viršutiniame kairiajame kampe, o Y reikšmė didėja žemyn. PDF failuose yra priešingai. HotPDF matuoja nuo puslapio apatinio kairiojo kampo taškais (points, 1/72 colio), o Y didėja judant į viršų. Taškas ties Y := 720 yra netoli US Letter formato puslapio (kurio aukštis – 792 taškai) viršaus, o Y := 50 yra netoli apačios. Jei pirmasis jūsų brėžinys bus vertikaliai apverstas, priežastis yra būtent ši: kodas, perkeltas iš ekrano grafikos, naudoja netinkamą kryptį ir išeina už apatinės ribos.

Ta pati konvencija taikoma ir TextOut metodui, todėl tekstas ir figūros naudoja tą patį mentalinį modelį, kai tik jį suprantate. Planuokite maketą nuspręsdami, kur bus kiekvieno elemento apačia, o ne viršus, ir viskas bus paprasta.

Keliai: MoveTo, LineTo, Stroke

Apvestas kelias veikia kaip pakeltas, padėtas ir vedamas rašiklis. MoveTo pakelia rašiklį ir nustato pradžios tašką nieko nepiešdamas. Kiekvienas LineTo pratęsia esamą kelią iki naujo taško. Puslapyje nieko neatsiranda, kol neiškviečiate Stroke, kuris nubraižo sukauptą kelią naudodamas dabartinę apvado spalvą ir linijos plotį, o todėl išvalo kelią, kad kitas MoveTo prasidėtų iš naujo.

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;

Dvi detalės padeda sutaupyti realaus derinimo laiko. Linijos plotis yra būsena (state), o ne argumentas: SetLineWidth nustato jį vieną kartą, ir kiekvienas vėlesnis Stroke naudoja šią reikšmę, kol ją vėl pakeisite – štai kodėl aukščiau pateikta polilinija yra storesnė už pirmąją liniją. Be to, kelias išvalomas po kiekvieno Stroke iškvietimo, todėl pamirštas Stroke reiškia, kad jūsų kruopščiai išdėstyta geometrija niekada nebus atvaizduota. Jei rezultate trūksta figūros, piešimo iškvietimas yra pirmoji vieta, kur reikėtų ieškoti klaidos.

Koordinatės yra taškai (points), o taškai gali būti daliniai skaičiai. MoveTo ir LineTo priima Single tipo reikšmes, todėl plona 0.5 taško linija arba padėtis ties 72.25 yra visiškai teisinga ir prasminga, o ne suapvalinama iki artimiausio sveiko skaičiaus. Šis tikslumas yra svarbus dviem priešingomis kryptimis. Mažesnis nei maždaug 0.5 linijos plotis gali būti atvaizduojamas kaip ploniausia įmanoma įrenginio linija, kuri ekrane gali išnykti, o spausdinant vėl atsirasti, todėl matomai linijai reikėtų nustatyti sąmoningą plotį, o ne palikti numatytąjį. Kita vertus, lentelių linijų ir tinklelių koordinačių pritraukimas prie sveikųjų taškų verčių apsaugo tankų tinklelį nuo netolygaus vaizdo, kai gretimos linijos suapvalinamos skirtingai. Iš anksto suplanuokite tinklelio tarpus taškais, o likęs maketas juos paveldės.

Užpildytos figūros ir spalva

Uždari primityvai gali būti užpildomi, o ne tik apvedami. Rectangle priima padėtį ir dydį, Circle – centrą ir spindulį, o abu jie pritaikomi naudojant Fill, kuris nudažo vidų dabartine užpildo spalva, arba Stroke tik apvadui nubrėžti. Užpildo ir apvado spalvos yra atskiros būsenos dalys, nustatomos su SetRGBFillColor ir SetRGBStrokeColor, kurios abi priima vieną TColor reikšmę. Tai reiškia, kad galite tiesiogiai naudoti Delphi spalvų konstantas ir RGB funkciją.

// 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;

Atkreipkite dėmesį į metodo Rectangle argumentų struktūrą. Ji susideda iš pozicijos ir dydžio: X, Y, Width, Height, o ne iš dviejų priešingų kampų. Delphi kūrėjams žinomas TCanvas.Rectangle priima (Left, Top, Right, Bottom), todėl dėl įpročio galite perduoti HotPDF antrąjį kampą ten, kur tikimasi pločio ir aukščio, ir stačiakampis bus netinkamo dydžio. (X, Y) pora yra apatinis kairysis kampas, atitinkantis puslapio koordinačių pradžią. Apskritimo atveju (X, Y) yra centras, o trečiasis argumentas – spindulys taškais.

Vienas spalvos pasirinkimas, kuris buvo klaidingas pradiniame pavyzdyje

Senesnėje šio pavyzdžio versijoje kiekvienai figūrai spalvos buvo parenkamos atsitiktinai naudojant Random($FFFFFF). Tai atrodo dinamiška, tačiau generuojamiems dokumentams tai nėra tinkamas sprendimas. PDF failą, kurį kuriate iš kodo, paprastai norite ir išbandyti, o atsitiktinės užpildo spalvos daro rezultatų palyginimą neįmanomą: baitų lygio palyginimas (diff) su žinomu geru failu kiekvieną kartą nepavyks be jokios realios priežasties. Pasirinkite aiškias spalvas. Kai norite įvairovės figūrų serijoje, valdykite ją naudodami savo duomenis arba fiksuotą spalvų paletės masyvą, kad ta pati įvestis visada sugeneruotų tą patį failą. Determinizmas yra vertingesnis už naujumą, kai failas keliauja per gamybos ciklą.

Kur vektorinis braižymas atsiperka, o kur ne

Naudokite šiuos kelio ir figūros iškvietimus, kai geometrija yra generuojama: grafikų linijoms ir stulpeliams, sąskaitos-faktūros lentelės linijoms, išnašų laukeliams diagramose, logotipui, sudarytam iš kelių kelių. Visa tai mastelį keičia be jokio suliejimo ir beveik nepadidina failo dydžio, nes stačiakampis yra tik keli skaičiai, o ne tūkstančiai pikselių. Kita vertus, jei turite nuotrauką ar ekrano nuotrauką, pieškite ją kaip vaizdą naudodami AddImage ir ShowImage; bandymas atkurti taškinį paveikslėlį vektoriniais iškvietimais neduos jokios naudos. Sudėtingos kreivės taip pat čia neaptariamos. Pirmiau nurodyti primityvai yra tiesios atkarpos, stačiakampiai ir apskritimai, kurie atlieka didžiąją dalį realių ataskaitų braižymo darbų; viskam, kam reikia laisvos formos Bezjė (Bezier) kreivių, naudojama kita API dalis.

Kitas įprotis, kurį verta išlaikyti, yra patikrinimas. Sugeneruota geometrija gali puikiai veikti jūsų kompiuteryje, bet nepavykti pas klientą, dažniausiai dėl šriftų pakeitimo tekste arba nepasitvirtinusio puslapio dydžio numatymo. Atidarykite paruoštą failą keliais mastelio lygiais, kad įsitikintumėte, jog kraštai išlieka švarūs, ir patikrinkite, ar kiekviena figūra telpa į jūsų numatytas paraštes. Naudojant deterministinę spalvų schemą, šį patikrinimą galima automatizuoti lyginant su pavyzdiniu PDF failu, o ne tikrinti vizualiai.

Čia parodyti MoveTo, LineTo, Stroke, Fill ir spalvų iškvietimai yra HotPDF komponento, skirto Delphi ir C++Builder, dalis.