HotPDF desenează grafică vectorială construind un traseu pe pagina curentă și cerând apoi ca acesta să fie pictat. Nu există niciun pas de bitmap între cele două. O linie pe care o desenați cu MoveTo și LineTo ajunge ca operatori de traseu PDF în fluxul de conținut, deci rămâne un vector adevărat: clară la 50% mărire, clară la 1600% și cu o fracțiune din dimensiunea pe care ar costa-o o variantă rasterizată. Pentru diagrame, linii de tabel, axe de grafic și decorațiuni de formular, exact asta vă doriți, iar API-ul din spate este destul de mic ca să fie învățat dintr-o singură ședință
Toată suprafața de desen stă pe THotPDF.CurrentPage. Între BeginDoc și EndDoc setați culoarea și lățimea liniei pe acel obiect pagină, așezați geometria și apelați un operator de pictare care o confirmă. Cele patru primitive pe care le veți folosi cel mai des sunt MoveTo și LineTo pentru trasee arbitrare, Rectangle pentru casete, Circle pentru discuri și cei doi operatori de pictare, Stroke și Fill
Sistemul de coordonate are originea în stânga jos
Acesta este lucrul care încurcă pe toată lumea venită dinspre VCL. Obiectul TCanvas cu care pictați controale pune originea în colțul din stânga sus, cu Y crescând în jos. PDF face invers. HotPDF măsoară din colțul din stânga jos al paginii, în puncte (1/72 dintr-un inch), cu Y crescând pe măsură ce urcați. Un punct la Y := 720 stă lângă partea de sus a unei pagini US Letter, care are 792 de puncte înălțime, iar Y := 50 stă lângă partea de jos. Dacă primul dumneavoastră desen iese oglindit pe verticală, acesta este motivul: codul portat din grafica de ecran presupune direcția greșită și fuge dincolo de marginea de jos
Aceeași convenție guvernează și TextOut, așa că textul și formele împart un singur model mental, odată ce îl interiorizați. Planificați un aranjament hotărând unde stă baza fiecărui element, nu vârful, iar restul decurge de acolo
Trasee: MoveTo, LineTo, Stroke
Un traseu conturat este un stilou ridicat, așezat și tras. MoveTo ridică stiloul și fixează punctul de start fără să marcheze nimic. Fiecare LineTo prelungește traseul curent până la un punct nou. Nimic nu apare pe pagină până când apelați Stroke, care desenează traseul acumulat folosind culoarea de contur și lățimea de linie curente, apoi golește traseul, astfel încât următorul MoveTo să pornească de la zero
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'DrawPaths.pdf';
Pdf.BeginDoc;
// Lățimea liniei este în puncte și se aplică până când o schimbați.
Pdf.CurrentPage.SetLineWidth(1.5);
Pdf.CurrentPage.SetRGBStrokeColor(clBlack);
// O linie orizontală lângă partea de sus a paginii (Y măsurat de jos).
Pdf.CurrentPage.MoveTo(72, 720);
Pdf.CurrentPage.LineTo(523, 720);
Pdf.CurrentPage.Stroke; // confirmă traseul; înainte nu s-a desenat nimic
// O polilinie conectată mai groasă: trei segmente într-un singur traseu.
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;
Două detalii economisesc timp real de depanare. Lățimea liniei este stare, nu argument: SetLineWidth o setează o dată, iar fiecare Stroke ulterior folosește acea valoare până când o schimbați din nou, motiv pentru care polilinia de mai sus este mai groasă decât linia. Iar traseul se resetează după fiecare Stroke, deci un Stroke uitat înseamnă că geometria pe care ați așezat-o cu atâta grijă nu se randează deloc. Dacă o formă lipsește din ieșire, apelul de pictare este primul loc unde să vă uitați
Coordonatele sunt puncte, iar punctele sunt fracționare. MoveTo și LineTo acceptă valori Single, deci o linie subțire de 0.5 puncte sau o poziție la 72.25 este legală și are sens, nu este rotunjită la cea mai apropiată unitate întreagă. Acea precizie contează în două direcții opuse. O lățime de linie sub aproximativ 0.5 se poate randa ca cea mai subțire linie posibilă, dependentă de dispozitiv, care dispare pe ecran și reapare la tipărire, deci o linie vizibilă vrea o lățime pe care o setați intenționat, nu valoarea implicită. La celălalt capăt, alinierea liniilor de tabel și a caroiajului la coordonate în puncte întregi împiedică o grilă densă să pară ușor neuniformă acolo unde liniile alăturate se rotunjesc diferit. Hotărâți pasul grilei în puncte de la început, iar restul aranjamentului îl moștenește
Forme umplute și culoare
Primitivele închise pot fi umplute, nu doar conturate. Rectangle primește o poziție și o dimensiune, Circle primește un centru și o rază, iar oricare dintre ele se confirmă cu Fill, care pictează interiorul în culoarea de umplere curentă, sau cu Stroke, doar pentru contur. Culoarea de umplere și culoarea de contur sunt bucăți de stare separate, setate cu SetRGBFillColor și SetRGBStrokeColor, ambele primind un singur TColor. Asta înseamnă că puteți refolosi direct constantele de culoare din Delphi și funcția ajutătoare RGB
// Rectangle(X, Y, Width, Height): X și Y sunt colțul din stânga jos.
Pdf.CurrentPage.SetRGBFillColor(RGB(220, 60, 60));
Pdf.CurrentPage.Rectangle(72, 500, 160, 90);
Pdf.CurrentPage.Fill;
// Circle(X, Y, Radius): X și Y sunt centrul.
Pdf.CurrentPage.SetRGBFillColor(clNavy);
Pdf.CurrentPage.Circle(420, 545, 45);
Pdf.CurrentPage.Fill;
// Doar contur: setați o culoare de contur și o lățime, apoi Stroke.
Pdf.CurrentPage.SetLineWidth(2);
Pdf.CurrentPage.SetRGBStrokeColor(clBlack);
Pdf.CurrentPage.Rectangle(72, 400, 160, 60);
Pdf.CurrentPage.Stroke;
Fiți atent la forma argumentelor lui Rectangle. Este poziție plus dimensiune, X, Y, Width, Height, nu două colțuri opuse. Metoda TCanvas.Rectangle pe care o știu dezvoltatorii Delphi primește (Left, Top, Right, Bottom), deci memoria musculară îi va da lui HotPDF un al doilea colț acolo unde el așteaptă o lățime și o înălțime, iar caseta iese de dimensiune greșită. Perechea (X, Y) este colțul din stânga jos, în acord cu originea paginii. Pentru un cerc, (X, Y) este centrul, iar al treilea argument este raza în puncte
O alegere de culoare pe care exemplul original a greșit-o
O versiune mai veche a acestui exemplu semăna culorile cu Random($FFFFFF) pentru fiecare formă. Arată vioi și este instinctul greșit pentru documente generate. Un PDF pe care îl construiți din cod este de obicei ceva ce vreți și să testați, iar culorile de umplere aleatorii fac ieșirea imposibil de comparat de la o rulare la alta: o diferență octet cu octet față de un fișier bun cunoscut eșuează de fiecare dată, fără vreun motiv real. Alegeți culori explicite. Când vreți varietate pe o serie de forme, conduceți-o din datele dumneavoastră sau dintr-un tablou fix de paletă, astfel încât aceeași intrare să producă întotdeauna același fișier. Determinismul valorează mai mult decât noutatea atunci când artefactul trece printr-un flux de livrare
Punând primitivele împreună: o casetă de explicație
Fiecare primitivă este simplă în sine; câștigul se vede atunci când câteva dintre ele se compun în ceva de care un raport chiar are nevoie. O casetă de explicație, caseta adnotată care arată spre o figură și o lămurește, folosește tot ce am acoperit până acum: un dreptunghi umplut cu bordură, o linie indicatoare conturată, un punct care ancorează indicatorul și text așezat în interiorul casetei folosind aceleași coordonate cu originea în stânga jos ca formele. FillAndStroke își merită locul aici, pictând interiorul și conturul unui singur traseu într-o singură confirmare, în loc să construiască dreptunghiul de două ori
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'Callout.pdf';
Pdf.BeginDoc;
// 1. Caseta: umplere palidă plus o bordură vizibilă, un traseu, o confirmare.
// Rectangle este colțul din stânga jos plus dimensiunea, Y măsurat de jos
Pdf.CurrentPage.SetRGBFillColor(RGB(255, 244, 214)); // panou chihlimbariu palid
Pdf.CurrentPage.SetRGBStrokeColor(RGB(180, 130, 40)); // ramă mai închisă
Pdf.CurrentPage.SetLineWidth(1);
Pdf.CurrentPage.Rectangle(90, 600, 240, 70);
Pdf.CurrentPage.FillAndStroke;
// 2. Indicatorul: un segment conturat de la marginea casetei în jos,
// spre lucrul care este adnotat
Pdf.CurrentPage.SetLineWidth(1.5);
Pdf.CurrentPage.MoveTo(90, 615); // marginea din stânga a casetei
Pdf.CurrentPage.LineTo(66, 546);
Pdf.CurrentPage.Stroke;
// 3. Un punct umplut ancorează indicatorul la ținta lui
Pdf.CurrentPage.SetRGBFillColor(RGB(180, 130, 40));
Pdf.CurrentPage.Circle(64, 542, 3);
Pdf.CurrentPage.Fill;
// 4. Eticheta, poziționată față de colțul din stânga jos al casetei.
// Textul și formele împart un singur sistem de coordonate, deci
// decalajele sunt aritmetică simplă față de (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;
Observați cât de puțină administrare de stare cere compoziția. Culoarea de umplere, culoarea de contur și lățimea de linie sunt setate fiecare chiar înaintea formei care le folosește, așa că fiecare bloc al desenului se citește ca o unitate de sine stătătoare și poate fi reordonat sau extras într-o funcție ajutătoare fără să târască după el stare ascunsă. Împachetați asta într-o procedură care primește punctul de ancorare și șirurile și aveți o adnotare de diagramă reutilizabilă pentru prețul a patruzeci de linii
Unde se merită desenul vectorial și unde nu
Apelați la aceste funcții de traseu și de forme atunci când geometria este generată: caroiajul și barele unui grafic, liniile unui tabel de factură, casete de explicație pe o diagramă, o siglă exprimată prin câteva trasee. Toate se scalează fără neclaritate și adaugă aproape nimic la dimensiunea fișierului, pentru că un dreptunghi înseamnă câteva numere, nu mii de pixeli. Reversul este la fel de cinstit. Dacă ce aveți de fapt este o fotografie sau o captură de ecran, desenați-o ca imagine cu AddImage și ShowImage; trasarea unui bitmap prin apeluri vectoriale nu vă aduce nimic. Segmentele drepte, dreptunghiurile și cercurile de mai sus duc marea majoritate a muncii reale de raportare, iar cele trei rafinamente despre care dezvoltatorii întreabă imediat după, curbele, tiparele de linie întreruptă și transparența, stau pe același obiect pagină
Curbe, linii întrerupte și transparență, pe scurt
Curbele libere prelungesc aceeași mașinărie de trasee pe care o aveți deja. CurveToC(X1, Y1, X2, Y2, X3, Y3) adaugă un segment Bezier cubic de la punctul curent până la (X3, Y3), curbându-se spre cele două puncte de control, iar variantele prescurtate CurveToV și CurveToY acoperă cazurile în care un punct de control coincide cu o extremitate. Un traseu poate amesteca liber segmente LineTo și CurveToC înainte ca un singur Stroke sau Fill să îl confirme, și așa se construiesc colțurile rotunjite și liniile netede de grafic
Conturele întrerupte sunt stare, exact ca lățimea liniei. SetDash([3, 3], 0) comută fiecare contur ulterior pe un tipar de trei puncte desenate și trei puncte libere, tabloul indicând lungimile de rulare desenat/liber în puncte, iar al doilea argument defazând locul unde începe ciclul; NoDash readuce stiloul la linie continuă. Setați-l, conturați caroiajul care îl vrea și resetați-l înaintea următoarei linii continue, altfel linia întreruptă infectează în tăcere tot ce urmează
Transparența trece printr-o stare grafică denumită, nu printr-un argument de culoare, pentru că în PDF canalul alfa este o proprietate a dicționarului de stare grafică. Înregistrați una pe document cu RegisterExtGState, transmițând un alfa de umplere și un alfa de contur între 0 și 1, apoi aplicați numele returnat cu CurrentPage.SetGraphicsState; umplerile și contururile de acolo încolo pictează la opacitatea înregistrată. Este un ceremonial mai greoi decât cel al funcțiilor de culoare și merită prima dată când o bară de evidențiere trebuie să stea peste text fără să îl ascundă
Obiceiul care mai merită păstrat este verificarea. Geometria generată poate trece pe mașina dumneavoastră și cădea pe a unui client, de obicei din cauza substituirii fontului în orice text amestecat sau a unei presupuneri despre dimensiunea paginii care nu se susține. Deschideți fișierul finit la câteva niveluri de mărire ca să confirmați că muchiile rămân curate și verificați că fiecare formă aterizează în interiorul casetei de margini pe care ați intenționat-o. Cu o schemă de culori deterministă, acea verificare poate fi automatizată față de un PDF de referință, nu evaluată din ochi
Apelurile MoveTo, LineTo, Stroke, Fill și cele de culoare prezentate aici fac parte din HotPDF Delphi Component pentru Delphi și C++Builder