Å generere en rapport koker ned til å plassere tre ting på en side og få dem til å bli enige om hvor de sitter: tekst ved kjente koordinater, skrifter som rendres likt på serveren som på skrivebordet ditt, og bilder tilpasset til å passe. Alt annet et rapportbibliotek gjør, er arrangert rundt disse tre. HotPDF, losLabs PDF-genereringsbibliotek for Delphi og C++Builder, gir deg hver av dem som et direkte kall på sideobjektet, og den eneste reelle friksjonen er koordinatsystemet under, som løper motsatt vei av VCL-canvaset du er vant til. Få den orienteringen på plass først, så slutter resten av layoutarbeidet å kjempe imot deg
Tekstplassering og origo nede til venstre
Nesten alles første rapport kommer ut opp-ned. Tittelen havner nær bunnkanten, og hver linje under den klatrer mot toppen. Ingenting feiler. PDF user space, definert i ISO 32000-1 §8.3, plasserer origo i nedre venstre hjørne med Y voksende oppover, som er speilbildet av GDI-canvaset der Y vokser nedover fra øverst til venstre. Fem minutter brukt på å gjøre fred med det, sparer en layout du ellers ville skrevet om når tallene slutter å gi mening
Sideobjektets sentrale kall er TextOut(X, Y, Angle, Text). X og Y plasserer teksten i punkter fra nedre venstre hjørne, og Angle roterer den i grader, som er hvordan et diagonalt DRAFT- eller COPY-stempel tegnes uten noen spesiell støtte. Trikset som lar VCL-trent intuisjon fortsette å fungere, er å uttrykke Y som sidehøyden minus avstanden du vil ha fra toppen:
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'invoice-0001.pdf';
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', [fsBold], 16);
Pdf.CurrentPage.TextOut(50, 792 - 50, 0, 'INVOICE'); // 50pt fra toppen av Letter
Pdf.CurrentPage.SetFont('Arial', [], 10);
Pdf.CurrentPage.TextOut(50, 792 - 70, 0, 'Date: 2026-06-11');
Pdf.CurrentPage.TextOut(300, 400, 45, 'COPY'); // rotert stempel
Pdf.AddPage; // CurrentPage peker nå hit
Pdf.CurrentPage.SetFont('Arial', [], 10); // skrifttilstanden overføres ikke
Pdf.CurrentPage.TextOut(50, 742, 0, 'Page 2 detail rows');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
De to tilstandsavhengige atferdene i den oppføringen står bak de fleste feilene som bare dukker opp på side to. AddPage peker CurrentPage om til siden den nettopp opprettet, så en sidereferanse du hurtiglagret tidligere, tegner ikke lenger der du forventer. Skriftvalg er også per side snarere enn per dokument. Hopper du over SetFont etter en AddPage, faller den første TextOut på den ferske siden tilbake til hva enn standard siden startet med, ikke den fete overskriftsskriften du satte tre sider tidligere. Den trygge vanen er å behandle «start en ny side» og «gjenopprett tekststilstanden» som ett uatskillelig steg i rapportløkken
Skrifter som finnes på serveren, ikke bare skrivebordet ditt
De fleste skriftproblemer er egentlig distribusjonsproblemer i forkledning. Utviklingsmaskinen din har bedriftsskriften installert, så rapporten ser riktig ut på skjermen din og sendes ut. Produksjonsverten kjører jobben under en tjenestekonto som aldri har hatt den skriften installert, rendreren erstatter stille noe den kan finne, og det første noen hører om det, er en kunde som spør hvorfor brevhodet endret seg. Utveien er å slutte å stole på OS-ens skriftkatalog og i stedet laste skriften fra en fil installatøren din legger på disk. HotPDFs Unicode-registreringskall tar imot en sti og gjør nøyaktig det:
Pdf.RegisterUnicodeTTF('C:\ProgramData\MyApp\Fonts\NotoSans.ttf');
Pdf.CurrentPage.SetFont('NotoSans', [], 12);
Pdf.CurrentPage.TextOut(50, 700, 0, WideString('Łódź - Ünïcode test ✓'));
TextOut tar imot en WideString direkte, noe som betyr mer enn det ser ut ved første øyekast. Et kundenavn med en aksent, en tysk gate, en polsk by: dette er ikke kantsaker, det er det normale innholdet i en kundetabell, og de går gjennom det samme kallet som ASCII-etikettene du hardkoder, så lenge den registrerte skriften faktisk inneholder glyfene. Én versjonsbegrensning følger med innebygde skrifter: dokumentet må være PDF 1.5 eller nyere, så hvis et urelatert krav fester deg til en eldre versjon, er det den tingen som stille vil gå i stykker. Skrift som går fra høyre til venstre, som arabisk og hebraisk, trenger ekte shaping snarere enn et rett glyf-oppslag, og det har sin egen pipeline; se artikkelen vår om kompleks skrift-shaping med HotPDF
Når ingen installert skrift kan uttrykke det du trenger, tenk MICR-tegn på en sjekk eller et proprietært symbolsett, fyller Type 3-skrifter gapet. Du definerer hver glyf som en liten innholdsstrøm gjennom RegisterType3Font og AddType3Glyph. Det er et spesialisert hjørne av API-et, og du vil sjelden gripe til det, men det er langt renere enn å spre hundrevis av små symbolbitmaps utover en side
Bilder: de midterste argumentene er en bredde og høyde, ikke et hjørne
Bildehåndtering deles i to steg, og å holde dem atskilt er hele poenget. AddImage tar imot et TBitmap eller TJPEGImage, bygger det inn én gang, og gir tilbake en indeks. PNG-grafikk må dekodes til et bitmap før den kommer dit. ShowImage tegner deretter den indeksen hvor som helst og så ofte du vil. Argumentrekkefølgen på ShowImage er det ene stedet det er verdt å senke farten og lese nøye:
var
Png: TPngImage;
Logo: TBitmap;
LogoIdx: Integer;
begin
Png := TPngImage.Create;
Logo := TBitmap.Create;
try
Png.LoadFromFile('brand-logo.png');
Logo.Assign(Png); // dekod PNG til et bitmap
LogoIdx := Pdf.AddImage(Logo, icFlate); // tapsfri for flatfarget grafikk
finally
Logo.Free;
Png.Free;
end;
// (Index, X, Y, Width, Height, Angle): ikke (X1, Y1, X2, Y2)
Pdf.CurrentPage.ShowImage(LogoIdx, 50, 700, 120, 40, 0);
end;
De to tallene etter posisjonen er en bredde og en høyde. De er ikke koordinatene til det motsatte hjørnet, og det siste argumentet er en rotasjonsvinkel i grader. Leser du signaturen som en X1/Y1/X2/Y2-boks, strekker en 120-ganger-40-logo plassert på (50, 700) seg i stedet derfra til (120, 40), og sprer seg over det meste av siden. Outputen gjør feilen åpenbar mens kildekoden ser helt fornuftig ut, noe som er det som gjør at den sløser bort en ettermiddag. KeepImageAspectRatio er som standard True, så en boks med feil proporsjoner brevkasser bildet i stedet for å forvrenge det; slå den til False bare når du faktisk mener å strekke
Oppdelingen mellom å registrere og å plassere lønner seg over lange kjøringer. Fordi AddImage bygger inn pikslene én gang, og hver ShowImage med den indeksen peker tilbake på det samme innebygde objektet, avgjør hvor du kaller AddImage, filstørrelsen. Kaller du den inne i sideløkken for en 500-siders kontoutskrift, blir den samme logoen bygget inn 500 ganger. Kaller du den én gang før løkken og beholder indeksen, blir logoen lagret én gang. En liten dictionary nøklet på ressursstien er nok til å sikre at hvert distinkte bilde registreres nøyaktig én gang
Valget av kodek er den andre størrelsesspaken. Fotografisk innhold, skannede vedlegg og liknende, hører hjemme i JPEG: send icJpeg til AddImage og senk JpegQuality til rundt 85, siden egenskapen starter på 100, og forskjellen ved 85 er usynlig på en utskrevet side. Flatfarget grafikk som logoer, diagrammer og strektegninger hører hjemme i icFlate, der tapsfri komprimering allerede er kompakt, og JPEG ville sørget for synlig ringing rundt de harde kantene. En kontoutskriftskjøring som presser ett fullkvalitetsfoto inn på hver side, kan ballongere til gigabyte; det samme innholdet ved JPEG 85 lander på rundt en tiendedel av størrelsen, og ingen leser kan se forskjellen
Streker, bokser og skyggelegging med baneprimitiver
Den horisontale linjen under en tabelloverskrift og den grå boksen bak et sumtall trenger ikke være bilder. Tegn dem som vektorer, så forblir de skarpe ved enhver zoom, skriver skarpt ut, og legger til nesten ingenting på filen. HotPDF følger den samme modellen rå PDF-innholdsstrømmer bruker: bygg en bane, kall deretter en operator som maler den
// Horisontal strek under tabelloverskriften
Pdf.CurrentPage.SetLineWidth(0.75);
Pdf.CurrentPage.MoveTo(50, 660);
Pdf.CurrentPage.LineTo(545, 660);
Pdf.CurrentPage.Stroke;
// Skyggelagt sumboks: X, Y, bredde, høyde
Pdf.CurrentPage.SetRGBFillColor(RGB(235, 235, 235));
Pdf.CurrentPage.Rectangle(395, 120, 150, 40);
Pdf.CurrentPage.Fill;
Rekkefølgen er ikke valgfri: sett malingstilstanden, konstruer banen, kall deretter Stroke eller Fill. En bane du bygger, men aldri maler, bidrar med ingenting til siden, noe som nesten alltid er svaret når en strek «ikke dukker opp». SetRGBFillColor tar imot en enkelt TColor, så de kjente VCL-konstantene som clNavy og clBlack passer rett inn, og Rectangle bruker de samme bredde-og-høyde-argumentene som bildeplassering, i stedet for to hjørner. Én advarsel om tynne linjer: alt under omtrent en halv punkt kan se elegant ut på en skjerm og deretter forsvinne på en 600 dpi kontorskriver, så 0,75pt er et rimelig gulv for enhver strek som må overleve å bli trykt
Paginering mot ekte data, ikke eksempeldata
Én detalj å få riktig før layouten setter seg: numeriske kolonner bør høyrejusteres, og måten å gjøre det på, er å måle den rendrede bredden til hver verdi og posisjonere den tilbake fra kolonnegrensen, ikke å polstre strengen med ledende mellomrom. Mellomromspolstring stemmer bare i en fastbreddeskrift, og ingen setter en finansiell rapport i en fastbreddeskrift. Kjør verdiene gjennom Delphis lokaliseringsbevisste rutiner som FormatFloat først, slik at tusenskilletegnet du måler bredden av, er det samme som kundens locale faktisk vil vise
Faren med paginering er at du skriver den mot demo-datasettet, der ti korte rader passer på én side, og løkken aldri må bryte. Produksjon gir deg en kunde hvis firmanavn strekker seg over 140 tegn og en kontoutskrift med 4000 linjeposter, og nå må løkken bryte riktig hver eneste gang. Mønsteret som holder stand, er én enkelt Y-markør som beveger seg nedover mens du trekker fra hver rads høyde, og en sjekk som starter en ny side i det øyeblikket markøren ville krysset bunnmargen. Nedover betyr her synkende Y, som er det ene stedet der origo nede til venstre forblir kontraintuitiv. Hold alt dette i én rutine som også utsteder SetFont på nytt og tegner den løpende overskriften på nytt på den nye siden, så får aldri av-med-én-side-feilene fotfeste. Når de samme rapportene også må oppfylle arkiv- eller tilgjengelighetsregler, er valgene du tar akkurat her, hvilke skrifter du bygger inn, om outputen er tagget, hvilke fargerom du bruker, de samme valgene disse standardene håndhever; HotPDF-guiden til PDF/A, PDF/X og PDF/UA er verdt å lese før malen fastner
Hvert kall vist her, tekstposisjonering, skriftregistrering, bildeinnbygging og banetegning, følger med i HotPDF Delphi Component for Delphi og C++Builder, hvis referanse dokumenterer det fullstendige output-API-et sammen med skjema-, kryptering- og signeringsfunksjonene det står ved siden av