Každý viditelný řetězec v dokumentu HotPDF přichází přes jedno volání: TextOut(X, Y, angle, Text). Příklad Hello World jej používá v té nejjednodušší podobě, s jedním nastavením písma a čtyřmi argumenty ponechanými na rozumných výchozích hodnotách. Za touto první stránkou nesou tytéž čtyři argumenty celou tíhu rozvržení. Třetí argument otáčí textem. Písmo nastavené těsně předtím určuje velikost a styl. A dvojice X, Y, měřená od rohu stránky v bodech, je to jediné, co stojí mezi čistým reportem a textem, který se překrývá, ořezává nebo posune o řádek níže na cizí tiskárně. Právě zde si TextOut zaslouží svou pozornost a zde výchozí hodnoty přestávají stačit
Signaturu stojí za to si zapamatovat dříve než cokoli jiného: X a Y jsou Single v bodech, angle je Extended ve stupních a Text je WideString, takže Unicode prochází bez samostatného volání. Druhé přetížení přijímá PWORD a délku pro případ, kdy již máte kódy glyfů, ale pro běžné řetězce sáhnete po formě WideString
Velikost a styl pocházejí ze SetFont, nikoli z TextOut
TextOut nemá žádný parametr velikosti. Velikost, tloušťka, sklon, to vše žije ve volání SetFont, které předchází vykreslení, a zůstává v platnosti, dokud jej nenahradí další SetFont. To je ten jediný fakt, který vysvětluje většinu zmatků prvního dne: řádek vyjde tučně, protože o tři volání dříve něco nastavilo [fsBold] a nic jej nevymazalo
Pdf.CurrentPage.SetFont('Times New Roman', [], 24);
Pdf.CurrentPage.TextOut(72, 740, 0, 'Quarterly Report'); // 24pt regular
Pdf.CurrentPage.SetFont('Times New Roman', [fsBold], 12);
Pdf.CurrentPage.TextOut(72, 712, 0, 'Revenue'); // 12pt bold
Pdf.CurrentPage.SetFont('Times New Roman', [fsItalic], 11);
Pdf.CurrentPage.TextOut(72, 694, 0, 'figures in thousands'); // 11pt italic
Pdf.CurrentPage.SetFont('Courier New', [fsBold, fsItalic], 10);
Pdf.CurrentPage.TextOut(72, 676, 0, ' +18.4% YoY'); // styles combine
Druhý argument je množina TFontStyles, takže [fsBold, fsItalic] je tučná kurzíva a [] je obyčejné písmo. Velikost je v bodech, což je stejná jednotka jako souřadnice, díky čemuž lze o svislém rozestupu snadno uvažovat: 12bodový řádek potřebuje vertikální krok zhruba 14 až 16 bodů, aby mohl dýchat, takže snížení Y o 14 na řádek je rozumné výchozí řádkování. Neexistuje žádný automatický posun řádku. Každou účaří počítáte sami, což je pro odstavec zdlouhavé, ale pro formulář přesné, protože každé pole sedí na pevné souřadnici
Dvě praktické poznámky k názvu písma. Je řešen vůči písmům nainstalovaným na stroji pro sestavení, a cokoli operační systém vrátí, se vloží, takže není zaručeno, že název, který se vyřeší na vašem desktopu, a název, který se vyřeší na build serveru, bude stejný řez písma. Písmo navíc musí pokrývat skripty v řetězci. Běh azbuky nebo textu CJK pod písmem pouze pro latinku se bez chyb vykreslí jako rámečky chybějících glyfů, což je důvod, proč stránka Hello World sahá po širokém písmu Unicode, když míchá jazyky
Argument angle se otáčí kolem kotvy
Třetí argument je ten, který většina kódu nechává navždy na nule. Předejte nenulovou hodnotu a běh textu se otočí proti směru hodinových ručiček kolem své vlastní (X, Y) kotvy, levého dolního rohu textu, o příslušný počet stupňů. Samotná kotva se neposune, takže stejná souřadnice, která umístila vodorovný štítek, umístí i jeho otočené dvojče; změní se pouze směr, kterým glyfy postupují
Pdf.CurrentPage.SetFont('Arial', [fsBold], 11);
// A vertical axis label down the left margin: 90 degrees reads bottom-to-top.
Pdf.CurrentPage.TextOut(40, 300, 90, 'Units sold');
// A diagonal DRAFT watermark across the page body.
Pdf.CurrentPage.SetFont('Arial', [fsBold], 60);
Pdf.CurrentPage.TextOut(150, 250, 45, 'DRAFT');
// Column headers tilted 60 degrees so long labels fit a narrow table.
Pdf.CurrentPage.SetFont('Arial', [], 9);
Pdf.CurrentPage.TextOut(120, 600, 60, 'Q1 actual');
Pdf.CurrentPage.TextOut(160, 600, 60, 'Q2 actual');
Devadesát stupňů je běžný případ, štítek běžící po straně grafu nebo název na hřbetu. Čtyřicet pět stupňů zvládá nakloněná záhlaví sloupců, což je trik, který umožňuje širokému štítku sedět nad úzkým sloupcem, aniž by přetekl do sousedních. Rotace nemění způsob interpretace kotvy, což lidi mate: 90stupňový běh stále začíná na (X, Y) a roste odtud nahoru, takže pro vycentrování otočeného štítku upravíte kotvu, nikoli úhel. Když několik otočených běhů sdílí účaří, dejte jim stejné Y a krokujte X, přesně tak, jak byste krokovali Y u poskládaných vodorovných řádků
Umístění souřadnic bez hádání
Souřadnice jsou tou částí, která přežije revizi, nebo v ní tiše selže. HotPDF měří od levého dolního rohu stránky, Y roste nahoru, v bodech po 72 na palec. Stránka US Letter má 612 x 792 bodů; A4 má 595 x 842 bodů. Jednopalcový horní okraj na formátu Letter proto umístí vaši první účaří blízko Y = 792 minus 72 minus velikost písma, nikoli na nějaké malé číslo blízko horního okraje. Každý, kdo přichází od souřadnic obrazovky, kde Y roste od nuly dolů, zapíše první řádek mimo spodní okraj a stráví deset minut přemýšlením, kam se poděl
K rozvržení přistupujte jako k aritmetice vůči pojmenovaným kotvám, nikoli jako ke sloupci magických čísel. Levý okraj, běžící účaří, kterou s každým řádkem snižujete, a pevné řádkování promění blok štítků v krátkou smyčku namísto stěny literálů:
const
LeftMargin = 72; // 1 inch in
TopBaseline = 720; // first line, ~1 inch down on Letter
Leading = 16; // vertical step between lines
var
Y: Single;
Line: string;
begin
Pdf.CurrentPage.SetFont('Arial', [], 11);
Y := TopBaseline;
for Line in ReportLines do
begin
Pdf.CurrentPage.TextOut(LeftMargin, Y, 0, Line);
Y := Y - Leading;
if Y < 72 then // bottom margin reached
begin
Pdf.AddPage;
Pdf.CurrentPage.SetFont('Arial', [], 11); // font resets on a new page
Y := TopBaseline;
end;
end;
end;
Ochrana proti zalomení stránky je řádek, na který každý zapomene jako první a v praxi to pocítí nejtvrději. Pod TextOut neexistuje žádné plynulé rozvržení (flow layout). Pokud při odečítání přejdete za spodní okraj, text se bude dál kreslit do spadávky, mimo stránku, do prázdna, a to bez varování. Y si tedy hlídáte sami, voláte AddPage, když překročí dno, a resetujete účaří. SetFont po AddPage není volitelná výplň: aktuální písmo nepřežije zalomení stránky, a pokud jej přeskočíte, první běh na nové stránce vyjde ve výchozím řezu prohlížeče
Mezery mezi znaky a slovy pro lícování a zarovnání
Někdy je řetězec správný, ale má nesprávnou šířku: hlavička, která se musí roztáhnout přes pevné pravidlo, kód, který by se měl číst se vzdušnějšími číslicemi, sloupec, jehož hodnoty je třeba mírně posunout kvůli zarovnání. PDF pro to má dva operátory stavu textu, mezery mezi znaky (Tc, mezera navíc přidaná po každém glyfu) a mezery mezi slovy (Tw, mezera navíc přidaná u každého znaku mezery). Oba jsou vyjádřeny v neškálovaných jednotkách textového prostoru, efektivně v bodech při aktuální velikosti písma. Jsou to stavy, nikoli argumenty pro TextOut, takže je nastavíte, vykreslíte a nastavíte zpět
// Letter-space a short heading so it stretches across a rule.
Pdf.CurrentPage.SetCharacterSpacing(4);
Pdf.CurrentPage.SetFont('Arial', [fsBold], 14);
Pdf.CurrentPage.TextOut(72, 740, 0, 'S U M M A R Y');
Pdf.CurrentPage.SetCharacterSpacing(0); // reset before normal body text
// Open up the gaps between words on a single wide line.
Pdf.CurrentPage.SetWordSpacing(6);
Pdf.CurrentPage.SetFont('Arial', [], 11);
Pdf.CurrentPage.TextOut(72, 712, 0, 'Name Department Extension');
Pdf.CurrentPage.SetWordSpacing(0);
Mezery mezi slovy působí pouze na znak mezery (kód 32), což má důsledek, který stojí za to znát: neudělá nic v rámci běhu CJK, který nemá mezery ASCII, a zvláštně interaguje s textem kódovaným jako indexy glyfů spíše než bajty. Pro výstup tabulek v latince je to levný způsob, jak rozšířit mezery bez přepisování řetězce. Rozestup znaků je lepším nástrojem pro hlavičku, která musí dosáhnout cílové šířky, protože rozloží úpravu rovnoměrně na každý glyf namísto toho, aby ji seskupil do mezer
Resetování je tou hlavní disciplínou. Mezery jsou stejně jako písmo součástí stavu kreslení stránky a stav přetrvává, dokud jej nezměníte. Zvětšete rozestupy znaků (letter-space) u jednoho nadpisu a zapomeňte je vynulovat, a každý další odstavec zdědí toto protažení, což působí jako jemná, těžko zařaditelná chyba, která přežije zběžnou korekturu, ale při pečlivé selže. Spolehlivým zvykem je nastavit hodnotu mezer, nakreslit běh, který ji potřebuje, a hned na dalším řádku ji nastavit zpět na nulu, aby žádný pozdější kód nemusel vědět, co udělala dřívější sekce
Kontrola výstupu tam, kde se skutečně rozbíjí
Rozvržení textu selže na druhém stroji, ne na prvním, takže kontroly, na kterých záleží, se odehrávají mimo váš stůl. Otevřete vygenerovaný soubor v systému, kde není nainstalována vaše vývojářská sada písem, a ověřte, že se vložená písma stále vykreslují, včetně latinky s diakritikou, všech nelatinských skriptů a interpunkce, a to při jednom průchodu, místo abyste namátkově kontrolovali jednoduché znaky. Vyberte a zkopírujte několik řádků, abyste potvrdili, že text je skutečným textem a ne obrysy, což hraje roli v okamžiku, kdy je v plánu vyhledávání nebo extrakce. Krmte rozvržení reprezentativními daty, nejdelším německým štítkem a nejširším číslem, ne úhledným zástupným textem, protože běh, který přeteče přes pole, je vždy ten, který jste nepsali ručně. A pokud má stránka skončit na předtištěném formuláři, vytiskněte nebo rastrovejte jeden vzorek a položte jej proti originálu; čtvrtmilimetrový posun účaří je na obrazovce neviditelný a na papíře zřejmý
Pokud jste ještě nenapsali jedinou stránku, začněte s příkladem HotPDF Hello World, který nastavuje dokument, písmo a levý dolní souřadnicový systém, na kterém závisí vše výše uvedené. Zde zobrazená volání TextOut, SetFont a volání mezer jsou součástí komponenty HotPDF pro Delphi a C++Builder