HotPDF piešia vektorinę grafiką sukurdamas kelią dabartiniame puslapyje ir tada nurodydamas jį nupiešti. Tarp šių veiksmų nėra jokio taškinės grafikos (bitmap) tarpinio žingsnio. Linija, kurią nubrėžiate su MoveTo ir LineTo, turinio sraute tampa PDF kelio operatoriais, todėl išlieka tikru vektoriumi: ryški esant 50% priartinimui, ryški esant 1600%, ir užima tik mažą dalį to dydžio, kurio prireiktų rastruotai versijai. Diagramoms, lentelių linijoms, grafikų ašims ir formų dekoracijoms tai yra būtent tai, ko jums reikia, o API, slypinti už to, yra pakankamai maža, kad ją būtų galima išmokti vienu prisėdimu
Visas piešimo paviršius egzistuoja THotPDF.CurrentPage. Tarp BeginDoc ir EndDoc tame 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 savavališkiems keliams, Rectangle stačiakampiams, Circle apskritimams (diskams) bei du piešimo operatoriai – Stroke (apvadas) ir Fill (užpildas)
Koordinatų sistema prasideda kairėje apačioje
Tai yra vienas dalykas, kuris suklaidina visus, pereinančius nuo VCL. TCanvas, kuriuo piešiate valdiklius, koordinatų pradžios tašką laiko viršutiniame kairiajame kampe, o Y reikšmė didėja einant žemyn. PDF daro priešingai. HotPDF matuoja nuo kairiojo apatinio puslapio kampo taškais (1/72 colio), kur Y didėja kylant aukštyn. Taškas ties Y := 720 yra netoli US Letter puslapio viršaus, kuris yra 792 taškų aukščio, o Y := 50 yra netoli apačios. Jei jūsų pirmasis piešinys išeina apverstas vertikaliai, štai kodėl: kodas, perkeltas iš ekrano grafikos, numato neteisingą kryptį ir išeina už apatinio krašto
Ta pati taisyklė galioja ir TextOut, todėl tekstas ir figūros dalijasi vienu mentaliniu modeliu, kai tik jį perprantate. Planuokite išdėstymą nuspręsdami, kur yra kiekvieno elemento apačia, o ne viršus, ir visa kita eisis savaime
Keliai: MoveTo, LineTo, Stroke
Nubrėžtas kelias yra tarsi pakeltas, nuleistas ir vilkimas rašiklis. MoveTo pakelia rašiklį ir nustato pradinį tašką nieko nežymėdamas. Kiekvienas LineTo pratęsia dabartinį kelią iki naujo taško. Puslapyje nieko nepasirodo, kol neiškviečiate Stroke, kuris nupiešia sukauptą kelią naudodamas dabartinę apvado spalvą ir linijos plotį, tada 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 daug derinimo (debugging) laiko. Linijos plotis yra būsena, o ne argumentas: SetLineWidth nustato jį vieną kartą ir kiekvienas vėlesnis Stroke naudoja šią reikšmę, kol ją vėl pakeisite; būtent todėl aukščiau esanti laužtė yra storesnė už brūkšnį. Kelias atstatomas po kiekvieno Stroke, todėl pamirštas Stroke reiškia, kad taip 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ų paieškoti klaidos
Koordinatės yra taškai, o taškai gali būti trupmeniniai. MoveTo ir LineTo priima Single reikšmes, todėl labai plona linija ties 0.5 taškais ar pozicija ties 72.25 yra legali ir prasminga, o ne suapvalinta iki artimiausio sveikojo skaičiaus. Šis tikslumas svarbus dviem priešingomis kryptimis. Mažesnis nei 0.5 linijos plotis gali būti atvaizduojamas kaip nuo įrenginio priklausanti ploniausia įmanoma linija, kuri ekrane dingsta, o atspausdinta vėl atsiranda, todėl matomam brūkšniui reikia sąmoningai nustatyto, o ne numatytojo pločio. Kita vertus, lentelių linijų ir tinklelių pritraukimas prie sveikųjų taškų koordinačių neleidžia tankiam tinkleliui atrodyti šiek tiek nelygiai, kai gretimos linijos suapvalinamos skirtingai. Iš anksto nuspręskite tinklelio tarpus taškais ir likusi išdėstymo dalis tai paveldės
Užpildytos figūros ir spalvos
Uždari primityvai gali būti užpildomi, o ne tik apibrėžiami kontūrais. Rectangle priima poziciją ir dydį, Circle priima centrą ir spindulį, ir abu jie gali būti patvirtinami su Fill, kuris nudažo vidų dabartine užpildo spalva, arba su Stroke, skirtu tik kontūrui. Užpildo ir apvado spalvos yra atskiros būsenos, nustatomos su SetRGBFillColor ir SetRGBStrokeColor, kurios abi priima TColor tipą. Tai reiškia, kad galite tiesiogiai naudoti Delphi spalvų konstantas ir RGB pagalbinę 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į į Rectangle argumentų formatą. Tai yra pozicija plius dydis, X, Y, Width, Height, o ne du priešingi kampai. Delphi kūrėjams žinomas TCanvas.Rectangle priima (Left, Top, Right, Bottom), todėl raumenų atmintis perduos HotPDF antrąjį kampą ten, kur jis tikisi pločio ir aukščio, ir dėžutė išeis netinkamo dydžio. Poros (X, Y) koordinatės reiškia kairįjį apatinį kampą, suderintą su puslapio pradžios tašku. Apskritimui (X, Y) yra centras, o trečiasis argumentas yra spindulys taškais
Vienas spalvų pasirinkimas, kurį originaliame pavyzdyje atlikta neteisingai
Senesnėje šio pavyzdžio versijoje kiekvienos figūros spalvos buvo parenkamos naudojant Random($FFFFFF). Tai atrodo gyvai, tačiau generuojamiems dokumentams tai yra neteisingas instinktas. Iš kodo sugeneruotą PDF failą dažniausiai taip pat norisi išbandyti, o atsitiktinės užpildo spalvos padaro išvestį neįmanomą palyginti po kiekvieno vykdymo: tiesioginis (byte-for-byte) palyginimas su žinomu geru failu kaskart nepavyksta be jokios rimtos priežasties. Pasirinkite aiškias spalvas. Kai norite įvairovės keliose figūrose, naudokite spalvas pagal savo duomenis arba iš anksto nustatytą palečių masyvą, kad ta pati įvestis visada sugeneruotų tą patį failą. Determinizmas yra vertingesnis už naujumą, kai artefaktas keliauja per išleidimo (release) konvejerį
Primityvų sujungimas: išnašos langelis
Kiekvienas primityvas pats savaime yra paprastas; tikroji nauda išryškėja tuomet, kai keli iš jų susijungia į tai, ko iš tikrųjų reikia ataskaitai. Išnaša – anotuotas langelis, nurodantis į paveikslėlį ir jį paaiškinantis – naudoja viską, kas buvo aptarta iki šiol: užpildytą stačiakampį su rėmeliu, nubrėžtą rodyklės liniją, tašką, tvirtinantį rodyklę, ir tekstą, išdėstytą langelio viduje naudojant tas pačias kairės-apačios koordinates, kaip ir figūros. Čia ypač praverčia FillAndStroke, vienu patvirtinimu nudažantis ir vieno kelio vidų, ir kontūrą, užuot du kartus braižius stačiakampį
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;
Atkreipkite dėmesį, kaip mažai būsenos valdymo reikia šiam sudėtiniam elementui. Užpildo spalva, apvado spalva ir linijos plotis nustatomi prieš pat juos naudojančią figūrą, todėl kiekvienas piešimo blokas skaitomas kaip savarankiškas vienetas ir gali būti pertvarkytas ar iškeltas į pagalbinę funkciją (helper) neperkeliant paslėptos būsenos. Apgaubkite tai procedūra, priimančia tvirtinimo tašką (anchor point) bei eilutes, ir turėsite daugkartinio naudojimo diagramos anotaciją, kainuojančią vos keturiasdešimt kodo eilučių
Kur vektorinis piešimas atsiperka, o kur ne
Naudokite šiuos kelių ir figūrų iškvietimus, kai geometrija yra generuojama: grafikų tinkleliams ir juostoms, sąskaitos-faktūros lentelės linijoms, išnašų langeliams diagramoje ar logotipo ženklui, išreikštam keliais takais. Viskas puikiai keičia mastelį nesusiliejant ir beveik nepadidina failo dydžio, nes stačiakampis tėra keli skaičiai, o ne tūkstančiai pikselių. Kita medalio pusė taip pat akivaizdi. Jei iš tikrųjų turite nuotrauką ar ekrano kopiją, vietoj to pieškite ją kaip paveikslėlį su AddImage ir ShowImage; rastrinės grafikos brėžimas vektoriniais iškvietimais neduos jokios naudos. Aukščiau esantys tiesūs segmentai, stačiakampiai ir apskritimai atlieka didžiąją dalį realaus ataskaitų darbo, o trys kiti patobulinimai, apie kuriuos kūrėjai klausia toliau – kreivės, punktyriniai raštai ir permatomumas – yra tame pačiame puslapio objekte
Trumpai apie kreives, punktyrus ir permatomumą
Laisvos formos kreivės praplečia tą patį kelių mechanizmą, kurį jau turite. CurveToC(X1, Y1, X2, Y2, X3, Y3) prideda kubinį Bezjė (Bezier) segmentą nuo dabartinio taško iki (X3, Y3), besilankstantį link dviejų valdymo taškų, o sutrumpinti variantai CurveToV ir CurveToY apima atvejus, kai vienas valdymo taškas sutampa su galiniu tašku. Kelias gali laisvai maišyti LineTo ir CurveToC segmentus prieš patvirtinant jį vienu Stroke ar Fill; būtent taip sukuriami užapvalinti kampai ir sklandžios grafikų linijos
Punktyriniai apvadai yra būsena, lygiai taip pat, kaip linijos plotis. SetDash([3, 3], 0) perjungia kiekvieną vėlesnį apvadą į raštą – trys taškai nupiešti, trys taškai praleisti; masyvas nusako nupiešto/praleisto ilgio taškais atkarpas, o antrasis argumentas nurodo fazę, kur prasideda ciklas; NoDash grąžina rašiklį į vientisą liniją. Nustatykite jį, nubrėžkite to reikalaujančius tinklelius ir atstatykite prieš kitą vientisą liniją, antraip punktyras tyliai užkrės viską, kas eina po to
Permatomumas valdomas per pavadintą grafikos būseną, o ne kaip spalvos argumentas, nes alfa PDF faile yra grafikos būsenos žodyno (graphics state dictionary) savybė. Užregistruokite jį dokumente su RegisterExtGState, perduodami užpildo alfa ir apvado alfa reikšmes nuo 0 iki 1, tuomet pritaikykite jo grąžintą pavadinimą su CurrentPage.SetGraphicsState; užpildai ir apvadai nuo to momento bus piešiami užregistruotu nepermatomumu. Tai yra sudėtingesnis procesas nei spalvų nustatytojai, tačiau jis atsiperka, kai paryškinimo juosta turi būti virš teksto, jo nepaslėpdama
Paskutinis įprotis, kurį verta išlaikyti, yra tikrinimas (verification). Sukurta geometrija gali sėkmingai veikti jūsų kompiuteryje, bet nepavykti kliento kompiuteryje – dažniausiai dėl šriftų pakeitimo bet kokiame jūsų pridedamame tekste ar nepasiteisinusios prielaidos dėl puslapio dydžio. Atidarykite užbaigtą failą naudodami kelis mastelius, kad įsitikintumėte, jog kraštai išlieka švarūs, ir patikrinkite, ar kiekviena figūra patenka į numatytą paraščių sritį. Turint deterministinę spalvų schemą, šį patikrinimą galima automatizuoti, lyginant su etaloniniu PDF failu, užuot tikrinus iš akies
Čia parodyti MoveTo, LineTo, Stroke, Fill ir spalvų iškvietimai yra HotPDF komponento, skirto Delphi ir C++Builder, dalis