PDFium har et rykte som en visningsmotor, gjengiveren bak Chromes PDF-fane, så det første å oppklare er at PDFium-komponenten også kan bygge et dokument som aldri har eksistert før. Forfattersiden pakker inn PDFiums side-objekt API: du lager et tomt dokument, legger til sider med eksplisitte dimensjoner, og slipper tekst, vektorbaner og bilder på hver side på koordinater du velger. Det er ingen sidebeskrivelsesspråk å lære og ingen utskriftsdriver i løkken. Du kaller metoder, biblioteket setter sammen PDF-objekter, og SaveAs serialiserer resultatet
Det du ikke får er en layoutmotor. Dette betyr nok til å si det på forhånd, fordi det former hvert eksempel nedenfor. PDFium-komponenten plasserer innhold der du forteller det å gjøre det, i absolutte koordinater, og ingen andre steder. Den vil ikke bryte et avsnitt, flyte tekst over et sideskift eller beregne en tabell fra rader og kolonner. Det er din jobb. Hvis du kom med forventninger om noe som flyter om prosa slik et tekstbehandlingsprogram gjør det, kalibrer nå: dette er et presist API for plassering på lavt nivå, nærmere det å tegne på et lerret enn å sette et dokument. For genererte fakturaer, sertifikater, etiketter og rapportsider der du allerede vet hvor hvert element hører hjemme, er den presisjonen akkurat det du ønsker
Minimumet som produserer en fil
Tre kall står mellom en tom TPdf og en lagret PDF: opprett dokumentet, legg til en side, skriv det ut. Alt annet er innhold du legger inn imellom
uses
Vcl.Graphics, // for clBlack and TColor
PDFium; // TPdf lives here
procedure CreateBlankPdf(const FileName: string);
var
Pdf: TPdf;
begin
Pdf := TPdf.Create(nil);
try
Pdf.CreateDocument; // empty in-memory document
Pdf.AddPage(0, 595, 842); // A4 portrait, in points
Pdf.AddText('First page', 'Arial', 18, 50, 780);
Pdf.SaveAs(FileName); // serialize to disk
finally
Pdf.Active := False;
Pdf.Free;
end;
end;
Én detalj feller folk som har sett eldre kodesnutter: du tildeler ikke Pdf.Active := True etter CreateDocument. Egenskapen Active rapporterer om et dokumenthåndtak eksisterer, og CreateDocument har allerede opprettet ett, så egenskapen er sann i det øyeblikket det kallet returnerer. Å sette den på nytt er en nulloperasjon i beste fall og villedende for neste leser i verste fall. Active gjør jobben sin på vei ut: å tilordne False frigjør det underliggende dokumentet før Free, som er den rene nedrivningsrekkefølgen. Behandle CreateDocument og en fillastende åpning som gjensidig utelukkende. Biblioteket nekter å opprette et nytt dokument på en TPdf som allerede har ett åpent, så gjenbruk betyr å lukke gjeldende dokument først
Koordinater starter nederst til venstre
Det andre argumentparet til AddText, og til hvert plasseringskall, er et punkt i PDF-brukerrommet. Opprinnelsen sitter nederst i venstre hjørne av siden, X går mot høyre, og Y går opp. Én enhet er ett punkt, 1/72 av en tomme, så en A4-side er 595 ganger 842 enheter og US Letter er 612 ganger 792. Denne oppovergående Y-en er den desidert vanligste kilden til forvirring av typen "teksten min er utenfor siden", fordi skjerm- og bitkartkoordinater plasserer opprinnelsen øverst med Y som vokser nedover. På en side som er 842 punkter høy, sitter en overskrift nær toppen rundt Y 780, ikke Y 60. Når en kjøring lander et uventet sted, er sidehøyden minus Y-en din nesten alltid det tallet du egentlig mente
AddPage tar en innsettingsposisjon som sitt første argument, uttrykt én-basert, med 0 som en praktisk stenografi for "start av dokument". Send 0 eller 1 for den første siden, og siden settes inn foran; send verdien som samsvarer med opptellingen du legger til for å legge til på slutten. Den nylig tillagte siden blir også den gjeldende siden, den som etterfølgende tegnekall retter seg mot, så det er ikke noe eget "velg denne siden"-trinn etter å ha lagt den til. Hvis du legger til flere sider og senere trenger å tegne tilbake på en tidligere en, sett PageNumber for å flytte markøren; mens du fyller sider i rekkefølge etter hvert som du oppretter dem, kan du la den være i fred
Skrive tekst, og skrifttyperegelen som biter i det stille
AddText-signaturen bærer alt en enkelt kjøring trenger: strengen, et skrifttypenavn, en størrelse i punkter, X- og Y-ankeret, deretter valgfri farge, en alfa-byte for åpenhet (transparency), og en rotasjonsvinkel i grader
procedure WriteHeader(Pdf: TPdf; const Title, Author: string);
begin
// Title in black, default opacity, no rotation
Pdf.AddText(Title, 'Arial', 20, 50, 780);
// A lighter byline 24 points below it
Pdf.AddText('By ' + Author, 'Arial', 11, 50, 756, clGray);
// A faint diagonal draft stamp across the page
Pdf.AddText('DRAFT', 'Arial', 64, 180, 380, clGray, $30, 45.0);
end;
Alfa-byten går fra $00 (usynlig) til $FF (ugjennomsiktig), noe som gjør utkaststempelet til et vannmerke i stedet for en solid blokk: $30 er omtrent nitten prosent opasitet, nok til å lese gjennom. Vinkelen roterer kjøringen mot klokken rundt ankeret sitt, så 45 grader gir det klassiske hjørne-til-hjørne-stempelet. Ingenting av dette trenger en egen vannmerkefunksjon. Et vannmerke er bare et stort, halvgjennomsiktig, rotert AddText-kall, og å tegne det før eller etter brødteksten avgjør om det sitter bak eller oppå innholdet
Skrifttyper fortjener en nøye setning, fordi feilmodusen er stille. Når du sender et skrifttypenavn, ber PDFium-komponenten operativsystemet om den skrifttypens TrueType-data og bygger den inn i dokumentet, som er grunnen til at en fil bygget på din maskin gjengis identisk på en som aldri har hatt skrifttypen installert. Haken er hva som skjer når navnet ikke løses opp: en skrivefeil, eller et snitt som rett og slett ikke er til stede på byggemaskinen. Det er ikke noe unntak. Biblioteket faller tilbake til å opprette et tekstobjekt som bærer navnet kun som en etikett, med ingenting innebygd, og overlater til visningsprogrammet å erstatte med hva enn det anser som nært. Teksten vises i testene dine, ser plausibel ut, og forskyver metrikk eller glyfer i det øyeblikket filen åpnes et sted med forskjellige skrifttyper installert. Bruk navn du vet er til stede på den genererende maskinen, behandle skrifttypelisten som en distribusjonsavhengighet, og åpne en prøve i et visningsprogram på et rent system før du stoler på utdataene
Vektorformer: bygg en bane, og forplikt den
Linjer, rektangler og fylte områder går gjennom en bane. Du åpner en med CreatePath, som setter startpunktet og all stilsetting på én gang, fyllmodus, fyll- og strekfarger med sine egne alfa-byter, strekbredde, linjeender og -sammenføyninger. Så forlenger du den med LineTo, BezierTo og ClosePath, og til slutt forplikter (commits) AddPath den ferdige banen inn på siden. Forpliktelsestrinnet er lett å glemme og produserer ingenting hvis du hopper over det
procedure DrawDivider(Pdf: TPdf; X, Y, Width: Single);
begin
// A thin horizontal rule. The rectangle overload sets a box directly:
// X, Y, Width, Height, then fill mode and colors.
Pdf.CreatePath(X, Y, Width, 0.5, fmNone, clBlack, $FF,
True, clBlack, $FF, 1.0);
Pdf.AddPath;
end;
procedure DrawTriangle(Pdf: TPdf);
begin
// Point overload: start at the first vertex, line to the rest, close.
Pdf.CreatePath(200, 300, fmWinding, clBlue, $80, True, clNavy, $FF, 2.0);
Pdf.LineTo(300, 300);
Pdf.LineTo(250, 400);
Pdf.ClosePath;
Pdf.AddPath; // nothing is drawn until this runs
end;
To overbelastninger dekker de vanlige tilfellene. Firkordinatformen tar X, Y, bredde og høyde og gir deg et aksejustert rektangel i ett kall, som er det du strekker deg etter for å tegne en regel, en cellekant eller et fylt bakgrunnspanel. Tokoordinatformen setter bare et startpunkt, og du sporer resten av omrisset selv med LineTo og BezierTo. Fyllmodus styrer hvordan overlappende områder males: fmWinding (vinding forskjellig fra null) passer til de fleste solide former, fmAlternate (partall-oddetall) håndterer utskjæringer og selvkryssende omriss, og fmNone etterlater en kun-streket bane uten fyll, som er det skillelinjen ovenfor bruker
Tabeller er baner og tekst, satt sammen for hånd
Fordi det ikke er noen tabell-primitiv, er en tabell en løkke. Du bestemmer kolonnenes X-forskyvninger og radhøyden, skriver hver celle med AddText, og tegner reglene med rektangelbaner. Aritmetikken er din, men den er grei, og når den først er skrevet, generaliserer den til hvilket som helst rutenett du trenger
procedure DrawTable(Pdf: TPdf; Left, Top: Double);
const
ColX: array[0..2] of Double = (0, 110, 210); // column offsets
RowH = 20;
var
Y: Double;
Row: Integer;
begin
// Header row
Pdf.AddText('Item', 'Arial', 10, Left + ColX[0], Top);
Pdf.AddText('Qty', 'Arial', 10, Left + ColX[1], Top);
Pdf.AddText('Price', 'Arial', 10, Left + ColX[2], Top);
// Rule under the header
Pdf.CreatePath(Left, Top - 5, 260, 0.5, fmNone, clBlack, $FF);
Pdf.AddPath;
// Data rows, stepping Y downward each iteration
Y := Top;
for Row := 1 to 3 do
begin
Y := Y - RowH;
Pdf.AddText('Item ' + IntToStr(Row), 'Arial', 9, Left + ColX[0], Y);
Pdf.AddText(IntToStr(Row * 2), 'Arial', 9, Left + ColX[1], Y);
Pdf.AddText('$' + IntToStr(Row * 10) + '.00', 'Arial', 9, Left + ColX[2], Y);
end;
end;
Legg merke til at Y trapper nedover med radhøyden hvert pass, igjen fordi opp er positivt. Det er også her fraværet av tekstmåling viser seg: ingenting stopper et langt varenavn fra å flyte over i neste kolonne, fordi biblioteket ikke vet hvor bred strengen din ble gjengitt. For utdata med fast format der du kontrollerer dataene, dimensjonerer du kolonner raust og går videre. For genuint variabelt innhold, enten begrenser du inndata eller måler glyfbredder selv før du plasserer dem, som er punktet der et dedikert komposisjonsbibliotek begynner å betale for seg
Bilder og flere sider
Rasterinnhold kommer inn gjennom bildehjelperne. AddPicture tar en innlastet TPicture og plasserer den i et punkt, med en valgfri bredde og høyde for å skalere den; AddImage aksepterer en filsti eller en TBitmap direkte, og AddJpegImage strømmer JPEG-bytes uten en rundtur gjennom et bitkart. Som med alt annet er plasseringskoordinatene nederste venstre hjørne av bildet i brukerrommet, og bredden og høyden er størrelsen på siden i punkter, ikke pikseldimensjonene til kilden
procedure CreateMultiPageReport(const FileName: string; PageCount: Integer);
var
Pdf: TPdf;
P: Integer;
begin
Pdf := TPdf.Create(nil);
try
Pdf.CreateDocument;
for P := 1 to PageCount do
begin
Pdf.AddPage(P, 595, 842); // append; the new page becomes current
Pdf.AddText('Page ' + IntToStr(P) + ' of ' + IntToStr(PageCount),
'Arial', 10, 50, 30); // footer near the bottom edge
// ... draw this page's body here ...
end;
Pdf.SaveAs(FileName);
finally
Pdf.Active := False;
Pdf.Free;
end;
end;
Et flersidig dokument er enkeltsidemønsteret i en løkke. Hver AddPage legger til en side og gjør den gjeldende, så brødteksten og bunnteksten du tegner deretter lander på siden du nettopp la til. Du tildeler ikke PageNumber på nytt inni denne løkken, fordi å legge til en side allerede flyttet markøren dit; du trenger bare PageNumber når du går tilbake til en side utenfor opprettelsesrekkefølgen. Kall SaveAs én gang på slutten, etter at den siste siden er fylt. Hvis du trenger en arkivprofil i stedet for en vanlig fil, eksponerer det samme dokumentobjektet SaveAsPdfA og de andre samsvarsvariantene, så valget av utdatastandard er et annet lagringskall, ikke en annen byggevei
Hvor dette passer inn
Den ærlige innrammingen er at PDFium-komponentens forfatter-API er et trofast, tynt lag over PDFiums side-objekt-modell: ekte dokumentopprettelse, ekte innebygde skrifttyper, ekte vektor- og rasterinnhold, serialisert til en standardsamsvarende fil. Den er ikke, og utgir seg ikke for å være, en omflytende dokumentmotor. Skillelinjen er tekstlayout. Hvis utdataene dine er malbaserte, fakturaer, sertifikater, etiketter, dashbord gjengitt til et fast rutenett, er den absolutte koordinatmodellen direkte og rask, og koden forblir lesbar. Hvis utdataene dine er langform prosa som må brytes og pagineres på egen hånd, vil du bygge opp en layoutmotor på toppen av disse kallene, og det er feil verktøy for jobben. Å vite hvilken side av den linjen du befinner deg på er mesteparten av avgjørelsen
Opprettelsesmetodene beskrevet her er en del av PDFium-komponenten for Delphi, som kobler denne forfatterveien sammen med gjengivelses- og tekstutvinningsfunksjonene PDFium er bedre kjent for