Een eerste regel geschreven vanuit het standpunt van de specificatie belandt op y = 770. De redenering klopt op papier: ISO 32000-1 legt de oorsprong van user space in de hoek linksonder, een A4-pagina is 842 punten hoog, dus 770 zit één inch onder de bovenrand. Draai dat programma tegen een component die vanaf de bovenkant meet en de groet belandt zo’n inch boven de onderrand, met de rest van de pagina leeg erboven. Voeg een tweede pagina toe en het zorgvuldig gekozen font verandert terug in 12-punts Arial. Beide verrassingen komen uit dezelfde twee beslissingen die elk PDF-programma op zijn eerste pagina neemt: welk font actueel is, en vanaf welke hoek de getallen tellen
Het kleinste HotPDF-programma in Delphi
De HotPDF Hello World is een consoleprogramma met vier aanroepen in vaste volgorde: documentproperties zetten, BeginDoc, tekenen met SetFont en TextOut, dan EndDoc. Alles daaromheen is gewoon Delphi. Hier is hij, en hij schrijft één pagina die de lezer in elf talen begroet:
program HelloWorld;
{$APPTYPE CONSOLE}
uses
Winapi.Windows, // DEFAULT_CHARSET; op Delphi vóór XE2 gebruik je kale Windows
HPDFDoc; // hoofdunit van HotPDF
procedure CreateHelloWorld(const FileName: string);
var
Pdf: THotPDF;
begin
Pdf := THotPDF.Create(nil);
try
// Documentbrede instellingen: dit alles gaat vóór BeginDoc
Pdf.FileName := FileName;
Pdf.AutoLaunch := False; // default is True: opent de viewer na EndDoc
Pdf.Compression := cmFlateDecode; // default is cmNone
Pdf.FontEmbedding := True; // al de default, uitgeschreven voor de lezer
Pdf.EnableFontSubsetting := True; // sluit alleen de getekende glyphs in
Pdf.BeginDoc; // maakt pagina 1 aan (A4)
// Het 4e argument heeft default ANSI_CHARSET; geef DEFAULT_CHARSET expliciet door
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 14, DEFAULT_CHARSET);
// X, Y in punten vanaf de hoek LINKSBOVEN; Y is de bovenrand van de regel
Pdf.CurrentPage.TextOut(72, 72, 0, 'Hello, Delphi PDF world!'); // English
Pdf.CurrentPage.TextOut(72, 102, 0, 'Hola, mundo Delphi PDF!'); // Spanish
Pdf.CurrentPage.TextOut(72, 132, 0, 'Hallo, Delphi PDF Welt!'); // German
Pdf.CurrentPage.TextOut(72, 162, 0, 'Bonjour, monde PDF Delphi!'); // French
Pdf.CurrentPage.TextOut(72, 192, 0, 'Ciao, mondo Delphi PDF!'); // Italian
Pdf.CurrentPage.TextOut(72, 222, 0, 'Olá, mundo Delphi PDF!'); // Portuguese
Pdf.CurrentPage.TextOut(72, 252, 0, 'Здравствуйте, Delphi PDF мир!'); // Russian
Pdf.CurrentPage.TextOut(72, 282, 0, 'こんにちは、Delphi PDFの世界!'); // Japanese
Pdf.CurrentPage.TextOut(72, 312, 0, 'Merhaba, Delphi PDF dünyası!'); // Turkish
Pdf.CurrentPage.TextOut(72, 342, 0, '你好,Delphi PDF世界'); // Chinese
Pdf.CurrentPage.TextOut(72, 372, 0, '여보세요, Delphi PDF 세계!'); // Korean
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
begin
CreateHelloWorld('HelloWorld.pdf');
Writeln('Wrote HelloWorld.pdf');
end.

THotPDF.Create(nil) samen met try/finally ... Free is het gebruikelijke Delphi-eigendomspatroon, en een PDF produceren verandert daar niets aan. De regels lopen vanaf Y = 72 naar beneden, 30 punten uit elkaar, wat comfortabel regelaandacht overlaat voor tekst van 14 punten. Draai het uit en HelloWorld.pdf verschijnt in de werkmap: één pagina, gecomprimeerde content, het font ingesloten als subset
Documentproperties horen vóór BeginDoc
BeginDoc start het document en maakt zijn eerste pagina aan, dus alles dat de hele bestandsvorm bepaalt moet vóór die aanroep worden toegekend. HotPDF dwingt dit voor sommige properties af in plaats van het aan conventie over te laten. Wijst u Compression ná BeginDoc toe, dan gooit de setter een exceptie met de melding Cannot set Compression value - document in progress.; AutoLaunch wordt op dezelfde manier bewaakt. Luid is hier prima. Stop elke documentinstelling in één blok boven BeginDoc en u ziet die melding nooit
Twee van de defaults verdienen een tweede blik. Compression begint als cmNone, dus een programma dat hem nooit aanraakt schrijft ongecomprimeerde content streams; voor een pagina tekst is dat slechts verspilling, voor een rapport met honderden pagina’s is het het eerste wat iemand aan de bestandsgrootte opvalt. AutoLaunch begint als True, wat betekent dat een consoletool de afgeronde PDF opent in de gekoppelde viewer op het moment dat EndDoc terugkeert. Dat is prettig aan uw bureau en verkeerd in een service, een geplande taak of een build agent. Zet hem voor die omgevingen op False in code; bij CI-runs waar u de code niet kunt veranderen, zet de omgevingsvariabele HOTPDF_SUPPRESS_AUTO_LAUNCH op 1 de start ook uit
Fontafhandeling is waar we een kant kiezen. FontEmbedding staat standaard op True, dus het face reist met het bestand mee en rendert ook op een machine waar Arial Unicode MS nooit is geïnstalleerd. EnableFontSubsetting staat daarentegen standaard uit om volledige fonts beschikbaar te houden voor bewerkworkflows. Voor een gegenereerde groet, factuur of rapport zet u subsetting aan. Arial Unicode MS dekt een enorm bereik aan scripts, en het hele face insluiten om elf korte regels te tonen maakt het bestand megabytes groter zonder zichtbaar voordeel. Laat hem alleen uit staan wanneer iemand verderop de tekst zal bewerken en glyphs nodig heeft die u niet hebt getekend
Waar ligt (0, 0) op een HotPDF-pagina?
HotPDF meet pagina-coördinaten vanaf de hoek linksboven met Y oplopend naar beneden, hetzelfde kader als een VCL TCanvas gebruikt. Bij de default Resolution van 72 is één unit één punt, 1/72 inch. Het PDF-bestand zelf bewaart nog steeds coördinaten van onder naar boven, zoals ISO 32000-1 §8.3.2.3 vereist; HotPDF zet elke Y die u doorgeeft om door hem af te trekken van de paginahoogte wanneer hij de content stream wegschrijft
De rekenkunde achter het openingsvoorbeeld is kort. De standaardpagina die BeginDoc en AddPage aanmaken is A4, 595 bij 842 punten. Een regel die één inch onder de bovenrand hoort te staan is Y = 72 in HotPDF-termen en y = 770 in rauwe PDF-termen, want 842 − 72 = 770. Geef 770 door aan TextOut en de tekst gaat 770 punten omlaag vanaf de bovenkant, wat 72 punten boven de onderrand is
Er schuilt in dezelfde aanroep nog een tweede, kleinere valkuil. Standaard is de Y die u doorgeeft de bovenrand van de tekst, en HotPDF schuift omlaag over de texthoogte voordat hij de baseline neerzet. Code geporteerd uit GDI verwacht meestal precies dat. Code geporteerd uit een andere PDF-library, of uit uw eigen content-stream-writer, verwacht meestal dat Y de baseline is, en elke regel komt dan één texthoogte lager uit dan gepland. THPDFPage.TextFromBaseLine(True) zet de huidige pagina over op baseline-positionering:
// Brug voor coördinaten die uit een bottom-up bron komen (een PDF-layout,
// een diagram uit een specificatie, een andere library). Helper geschreven
// voor dit artikel, geen deel van HotPDF. Geldig bij de default Resolution van 72
function FromBottomUp(Page: THPDFPage; PdfY: Single): Single;
begin
Result := Page.Height - PdfY;
end;
procedure DrawFromPdfCoordinates(Pdf: THotPDF);
begin
Pdf.CurrentPage.SetFont('Arial', [], 12);
// Y benoemt nu de baseline, zoals de eigen tekstoperatoren van PDF glyphs plaatsen
Pdf.CurrentPage.TextFromBaseLine(True);
// Baseline 770 punten boven de onderrand, dus Y = 72 vanaf de bovenkant
Pdf.CurrentPage.TextOut(72, FromBottomUp(Pdf.CurrentPage, 770), 0,
'Baseline one inch below the top edge');
end;
Resolution is de andere instelling die verandert wat de getallen betekenen. Zet THotPDF.Resolution op 96 en X/Y-coördinaten, marges en TextWidth-resultaten worden uitgedrukt in 1/96 inch, handig wanneer de indeling in schermpixels werd ontworpen. Pagina-Width en Height blijven echter punten rapporteren, dus indelingscode die paginagrenzen met tekencoördinaten mengt leest beter CurrentPage.UserWidth en CurrentPage.UserHeight, die in dezelfde eenheden terugkomen als X en Y. Bij 72 zijn de twee paren gelijk. Wilt u liever in millimeters denken, dan is de property MeasurementUnit (standaard muPoints, met muMM, muInches, muCentimeters, muTwips en muPixels beschikbaar) de nettere schakelaar. Kies één conventie per programma. Stapel ze nooit
Waarom print pagina twee in 12-punts Arial?
Omdat elke nieuwe pagina vanuit een bekende toestand begint. THotPDF.AddPage maakt de nieuwe pagina actueel, stelt hem in op A4 en roept er SetFont('Arial', [], 12) op aan, met de grootte op 12 punten ongeacht de Resolution. BeginDoc maakt pagina één via hetzelfde pad aan, en dat is waarom de Hello World zijn font direct ná BeginDoc zet en niet ervoor. De consequentie voor uitvoer met meerdere pagina’s is makkelijk te missen: het SetFont dat u op pagina één aanriep draagt niet over. Latijnse regels op pagina twee zien er in Arial nog prima uit, alleen kleiner. De CJK-regels verraden het, want Arial heeft daar geen glyphs voor en de reader toont lege vierkantjes of helemaal niets
Een voorspelbaar startfont is het juiste ontwerp voor een pagina-object: teencode voor pagina vijf hangt niet stilletjes af van wat pagina vier toevallig naliet. De prijs is dat pagina-instelling moet worden herhaald, en de netste manier om dat te herhalen is één procedure die elke pagina doorloopt, ook de eerste:
uses
Winapi.Windows, Vcl.Graphics, HPDFDoc;
// Uw eigen helper: één plek die weet wat een verse pagina nodig heeft
procedure PreparePage(Pdf: THotPDF; IsFirst: Boolean);
begin
if not IsFirst then
Pdf.AddPage; // zet het font terug op Arial 12
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 14, DEFAULT_CHARSET);
end;
procedure WriteGreetings(Pdf: THotPDF; Lines: array of string);
const
TopMargin = 72;
Leading = 30;
LinesPerPage = 20;
var
I, Row: Integer;
begin
PreparePage(Pdf, True); // pagina 1 bestaat na BeginDoc al
Row := 0;
for I := 0 to High(Lines) do
begin
if Row = LinesPerPage then
begin
PreparePage(Pdf, False);
Row := 0;
end;
Pdf.CurrentPage.TextOut(72, TopMargin + Row * Leading, 0, Lines[I]);
Inc(Row);
end;
// Een bold kop heeft zijn eigen SetFont nodig, daarna komt het bodyfont terug
Pdf.AddPage;
Pdf.CurrentPage.SetFont('Arial Unicode MS', [fsBold], 18, DEFAULT_CHARSET);
Pdf.CurrentPage.TextOut(72, 72, 0, 'Appendix');
Pdf.CurrentPage.SetFont('Arial Unicode MS', [], 14, DEFAULT_CHARSET);
Pdf.CurrentPage.TextOut(72, 110, 0, 'Body text after the heading');
end;
Schakelen uw pagina’s ook over op baseline-positionering met TextFromBaseLine, zet die aanroep dan ook in PreparePage, naast het font. Elke per-pagina-instelling in één procedure houden betekent dat een nieuwe eis, een kleur of een andere grootte één keer wordt toegevoegd en elke pagina bereikt
Eén font voor veel scripts, en waar het stopt
Eén SetFont kan Latijns, Cyrillisch, Japans en Chinees op één pagina dragen, omdat Arial Unicode MS glyphs voor een zeer breed scala aan scripts in één face bundelt. Het vierde argument van SetFont is de Windows character set, en zijn default is ANSI_CHARSET, wat fontkeuze aan de westerse codepagina koppelt. DEFAULT_CHARSET (waarde 1) doorgeven laat Windows het face op naam kiezen, en dat wil een meertalige pagina hebben
Het font moet bestaan op de machine die de PDF bouwt. HotPDF sluit in welk font Windows de naam ook naar oplost, en wanneer Arial Unicode MS ontbreekt substitueert Windows zonder klagen een ander face. De uitvoer sluit dan de vervanger in, en regels in scripts dat dat face niet dekt komen eruit als lege vierkantjes. Een buildserver is de gebruikelijke plek waar dit opduikt, omdat daar niemand Office-fonts heeft geïnstalleerd. Controleer de lijst ingesloten fonts van het eerste bestand dat een nieuwe machine produceert voordat u haar een batchverloop toevertrouwt
Wanneer één face niet elke string dekt, neemt TextOutWithFallback dezelfde argumenten als TextOut, lost per teken een font op, tekent elke reeks met het face dat hem kan renderen en herstelt daarna uw oorspronkelijke font. Right-to-left-scripts zoals Arabisch en Hebreeuws vragen meer dan glyphdekking: ze vragen shaping en omgekeerde reeksvolgorde, en dat is het terrein van de RtLTextOut-functie, doorgaans samen met een TrueType-bestand geladen via RegisterUnicodeTTF
Van hieruit zijn de natuurlijke volgende stappen grootte, stijl en rotatie van tekst, en bewust meerdere blokken op één pagina plaatsen, wat de TextOut-sample doorwerkt. Lijnen, vakken en afbeeldingen gebruiken hetzelfde CurrentPage-object en dezelfde coördinaten vanaf linksboven, dus alles hierboven over oorsprongen en per-pagina-instelling geldt voor hen ongewijzigd
De pagina HotPDF Delphi Component dekt de rest van de teken-API voor Delphi en C++Builder, van vormen en afbeeldingen tot formulieren, zodra deze Hello World schoon draait tegen uw eigen fonts