HotPDF tekent vectorafbeeldingen door een pad op de huidige pagina op te bouwen en er vervolgens om te vragen dat het wordt geschilderd. Er is geen bitmapstap tussendoor. Een lijn die u tekent met MoveTo en LineTo eindigt als PDF-padoperatoren in de contentstream, dus het blijft een echte vector: scherp bij 50% zoom, scherp bij 1600%, en een fractie van de grootte die een gerasterde versie zou kosten. Voor diagrammen, tabelregels, grafiekassen en formulierdecoraties is dat precies wat u wilt, en de API erachter is klein genoeg om in één zitting te leren
Het hele tekenoppervlak leeft op THotPDF.CurrentPage. Tussen BeginDoc en EndDoc stelt u kleur en lijndikte in op dat paginaobject, legt u geometrie neer en roept u een tekenoperator aan om het vast te leggen. De vier primitieven die u het meest zult gebruiken, zijn MoveTo en LineTo voor willekeurige paden, Rectangle voor vakken, Circle voor schijven, en de twee schilderoperatoren Stroke en Fill
Het coördinatensysteem is linksonder
Dit is het enige dat iedereen laat struikelen die vanuit VCL komt. Het TCanvas waarmee u controls schildert, plaatst de oorsprong in de linkerbovenhoek waarbij Y naar beneden groeit. PDF doet het tegenovergestelde. HotPDF meet vanaf de linkerbenedenhoek van de pagina in punten (1/72 inch), waarbij Y toeneemt naarmate u omhoog gaat. Een punt op Y := 720 bevindt zich nabij de bovenkant van een US Letter-pagina, die 792 punten hoog is, en Y := 50 bevindt zich nabij de onderkant. Als uw eerste tekening verticaal gespiegeld verschijnt, komt dit hierdoor: code geporteerd van schermgrafiek veronderstelt de verkeerde richting en loopt van de onderrand af
Dezelfde conventie is van toepassing op TextOut, dus tekst en vormen delen één mentaal model zodra u het zich eigen maakt. Plan een lay-out door te beslissen waar de onderkant van elk element zich bevindt, niet de bovenkant, en de rest volgt vanzelf
Paden: MoveTo, LineTo, Stroke
Een omlijnd pad is een pen die is opgetild, geplaatst en gesleept. MoveTo tilt de pen op en stelt het startpunt in zonder iets te markeren. Elke LineTo breidt het huidige pad uit naar een nieuw punt. Er verschijnt niets op de pagina totdat u Stroke aanroept, dat het verzamelde pad tekent met behulp van de huidige omlijningskleur en lijndikte, en vervolgens het pad wist zodat de volgende MoveTo vers begint
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;
Twee details besparen echt debugging-tijd. Lijndikte is status, geen argument: SetLineWidth stelt het eenmaal in en elke daaropvolgende Stroke gebruikt die waarde totdat u deze opnieuw wijzigt, wat de reden is waarom de bovenstaande polylijn dikker is dan de regel. En het pad wordt na elke Stroke gereset, dus een vergeten Stroke betekent dat de geometrie die u zo zorgvuldig hebt opgebouwd helemaal nooit wordt weergegeven. Als er een vorm ontbreekt in de uitvoer, is de schilderaanroep de eerste plek om te kijken
De coördinaten zijn punten, en punten zijn fractioneel. MoveTo en LineTo accepteren Single waarden, dus een haarlijn van 0.5 punten of een positie op 72.25 is geldig en betekenisvol, en wordt niet afgerond naar de dichtstbijzijnde hele eenheid. Die precisie is in twee tegengestelde richtingen van belang. Een lijndikte van minder dan ongeveer 0.5 kan worden weergegeven als een apparaatafhankelijke kleinst mogelijke lijn die op het scherm verdwijnt en bij het afdrukken weer verschijnt, dus een zichtbare regel vereist een dikte die u met opzet instelt in plaats van de standaardwaarde. Aan de andere kant voorkomt het uitlijnen van tabelregels en rasterlijnen op hele puntcoördinaten dat een dicht raster er enigszins ongelijkmatig uitziet, waar aangrenzende lijnen anders zouden afronden. Bepaal de rasterafstand vooraf in punten en de rest van de lay-out neemt dit over
Gevulde vormen en kleur
Gesloten primitieven kunnen worden gevuld in plaats van omlijnd. Rectangle neemt een positie en een grootte, Circle neemt een middelpunt en een radius, en beide worden vastgelegd met Fill, dat het binnenste in de huidige vulkleur schildert, of met Stroke voor alleen een omlijning. Vulkleur en omlijningskleur zijn afzonderlijke statuselementen, ingesteld met SetRGBFillColor en SetRGBStrokeColor, die beide een enkele TColor aannemen. Dat betekent dat u Delphi's kleurconstanten en de RGB-helper direct kunt hergebruiken
// 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;
Let op de vorm van de argumenten bij Rectangle. Het is positie-plus-grootte, X, Y, Width, Height, niet twee tegenovergestelde hoeken. De TCanvas.Rectangle die Delphi-ontwikkelaars kennen neemt (Left, Top, Right, Bottom), dus door spiergeheugen zult u HotPDF een tweede hoek aanreiken waar het een breedte en een hoogte verwacht, en het vak krijgt de verkeerde grootte. Het (X, Y)-paar is de linkerbenedenhoek, consistent met de oorsprong van de pagina. Voor een cirkel is (X, Y) het middelpunt en het derde argument de straal in punten
Één kleurkeuze die het originele voorbeeld verkeerd had
Een oudere versie van dit voorbeeld voorzag kleuren van een beginwaarde met Random($FFFFFF) voor elke vorm. Het ziet er levendig uit, en het is het verkeerde instinct voor gegenereerde documenten. Een PDF die u via code bouwt, is meestal iets dat u ook wilt testen, en willekeurige vulkleuren maken de uitvoer onmogelijk te vergelijken van de ene run op de andere: een byte-voor-byte vergelijking (diff) tegen een bekend-goed bestand mislukt elke keer, zonder echte reden. Kies expliciete kleuren. Wanneer u variatie wilt in een reeks vormen, stuur dit dan aan vanuit uw gegevens of een vaste palet-array, zodat dezelfde invoer altijd hetzelfde bestand oplevert. Determinisme is meer waard dan originaliteit wanneer het artefact door een release-pijplijn beweegt
De primitieven samenvoegen: een callout-box
Elke primitief is op zichzelf eenvoudig; de opbrengst blijkt wanneer een handvol ervan samensmelt tot iets wat een rapport daadwerkelijk nodig heeft. Een callout, het geannoteerde vak dat naar een figuur wijst en dit uitlegt, gebruikt alles wat tot nu toe is behandeld: een gevulde rechthoek met een rand, een omlijnde aanwijzerlijn, een stip om de aanwijzer te verankeren, en tekst die binnen het vak is opgemaakt met dezelfde linksonder-coördinaten die de vormen gebruiken. FillAndStroke verdient hier zijn plek, door het binnenste en de omlijning van een pad in een enkele actie te schilderen in plaats van de rechthoek twee keer op te bouwen
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;
Merk op hoe weinig statusbeheer de compositie nodig heeft. Vulkleur, omlijningskleur en lijndikte worden elk direct vóór de vorm die ze gebruikt ingesteld, zodat elk blok van de tekening leest als een opzichzelfstaande eenheid en kan worden herschikt of geëxtraheerd naar een helper zonder verborgen status mee te slepen. Verpak dit in een procedure die het ankerpunt en de strings aanneemt, en u heeft een herbruikbare diagram-annotatie voor de moeite van veertig regels
Waar vector tekenen loont, en waar niet
Grijp naar deze pad- en vorm-aanroepen wanneer de geometrie wordt gegenereerd: rasterlijnen en balken van een grafiek, de gelinieerde regels van een factuurtabel, callout-boxen in een diagram, een logo uitgedrukt als een handvol paden. Dit alles schaalt zonder vervaging en voegt bijna niets toe aan de bestandsgrootte, want een rechthoek bestaat uit een paar getallen in plaats van duizenden pixels. De keerzijde is ook eerlijk. Als wat u daadwerkelijk heeft een foto of een screenshot is, teken het dan in plaats daarvan als een afbeelding met AddImage en ShowImage; een bitmap overtrekken met vector-aanroepen levert u niets op. De rechte segmenten, rechthoeken en cirkels hierboven dragen de grote meerderheid van het echte rapportagewerk, en de drie verfijningen waar ontwikkelaars als volgende naar vragen, krommen, stippelpatronen en transparantie, bevinden zich op hetzelfde paginaobject
Krommen, stippellijnen en transparantie in het kort
Vrije-vorm krommen (curves) breiden hetzelfde padmechanisme uit dat u al heeft. CurveToC(X1, Y1, X2, Y2, X3, Y3) voegt een kubisch Bezier-segment toe van het huidige punt naar (X3, Y3), buigend naar de twee controlepunten, en de steno-varianten CurveToV en CurveToY dekken de gevallen waarbij één controlepunt samenvalt met een eindpunt. Een pad kan LineTo en CurveToC segmenten vrijelijk mixen voordat een enkele Stroke of Fill het vastlegt, wat de manier is waarop afgeronde hoeken en vloeiende grafieklijnen worden gebouwd
Onderbroken omlijningen (dashes) zijn status, precies zoals lijndikte. SetDash([3, 3], 0) schakelt elke daaropvolgende omlijning over naar een drie-punten-aan, drie-punten-uit patroon, waarbij de array de aan/uit-runlengtes in punten specificeert en het tweede argument faseert waar de cyclus start; NoDash keert de pen terug naar een ononderbroken lijn. Stel het in, trek de rasterlijnen die het nodig hebben, en reset het voor de volgende ononderbroken regel, anders infecteert het stippelpatroon stilletjes alles wat volgt
Transparantie verloopt via een benoemde grafische status in plaats van een kleurargument, omdat alfa in PDF een eigenschap is van de graphics state dictionary. Registreer er een op het document met RegisterExtGState, waarbij u een vul-alfa en een omlijnings-alfa tussen 0 en 1 doorgeeft, en pas vervolgens de naam die dit retourneert toe met CurrentPage.SetGraphicsState; vullingen en omlijningen vanaf dat punt worden geschilderd met de geregistreerde dekking. Het is een zwaardere ceremonie dan de kleurinstellingen, maar is het waard de eerste keer dat een markeerbalk over tekst heen moet liggen zonder deze te verbergen
De resterende gewoonte die het waard is om te behouden, is verificatie. Gegenereerde geometrie kan op uw machine slagen en bij een klant falen, meestal vanwege lettertype-substitutie in tekst die u erin mengt, of een aanname over het paginaformaat die geen standhoudt. Open het voltooide bestand op een paar zoomniveaus om te bevestigen dat de randen strak blijven, en controleer of elke vorm binnen het bedoelde margekader landt. Met een deterministisch kleurenschema kan die controle worden geautomatiseerd tegen een referentie-PDF in plaats van op het oog te worden beoordeeld
De hier getoonde MoveTo, LineTo, Stroke, Fill en kleur-aanroepen maken deel uit van de HotPDF Component voor Delphi en C++Builder