Puno poravnanje (full justification) je izgled (layout) koji čini da se stupac teksta poravna na oba ruba, lijevom i desnom, što je izgled kakav očekujete od tiskane knjige ili formalnog izvješća. Lako ga je opisati i iznenađujuće lako pogriješiti, jer odgovor na pitanje "kamo ide taj dodatni prostor" nije isti za engleski i za japanski, i zato jer onaj naivni način mjerenja svake linije pretvara brzu stranicu u sporu. HotPDF vam daje poravnanje svjesno pisma (script-aware) putem samo jednog poziva za raspored okvira (box-layout call), a ispod tog poziva krije se školski ispravak performansi (performance fix) kojeg vrijedi razumjeti samog po sebi
Ovaj članak prolazi kroz oboje. Prvo, tipografsko pravilo koje odlučuje kako se raspodjeljuje višak (slack) kod pisama s razmacima među riječima (word gaps) u odnosu na pisma bez njih. Drugo, promjenu mjerenja koja je smanjila cijenu poravnanja po stranici za otprilike osamdeset puta bez vidljive razlike u konačnom izlazu (output). Oboje je važno ako generirate dokumente u velikim količinama (at volume) i želite da se čitaju kao pravi slog (real typesetting), umjesto kao monospace ispis rastegnut da se ukalupi u širinu (stretched to fit)
Što zapravo zahtijeva puno poravnanje
Linija teksta iscrtana u svojoj prirodnoj širini gotovo nikada ne doseže desni rub svog stupca. Uvijek ostaje ostatak, višak (slack), između onog mjesta gdje završava posljednji glif i mjesta na kojem se nalazi granica stupca. Lijevo poravnanje (left alignment) ostavlja taj višak s desne strane. Desno poravnanje premješta ga na lijevu. Centriranje (centering) ga razdjeljuje (splits). Puno poravnanje uklanja ga širenjem same linije sve dok oba ruba ne dotaknu okvir (box), a jedini iskreni način za to jest razdvajanje glifova iznutra
Pravilo koje odvaja dobro poravnanje od lošeg jest točno to kamo ćete smjestiti taj višak prostora. Pismo koje riječi piše s razmacima između njih, kao što je to engleski jezik i ostatak latinične obitelji, ima prirodne šavove (seams) pri svakom razmaku između riječi. Širenje tih razmaka nevidljivo je oku jer čitatelji ionako već prihvaćaju da razmaci između riječi variraju. Pismo koje piše bez razmaka između riječi, poput kineskih Han znakova, japanske kane ili korejskog Hangula, nema takvih šavova. Tu se višak (slack) mora ravnomjerno rasporediti između susjednih glifova, što je princip kojeg japanski slagači (typesetters) nazivaju kintou-waritsuke, ravnomjeran razmak (even spacing). Stavljanje rastezanja razmaka između riječi u latiničnom stilu na CJK (kinesko-japansko-korejsku) liniju teksta, ili pak utrpavanje cjelokupnog viška prostora na samo jedno mjesto gdje CJK linija slučajno sadrži razmak, stvara rijeke (rivers) i praznine koje odaju potpuno amaterski (amateur) izlaz (output)
Kako HotPDF odlučuje kamo ide prostor
HotPDF tu odluku donosi po razmaku, a ne po liniji. Kad poravnava liniju, prolazi kroz svaki susjedni par glifova i pita se nalazi li se rastezljiva granica između njih. Granica je rastezljiva kad je bilo koja strana razmak ili tabulator, slučaj kod latinice, ili kad su obje strane znakovi koji se mogu prelomiti u CJK pismu, slučaj ravnomjernog razmaka. Broji te granice, dijeli višak linije jednako među njima, i dodaje taj udio svakom razmaku koji na to ima pravo
Posljedica proizlazi sasvim prirodno. Engleska linija ima rastezljive granice samo na svojim razmacima među riječima, pa cijeli višak slijeće upravo tamo i riječi se razmiču dok slova unutar svake riječi zadržavaju svoj prirodan razmak. Han ili kana linija ima rastezljivu granicu između gotovo svakog para glifova, pa se višak ravnomjerno raspoređuje duž cijele linije, točno onaj ravnomjeran razmak među glifovima kakav ta pisma zahtijevaju. Linija koja je jedna duga latinična riječ bez ikakvog unutarnjeg razmaka nema uopće rastezljivu granicu, pa je HotPDF ostavlja pri njezinoj prirodnoj širini umjesto da riječ rastrga slovo po slovo. Ista logika obrađuje pomiješane latinične i CJK nizove u jednoj liniji bez posebnih slučajeva, jer je odluka lokalna za svaku granicu
Jedna granica je posvuda namjerno isključena. Pozicija nakon posljednjeg glifa u liniji nikad se ne tretira kao razmak, jer bi rastezanje na tom mjestu samo ponovno uvelo ostatak s desne strane, što je suprotno poravnanju
Zašto se posljednju liniju ostavlja na miru
Posljednja linija odlomka je posebna, a njezino pogrešno rješavanje najčešća je greška kod poravnanja. Posljednja linija odlomka obično je kratka, često samo par riječi, a rastezanje do pune širine stupca razvlači te riječi po cijeloj stranici u rijedak, razlomljen redak. Ispravna tipografija ostavlja posljednju liniju pri njezinoj prirodnoj širini, poravnatu ulijevo
HotPDF prepoznaje posljednju liniju po poziciji. Dok prelama tekst u linije, zna kad linija koju je upravo odvojio doseže kraj zadanog niza znakova. Ta posljednja linija ispisuje se s običnim lijevim poravnanjem i zadržava svoju prirodnu širinu. Svaka linija prije nje poravnata je na oba ruba. Tvrdi prijelomi linije koje upišete u tekst poštuju se onako kako su napisani, pa se ni namjerno kratka linija nikad ne rasteže. Čitatelj vidi čist pravokutan blok teksta čija posljednja linija završava prirodno, što je upravo ono što oko očekuje
Trošak mjerenja koji je usporio poravnanje
Da biste poravnali liniju, morate znati njezinu točnu širinu, i morate znati pomak svakog glifa kako biste dodatni prostor mogli postaviti precizno. Prva implementacija je te brojke dobivala na očit način. Mjerila je cijelu liniju punim Unicode upitom širine, a zatim mjerila prefiks po prefiks kako bi razlikom dobila pomak svakog glifa. Za liniju od N glifova to je N+1 poziva mehanizmu za mjerenje, a svaki poziv je puno GDI povratno putovanje, koje traži od operacijskog sustava da oblikuje i izmjeri tekst te vrati odgovor
Po liniji to zvuči jeftino. Kroz cijelu stranicu nije. Uzmite gustu A4 stranicu tijela teksta, otprilike četrdeset pet linija od po osamdesetak znakova. Uz N+1 povratnih putovanja po liniji to je oko 81 povratno putovanje za svaku liniju i otprilike 3.645 za stranicu, od čega je gotovo sve utrošeno na ponovno mjerenje teksta koji je mehanizam već pogledao samo trenutak ranije. Kod serijskog posla koji proizvodi tisuće stranica, taj troškovni dodatak dominira vremenom rasporeda, a svako povratno putovanje prelazi granicu između vašeg procesa i grafičkog podsustava
Samo jedan poziv umjesto N plus jedan
Ispravak je vrsta promjene koja izgleda malena, a donosi velik učinak. GDI već može u jednom upitu javiti ukupnu širinu niza i poziciju svakog glifa. HotPDF to izlaže kroz GetWideCharAdvances, koji puni niz prirodnim pomakom svakog glifa, uključujući kerning, i vraća ukupnu širinu, u jednom pozivu umjesto N+1. Rutina za poravnanje, interno _HPDFEmitJustifiedWideLine, jednom zatraži sve pomake, izračuna višak, rasporedi ga po rastezljivim granicama, i ispiše liniju
Za tu istu A4 stranicu mjerenje po liniji pada s oko 81 povratnog putovanja na jedno, pa stranica pada s otprilike 3.645 povratnih putovanja na oko 45, blizu osamdesetorostrukom smanjenju. Izlaz je identičan bajt po bajt, jer se u mjerenju ništa nije promijenilo osim koliko se puta traži. Isti GDI mehanizam, iste metrike fonta, isti kerning daju iste brojke. Pao je samo broj povratnih putovanja. Kad je mjerenje već ispravno, ispravna optimizacija je prestati ga ponavljano tražiti, a ne ga aproksimirati
Kako linija dopire do stranice
Kad je višak raspoređen, HotPDF ispisuje liniju pomoću ExtTextOut i niza pomaka po glifu, niza Dx. Svaki unos je udaljenost od ishodišta jednog glifa do sljedećeg, što je prirodan pomak tog glifa uvećan za njegov udio viška kad iza njega slijedi rastezljiva granica. To se izravno preslikava na PDF model slikanja. Pozicionirani tekst piše se operatorom TJ, nizom koji isprepliće nizove glifova s izričitim vodoravnim prilagodbama, a vrijednosti Dx postaju upravo te prilagodbe. Zato dodatni prostor sliježe između glifova na preciznim potočkastim pozicijama umjesto da se glumi znakovima za popunu, i zato poravnata HotPDF linija ispravno mjeri ako je nizvodni alat kasnije očita
Za poravnate odlomke sami ne pozivate ExtTextOut. Ulazna točka je WideTextOutBox, koja Unicode niz omotava u okvir i primjenjuje poravnanje koje zatražite. Ona dijeli tekst na linije koje stanu u širinu okvira, smješta svaku liniju niz visinu okvira, i vraća broj znakova koje je uspjela smjestiti prije nego što ponestane okomitog prostora. Poravnanje se bira nabrajačkim tipom za poravnanje
type
THPDFJustificationType = (jtLeft, jtCenter, jtRight, jtJustify);
Prva tri su samoobjašnjiva: lijevo, centrirano i desno poravnanje. Četvrti, jtJustify, je puno poravnanje na oba ruba opisano ovdje, i to je vrijednost koju WideTextOutBox čita kako bi uključila razmak svjestan pisma
Poravnavanje odlomka u praksi
Potpun primjer stvara dokument, postavlja font, i ulijeva odlomak u okvir s punim poravnanjem. Isti kod poravnava latinični i CJK tekst bez promjene zastavice, jer svijest o pismu živi ispod razine API-ja
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, razmak redaka, širina okvira, visina okvira, tekst, poravnanje
Pdf.CurrentPage.WideTextOutBox(72, 72, 4, 380, 240, Body, jtJustify);
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Da biste isti blok nacrtali poravnat ulijevo, centrirano ili udesno, promijenite samo posljednji argument u jtLeft, jtCenter ili jtRight. Prelamanje, smještaj linija i povratna vrijednost ostaju isti. Izmjerena širina koja pokreće sve četiri staze dolazi iz GetWideTextWidth, upita širine svjesnog Unicodea koji ispravno mjeri WideString, dok bi starije mjerenje po bajtu pogrešno izmjerilo sve iza Latin-1, što je upravo ono što omogućuje da okvir na pravom mjestu prelama CJK tekst i tekst sa surogatnim parovima
Poravnanje je jedan sloj veće hrpe za oblikovanje teksta. Kad linija sadrži pisma koja preslažu ili spajaju svoje glifove, odluke o razmaku ovdje nadograđuju se na rad opisan u našem članku o oblikovanju teksta za složena pisma, a kad font nosi tipografske varijante koje želite odabrati, pogledajte kako upravljati stilskim alternativama OpenType GSUB. Sve se to isporučuje unutar HotPDF Delphi komponente za Delphi i C++Builder, uz šire API-je za tekst, raspored i dokumente pokrivene diljem ovog bloga