PDFium har ry for at være en fremvisningsmotor, gengiveren bag Chromes PDF-fane, så det første, der skal slås fast, er, at PDFium Component også kan bygge et dokument, der aldrig har eksisteret før. Oprettelsesdelen indpakker PDFiums side-objekt API: du laver et tomt dokument, tilføjer sider med eksplicitte dimensioner, og placerer tekst, vektorstier og billeder på hver side ved de koordinater, du vælger. Der er intet sidebeskrivelsessprog at lære og ingen printerdriver involveret. Du kalder metoder, biblioteket samler PDF-objekter, og SaveAs serialiserer resultatet
Det, du ikke får, er en layoutmotor. Dette er vigtigt nok til at sige på forhånd, fordi det former hvert eksempel nedenfor. PDFium Component placerer indhold, hvor du beder det om, i absolutte koordinater, og ingen andre steder. Det vil ikke ombryde et afsnit, lade tekst flyde over et sideskift eller beregne en tabel ud fra rækker og kolonner. Det er din opgave. Hvis du ankom og forventede noget, der ombryder prosa på samme måde som et tekstbehandlingsprogram gør, så kalibrer nu: dette er et præcist, lavniveau placerings-API, tættere på at tegne på et lærred end at sætte et dokument op. Til genererede fakturaer, certifikater, etiketter og rapportsider, hvor du allerede ved, hvor hvert element hører til, er den præcision præcis, hvad du ønsker
Det mindste, der producerer en fil
Tre kald står mellem et tomt TPdf og en gemt PDF: opret dokumentet, tilføj en side, skriv det ud. Alt andet er indhold, du lægger indimellem
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;
En detalje spænder ben for folk, der har set ældre kodestumper: du tildeler ikke Pdf.Active := True efter CreateDocument. Egenskaben Active rapporterer, om der findes et dokumenthåndtag, og CreateDocument har allerede oprettet et, så egenskaben er sand i det øjeblik kaldet returnerer. At sætte den igen er i bedste fald en tom handling og i værste fald vildledende for den næste læser. Active tjener sig selv ind på vej ud: at tildele falsk frigiver det underliggende dokument før Free, hvilket er den rene nedrivningsrækkefølge. Behandl CreateDocument og en filindlæsende åbning som gensidigt udelukkende. Biblioteket nægter at oprette et nyt dokument på et TPdf, der allerede har et åbent, så genbrug betyder, at det aktuelle dokument skal lukkes først
Koordinater starter nederst til venstre
Det andet argumentpar til AddText, og til hvert placeringskald, er et punkt i PDF-brugerrummet. Nulpunktet (origo) sidder i nederste venstre hjørne af siden, X løber mod højre, og Y løber opad. Én enhed er ét punkt, 1/72 af en tomme, så en A4-side er 595 gange 842 enheder og US Letter er 612 gange 792. Den opadgående Y er den absolut mest almindelige kilde til forvirringen om, at "min tekst er uden for siden", fordi skærm- og bitmapkoordinater sætter nulpunktet i toppen med Y voksende nedad. På en 842-punkt høj side sidder en overskrift nær toppen omkring Y 780, ikke Y 60. Når et forløb lander et uventet sted, er sidehøjden minus din Y næsten altid det tal, du egentlig mente
AddPage tager en indsættelsesposition som sit første argument, udtrykt som 1-baseret, med 0 som en praktisk "start af dokument"-forkortelse. Videregiv 0 eller 1 for den første side, og siden indsættes forrest; videregiv den værdi, der matcher det antal, du tilføjer til, for at tilføje i slutningen. Den nyligt tilføjede side bliver også den aktuelle side, den som efterfølgende tegnekald målretter mod, så der er intet separat "vælg denne side"-trin efter tilføjelsen. Hvis du tilføjer flere sider og senere har brug for at tegne tilbage på en tidligere, skal du sætte PageNumber for at flytte markøren; mens du fylder sider i rækkefølge, som du opretter dem, kan du lade det være i fred
Skrivning af tekst, og skrifttypereglen, der bider tavst
Signaturen for AddText bærer alt, hvad en enkelt kørsel har brug for: strengen, et skrifttypenavn, en størrelse i punkter, X- og Y-ankeret, og derefter valgfri farve, en alfa-byte til gennemsigtighed og en rotationsvinkel 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 løber fra $00 (usynlig) til $FF (uigennemsigtig), hvilket er det, der gør udkast-stemplet til et vandmærke i stedet for en solid blok: $30 er omtrent nitten procents opacitet, nok til at læse igennem. Vinklen roterer forløbet mod uret omkring dets anker, så 45 grader giver det klassiske hjørne-til-hjørne stempel. Intet af dette kræver en separat vandmærkefunktion. Et vandmærke er bare et stort, halvgennemsigtigt, roteret AddText-kald, og om det tegnes før eller efter brødteksten, afgør, om det sidder bagved eller oven på indholdet
Skrifttyper fortjener en omhyggelig sætning, fordi fejltilstanden er stille. Når du videregiver et skrifttypenavn, beder PDFium Component styresystemet om den skrifttypes TrueType-data og indlejrer den i dokumentet, hvilket er grunden til, at en fil, der er bygget på din maskine, gengives identisk på en, der aldrig har haft skrifttypen installeret. Hagen er, hvad der sker, når navnet ikke kan fortolkes: en slåfejl, eller et skrifttype, der simpelthen ikke er til stede på byggemaskinen. Der kastes ingen undtagelse. Biblioteket falder tilbage til at oprette et tekstobjekt, der kun bærer navnet som en etiket, uden noget indlejret, og overlader til fremviseren at erstatte med noget, den anser for tæt på. Teksten vises i dine tests, ser plausibel ud, og forskyder metrikker eller glyffer i det øjeblik, filen åbnes et sted, hvor der er installeret andre skrifttyper. Brug navne, du ved, er til stede på den genererende maskine, behandl skrifttypelisten som en afhængighed ved implementering, og åbn et eksempel i en fremviser på et rent system, før du stoler på outputtet
Vektorformer: byg en sti, og bekræft den derefter
Linjer, rektangler og udfyldte områder går gennem en sti. Du åbner en med CreatePath, som sætter startpunktet og hele stylingen på én gang, udfyldningstilstand, udfyldnings- og stregfarver med deres egne alfa-bytes, stregbredde, linjeafslutninger og samlinger. Derefter udvider du den med LineTo, BezierTo og ClosePath, og til sidst bekræfter AddPath den færdige sti på siden. Bekræftelsestrinnet er let at glemme og producerer intet, hvis du springer det over
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 dækker de almindelige tilfælde. Formen med fire koordinater tager X, Y, bredde og højde og giver dig et aksejusteret rektangel i ét kald, hvilket er det, du griber til for at tegne en linje, en cellekant eller et udfyldt baggrundspanel. Formen med to koordinater sætter kun et startpunkt, og du sporer resten af omridset selv med LineTo og BezierTo. Udfyldningstilstanden styrer, hvordan overlappende områder males: fmWinding (nonzero winding) passer til de fleste faste former, fmAlternate (even-odd) håndterer udskæringer og selvskærende omrids, og fmNone efterlader en sti, der kun er streget uden udfyldning, hvilket er det, adskilleren ovenfor bruger
Tabeller er stier og tekst, samlet i hånden
Fordi der ikke findes nogen tabel-primitiv, er en tabel en løkke. Du bestemmer kolonne-X-forskydningerne og rækkehøjden, skriver hver celle med AddText og tegner linjerne med rektangelstier. Aritmetikken er din egen, men den er ligetil, og når den først er skrevet, generaliseres den til ethvert gitter, du har brug for
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;
Læg mærke til, at Y træder nedad med rækkehøjden for hver gennemgang, igen fordi opad er positiv. Dette er også det sted, hvor fraværet af tekstopmåling viser sig: intet forhindrer et langt elementnavn i at løbe over i den næste kolonne, fordi biblioteket ikke ved, hvor bred din streng blev gengivet. For output med fast format, hvor du kontrollerer dataene, dimensionerer du kolonnerne generøst og går videre. For ægte variabelt indhold begrænser du enten input eller måler glyfbredder selv, før du placerer dem, hvilket er det punkt, hvor et dedikeret kompositionsbibliotek begynder at betale sig selv
Billeder og flere sider
Rasterindhold kommer ind via billedhjælperne. AddPicture tager en indlæst TPicture og placerer den på et punkt, med en valgfri bredde og højde for at skalere den; AddImage accepterer en filsti eller en TBitmap direkte, og AddJpegImage streamer JPEG-bytes uden at skulle forbi en bitmap. Som med alt andet er placeringskoordinaterne det nederste venstre hjørne af billedet i brugerrummet, og bredden og højden er størrelsen på siden i punkter, ikke pixeldimensionerne for 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 flersidet dokument er enkeltsidemønsteret i en løkke. Hver AddPage tilføjer en side og gør den aktuel, så den brødtekst og sidefod, du tegner næste gang, lander på den side, du lige har tilføjet. Du skal ikke omdele PageNumber inde i denne løkke, fordi tilføjelse af en side allerede har flyttet markøren dertil; du har kun brug for PageNumber, når du går tilbage til en side ude af oprettelsesrækkefølge. Kald SaveAs én gang til sidst, når den sidste side er fyldt. Hvis du har brug for en arkivprofil frem for en almindelig fil, eksponerer det samme dokumentobjekt SaveAsPdfA og de andre overensstemmelsesvarianter, så valget af outputstandard er et andet gem-kald, ikke en anden byggevej
Hvor dette passer ind
Den ærlige ramme er, at PDFium Component's oprettelses-API er et trofast, tyndt lag over PDFiums side-objekt-model: ægte dokumentoprettelse, ægte indlejrede skrifttyper, ægte vektor- og rasterindhold, serialiseret til en standardoverholdende fil. Det er ikke, og foregiver ikke at være, en ombrydende dokumentmotor. Skillelinjen er tekstlayout. Hvis dit output er skabelonbaseret, fakturaer, certifikater, etiketter, instrumentbrætter gengivet til et fast gitter, er den absolutte koordinatmodel direkte og hurtig, og koden forbliver læsbar. Hvis dit output er langstrakt prosa, der skal ombrydes og pagineres på egen hånd, vil du bygge en layoutmotor oven på disse kald, og det er det forkerte værktøj til opgaven. At vide hvilken side af den linje, du befinder dig på, er det meste af beslutningen
Oprettelsesmetoderne beskrevet her er en del af PDFium Component til Delphi, som kombinerer denne oprettelsesvej med de gengivelses- og tekstudtrækningsfunktioner, PDFium er bedre kendt for