Popolno poravnavanje je postavitev, ki naredi stolpec besedila poravnan na levi in desni rob hkrati - videz, ki ga pričakujete od tiskane knjige ali formalnega poročila. Enostavno ga je opisati in presenetljivo enostavno ga je narobe narediti, ker odgovor na vprašanje "kam gre dodaten prostor" ni enak za angleščino in japonščino, in ker naivni način merjenja vsake vrstice pretvori hitro stran v počasno. HotPDF vam nudi poravnavanje, ki upošteva pisavo, prek enega samega klica za postavitev v okno, pod tem klicem pa se skriva učbeniška optimizacija zmogljivosti, ki je vredna razumevanja sama po sebi
Ta članek obravnava oba vidika. Najprej tipografsko pravilo, ki odloča, kako se porazdeli praznina za pisave s presledki med besedami v primerjavi s pisavami brez njih. Nato pa spremembo merjenja, ki je zmanjšala stroške poravnavanja na stran za približno osemdesetkrat brez vidne razlike v izpisu. Oba vidika sta pomembna, če dokumente ustvarjate v velikih količinah in želite, da se berejo kot resnično stavništvo namesto kot monoprostorski izpis, raztegnjen, da ustreza oknu
Kaj popolno poravnavanje dejansko zahteva
Vrstica besedila, narisana pri njeni naravni širini, skoraj nikoli ne doseže desnega roba stolpca. Med koncem zadnjega glifa in mejo stolpca je vedno ostanek - praznina. Leva poravnava pusti praznino na desni. Desna poravnava jo premakne na levo. Centriranje jo razdeli. Popolno poravnavanje jo odstrani z razširitvijo same vrstice, dokler oba robova ne dosežeta okna, in edini pošten način za to je potiskanje glifov narazen od znotraj
Pravilo, ki loči dobro poravnavanje od slabega, je, kam daste praznino. Pisava, ki piše besede s presledki med njimi, kot je angleščina in preostala latinska družina, ima naravne šive pri vsakem presledku med besedami. Razširitev teh presledkov je za oko nevidna, ker bralci že sprejemajo, da se presledki med besedami razlikujejo. Pisava, ki piše brez presledkov med besedami, kot so kitajski ideogrami, japonska kana ali korejski hangul, takih šivov nima. Tam mora biti praznina enakomerno razporejana med sosednje glifi, kar je načelo, ki ga japonski stavci imenujejo kintou-waritsuke, enakomerni razmik. Uvajanje latinskega raztezanja presledkov med besedami v vrstico CJK, ali natrpanje vse praznine na eno mesto, kjer vrstica CJK slučajno vsebuje presledek, ustvari reke in vrzeli, ki označujejo amaterski izpis
Kako HotPDF odloča, kam gre prostor
HotPDF sprejme to odločitev na razmik, ne na vrstico. Ko poravna vrstico, prehodi vsak sosednji par glifov in vpraša, ali med njima leži raztegljiva meja. Meja je raztegljiva, kadar je na kateri koli strani presledek ali zavihek - latinski primer - ali kadar sta obe strani znaki, ki jih je mogoče prelomiti v CJK - primer enakomerne razporeditve prostora. Prešteje te meje, enakomerno razdeli praznino vrstice med njih in doda ta delež vsakemu ustrezajočemu razmiku
Posledica izhaja naravno. Angleška vrstica ima raztegljive meje le pri presledkih med besedami, zato vsa praznina pristane tam in besede se razširijo, medtem ko črke znotraj vsake besede ohranijo naravne razmike. Vrstica iz ideogramov ali kane ima raztegljivo mejo med skoraj vsakim parom glifov, zato se praznina enakomerno porazdeli po celotni vrstici - natanko enakomerní razmiki med glifi, ki jih te pisave zahtevajo. Vrstica, ki je ena sama dolga latinska beseda brez notranjega presledka, nima nobene raztegljive meje, zato HotPDF pusti pri njeni naravni širini namesto trganja besede črko za črko. Enaka logika brez posebnih primerov obravnava mešane latinske in CJK poteke v eni vrstici, ker je odločitev lokalna pri vsaki meji
Ena meja je namerno povsod izključena. Položaj za zadnjim glifom vrstice nikoli ni obravnavan kot razmik, ker bi raztezanje tam preprosto znova uvedlo ostanek na desni - kar je nasprotje poravnavanja
Zakaj zadnja vrstica ostane pri miru
Zadnja vrstica odstavka je posebna, in napačno ravnanje z njo je najpogostejša napaka pri poravnavanju. Zadnja vrstica odstavka je navadno kratka, pogosto le nekaj besed, in njeno raztezanje na polno širino stolpca vleče te besede čez stran v redko, raztrgano vrstico. Pravilna tipografija pusti zadnjo vrstico pri njeni naravni širini, poravnano na levo
HotPDF zazna zaključno vrstico po položaju. Ko ovija besedilo v vrstice, ve, kdaj vrstica, ki jo je pravkar odrezal, doseže konec posredovanega niza. Ta zadnja vrstica je oddana z navadno levo poravnavo in ohrani svojo naravno širino. Vsaka vrstica pred njo je poravnana na oba robova. Trdi prelomi vrstic, ki jih pišete v besedilo, so upoštevani kot zapisani, zato namerna kratka vrstica nikoli ni raztegnjena. Bralec vidi čist pravokoten blok besedila, katerega zadnja vrstica se naravno konča - kar je tisto, kar oko pričakuje
Strošek merjenja, ki je naredil poravnavanje počasno
Za poravnavanje vrstice morate poznati njeno točno širino in napredek vsakega glifa, da lahko dodate dodaten prostor natančno. Prva implementacija je dobila te številke na očiten način. Izmerila je celotno vrstico s polno poizvedbo po širini Unicode, nato pa merila predpono za predpono, da bi obnovila napredek vsakega glifa z razlikovanjem. Za vrstico N glifov je to N+1 klicev v merilni stroj, vsak klic pa je poln povratni potok GDI - sistem se zahteva, da oblikuje in izmeri besedilo ter vrne odgovor
Na vrstico se to sliši poceni. Čez stran pa ni. Vzemite gosto stran A4 s telesnim besedilom, približ no petinštirideset vrstic z okrog osemdeset znaki vsaka. Pri N+1 povratnih potokih na vrstico je to okrog 81 povratnih potokov za vsako vrstico in grobe 3645 za stran, skoraj vse porabljene za ponovno merjenje besedila, ki ga je stroj že obdelal trenutke prej. Pri paketnem opravilu, ki ustvarja tisoče strani, ta obremenitev prevlada nad časom postavitve, vsak povratni potok pa prečka mejo med vašim procesom in grafičnim podsistemom
En klic namesto N plus ena
Popravek je tista vrsta spremembe, ki izgleda majhna in se izplača z velikim povratkom. GDI že zna poročati skupno širino niza in položaj vsakega glifa v eni sami poizvedbi. HotPDF to izpostavlja prek GetWideCharAdvances, ki zapolni matriko z naravnim napredkom vsakega glifa, vključno z kerningom, in vrne skupno širino - v enem klicu namesto N+1. Rutina za poravnavanje, interno _HPDFEmitJustifiedWideLine, enkrat zahteva vse napredke, izračuna praznino, jo porazdeli čez raztegljive meje in odda vrstico
Za tisto isto stran A4 se merjenje na vrstico zmanjša s pribl. 81 povratnih potokov na enega, zato stran pade s pribl. 3645 povratnih potokov na okrog 45 - skoraj osemdesetkratno zmanjšanje. Izpis je bajt za bajtom enak, ker se o merjenju ni spremenilo nič razen tega, kolikokrat je zahtevano. Isti mehanizem GDI, enake meritve pisav, enaki napredki kerning dajejo enake številke. Padlo je le število povratnih potokov. Kadar je merjenje že pravilno, je prava optimizacija prenehati ga večkrat zahtevati, ne pa ga aproksimirati
Kako vrstica doseže stran
Ko je praznina dodeljena, HotPDF odda vrstico z ExtTextOut in matriko napredkov na glif - matriko Dx. Vsak vnos je razdalja od izhodišča enega glifa do naslednjega, kar je naravni napredek tega glifa plus njegov delež praznine, kadar za njim sledi raztegljiva meja. To se neposredno preslika na slikovni model PDF. Postavljeno besedilo se zapiše z operatorjem TJ, matriko, ki prepleta poteke glifov z eksplicitnimi vodoravnimi prilagoditvami, vrednosti Dx pa postanejo natanko te prilagoditve. Zato dodaten prostor pade med glifi na natančnih položajih pod točko namesto da bi bil ponarejen z znaki za oblazinjenje, in zato poravnana vrstica HotPDF pravilno meri, če jo naknadno orodje prebere nazaj
Za poravnane odstavke sami ne kličete ExtTextOut. Vstopna točka je WideTextOutBox, ki ovije niz Unicode v okno in aplikira poravnavanje, ki ga zahtevate. Razdeli besedilo na vrstice, ki ustrezajo širini okna, vsako vrstico postavi po višini okna in vrne število znakov, ki jih je uspelo umestiti, preden je zmanjkalo navpičnega prostora. Poravnavanje je izbrano prek enumeratorja
type
THPDFJustificationType = (jtLeft, jtCenter, jtRight, jtJustify);
Prve tri so samoumevne - leva, centrirana in desna poravnava. Četrta, jtJustify, je tukaj opisano popolno poravnavanje na oba robova, in to je vrednost, ki jo WideTextOutBox bere, da vklopi razporejanje prostora glede na pisavo
Poravnavanje odstavka v praksi
Celovit primer ustvari dokument, nastavi pisavo in vlije odstavek v okno s popolnim poravnavanjem. Enaka koda poravna latinsko in CJK besedilo brez spremembe zastavice, ker zavedanje o pisavi leži pod vmesnikom API
uses
HPDFDoc;
procedure JustifyParagraph;
var
Pdf: THotPDF;
Body: WideString;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.FileName := 'Justified.pdf';
Pdf.BeginDoc;
Pdf.CurrentPage.SetFont('Arial', 11);
Body :=
'Full justification spreads the slack on each filled line so both ' +
'edges meet the column, while the last line keeps its natural width. ' +
'For scripts with word gaps the space lands between words; for ' +
'scripts without them it spreads evenly between glyphs.';
// X, Y, LineSpacing, BoxWidth, BoxHeight, Text, Align
Pdf.CurrentPage.WideTextOutBox(72, 72, 4, 380, 240, Body, jtJustify);
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Za izris istega bloka z levo poravnavo, centriranega ali z desno poravnavo spremenite le zadnji argument v jtLeft, jtCenter ali jtRight. Ovijanje, postavitev vrstic in povratna vrednost ostanejo enaki. Izmerjena širina, ki poganja vse štiri poti, prihaja iz GetWideTextWidth - poizvedbe po širini, ki upošteva Unicode in pravilno meri WideString tam, kjer bi starejše bajtno merjenje napačno izmerilo karkoli onkraj Latin-1, kar je tisto, kar naredi prelom okna za CJK in besedilo surogatnih parov na pravem mestu
Poravnavanje je ena plast večjega sklada za oblikovanje besedila. Ko vrstica vsebuje pisave, ki preuredijo ali združijo svoje glifi, odločitve o razmiku tukaj ležijo na vrhu dela, opisanega v našem članku o oblikovanju besedila z zapletenimi pisavami, in ko pisava nosi tipografske variante, ki jih želite izbrati, si oglejte kako goniti stilistične alternative OpenType GSUB. Vse to je na voljo v komponenti HotPDF za Delphi in C++Builder, skupaj z vmesniki API za besedilo, postavitev in dokument, ki so obravnavani po vsem tem blogu