Fuld justering er det layout, der får en kolonne af tekst til at flugte på både venstre og højre kant, det udseende du forventer fra en trykt bog eller en formel rapport. Det er let at beskrive og overraskende let at tage fejl af, fordi svaret på spørgsmålet "hvor bliver den ekstra plads af" ikke er det samme for engelsk som det er for japansk, og fordi den naive måde at måle hver linje på gør en hurtig side til en langsom én. HotPDF giver dig script-bevidst justering gennem et enkelt boks-layout-kald, og under det kald sidder et skoleeksempel på en ydeevnerettelse, der er værd at forstå i sig selv
Denne artikel gennemgår begge dele. For det første den typografiske regel, der bestemmer, hvordan slæk fordeles for scripts med ordmellemrum versus scripts uden dem. For det andet den ændring i målingen, der reducerede omkostningerne for justering pr. side med ca. 80 gange uden synlig forskel i outputtet. Begge dele betyder noget, hvis du genererer dokumenter i store mængder og ønsker, at de skal læses som rigtig sætning, i stedet for monospaced output, der strækkes til at passe
Hvad fuld justering egentlig kræver
En linje med tekst, der er tegnet ved dens naturlige bredde, når næsten aldrig den højre kant af dens kolonne. Der er altid en rest, et slæk, mellem der hvor den sidste glyph ender, og der hvor kolonnens kant sidder. Venstrejustering efterlader det slæk til højre. Højrejustering flytter det til venstre. Centrering deler det op. Fuld justering fjerner det ved at udvide selve linjen, indtil begge kanter møder boksen, og den eneste ærlige måde at gøre det på er at skubbe glyfferne fra hinanden indefra
Den regel, der adskiller god justering fra dårlig, er hvor du placerer slækket. Et script, der skriver ord med mellemrum mellem dem, f.eks. engelsk og resten af den latinske familie, har naturlige sømme ved hvert ordmellemrum. At gøre disse mellemrum bredere er usynligt for øjet, fordi læserne allerede accepterer, at ordmellemrum varierer. Et script, der skriver uden ordmellemrum, f.eks. kinesiske Han-tegn, japanske kana eller koreanske Hangul, har ingen sådanne sømme. Der skal slækket spredes jævnt ud over glyffer ved siden af hinanden, hvilket er princippet, som japanske typesættere kalder kintou-waritsuke, jævn afstand. Hvis man placerer et stræk med ord-mellemrum i latinsk stil på en CJK-linje, eller propper hele slækket ind det ene sted, hvor en CJK-linje tilfældigvis indeholder et mellemrum, frembringer det floderne og kløfterne, der er kendetegnende for amatøroutput
Hvordan HotPDF beslutter, hvor mellemrummet går hen
HotPDF træffer den beslutning pr. mellemrum, ikke pr. linje. Når den justerer en linje, går den til hvert par af tilstødende glyffer og spørger, om der er en strækbar grænse imellem dem. En grænse er strækbar, når begge sider er et mellemrum eller et faneblad (tabulator), den latinske sag, eller når begge sider er CJK-brydbare karakterer, sagen for jævne afstande. Den tæller disse grænser, opdeler linjens slæk lige meget imellem dem og tilføjer andelen til hvert kvalificerende mellemrum
Konsekvensen falder helt naturligt. En engelsk linje har kun strækbare grænser ved sine ordmellemrum, så al slæk lander der, og ordene spredes, mens bogstaverne i hvert ord beholder deres naturlige afstand. En Han- eller kana-linje har en strækbar grænse mellem næsten ethvert par af glyffer, så slækket fordeles jævnt over hele linjen, præcis den jævne mellemrumsglyph-afstand, som disse scripts kræver. En linje, der er ét langt latinsk ord uden noget internt mellemrum, har slet ingen strækbar grænse, så HotPDF lader det være i dets naturlige bredde, i stedet for at rive ordet fra hinanden bogstav for bogstav. Den samme logik håndterer blandede latinske og CJK-forløb på én linje uden specialtilfælde, fordi beslutningen er lokal for hver grænse
Én grænse udelades bevidst overalt. Positionen efter den sidste glyph på en linje bliver aldrig behandlet som et mellemrum, fordi strækning der bare ville genindføre en rest på højre hånd, hvilket er det modsatte af justering
Hvorfor den sidste linje lades være
Den sidste linje i et afsnit er speciel, og at gøre det forkert er den mest almindelige fejl ved justering. Et afsnits sidste linje er normalt kort, oftest kun et par ord, og strækning af den til fuld kolonnebredde trækker disse ord hen over siden, så de danner en sparsom, knækket række. Korrekt typografi efterlader den sidste linje i dens naturlige bredde, justeret til venstre
HotPDF registrerer den afsluttende linje efter dens placering. Mens den pakker teksten om til linjer, ved den, hvornår linjen, den lige delte af, når frem til slutningen af den givne streng. Denne sidste linje sendes ud med almindelig venstrejustering og bevarer sin naturlige bredde. Hver linje foran den justeres til begge kanter. Hårde linjeskift, som du skriver i teksten, respekteres som skrevet, så en bevidst kort linje strækkes heller aldrig. Læseren ser en ren, rektangulær tekstblok, hvis sidste linje slutter naturligt, hvilket er, hvad øjet forventer
Den måleudgift, der gjorde justeringen langsom
For at justere en linje skal du kende dens nøjagtige bredde, og du skal kende hver glyphs fremskridt, så du kan placere det ekstra mellemrum præcist. Den første implementering fik disse tal på den mest oplagte måde. Den målte hele linjen med en fuld Unicode-breddeforespørgsel og målte derefter præfiks efter præfiks for at gendanne hver glyphs fremskridt ved hjælp af differens. For en linje med N glyffer er det N+1 opkald til målemotoren, og hvert opkald er et fuldt GDI-rundtur, der beder operativsystemet om at forme og måle tekst, og aflevere svaret tilbage
Per linje lyder det billigt. Hen over en side er det ikke det. Tag en tæt A4-side med brødtekst, ca. 45 linjer på ca. 80 tegn hver. Med N+1 rundture pr. linje, er det ca. 81 rundture for hver linje og groft sagt 3.645 for siden, som næsten alle bruges på at genmåle tekst, som motoren allerede havde kigget på øjeblikke tidligere. Ved et batchjob, der producerer tusindvis af sider, dominerer det ekstra arbejde layout-tiden, og hver rundtur krydser grænsen mellem din proces og grafikundersystemet
Et kald i stedet for N plus et
Rettelsen er en af de ændringer, der ser små ud, men som betaler sig fuldt ud. GDI kan allerede rapportere en strengs samlede bredde og hver glyphs placering i én enkelt forespørgsel. HotPDF udstiller det gennem GetWideCharAdvances, som fylder et array med hver glyphs naturlige fremskridt, inkl. kerning, og returnerer den samlede bredde i ét kald snarere end N+1. Justeringsrutinen, _HPDFEmitJustifiedWideLine internt, beder om alle fremskridtene på én gang, udregner slækket, fordeler det ud på de strækbare grænser og udsender linjen
For den samme A4-side falder pr.-linje-målingen fra ca. 81 rundture til en enkelt, så siden falder fra groft sagt 3.645 rundture til ca. 45, næsten en faktor 80 fald. Outputtet er byte-for-byte det samme, fordi intet omkring målingen blev ændret, andet end hvor mange gange der blev bedt om den. Den samme GDI-motor, de samme skrifttype-måledata, den samme kerning leverer de samme tal. Det var kun antallet af rundture, der faldt. Når en måling allerede er korrekt, er den rette optimering at holde op med at bede om det gentagne gange, og ikke at anslå det
Hvordan linjen når ud på siden
Når slækket er blevet tildelt, sender HotPDF linjen ud med ExtTextOut og en pr.-glyph-fremskridtsarray, Dx-arrayet. Hver indgang er afstanden fra en glyphs startpunkt til det næste, som er glyphs naturlige fremskridt plus dens del af slækket, når der følger en strækbar grænse efter den. Dette knytter sig direkte til PDF-billedmodellen. Placeret tekst skrives med TJ-operatoren, som er et array, der fletter glyphs-forløb med eksplicitte vandrette justeringer, og Dx-værdierne bliver netop disse justeringer. Det er grunden til, at den ekstra plads lander imellem glyfferne på præcise subpoint-positioner, og at det ikke forfalskes med utfyldningstegn (padding), og grunden til, at en justeret HotPDF-linje måles korrekt, hvis et downstream-værktøj læser den tilbage
Du kalder ikke selv ExtTextOut til justerede afsnit. Udgangspunktet er WideTextOutBox, der ombryder en Unicode-streng til en boks og benytter den justering, du beder om. Den opdeler teksten i linjer, der passer ind i boksens bredde, placerer hver linje ned langs boksens højde, og returnerer antallet af tegn, det lykkedes den at få plads til, inden den løb tør for lodret plads. Justeringen vælges ud fra justerings-optællingen
type
THPDFJustificationType = (jtLeft, jtCenter, jtRight, jtJustify);
De tre første er selvforklarende; venstre, centreret og højre justering. Det fjerde, jtJustify, er den fulde justering mod begge kanter som er beskrevet her, og det er den værdi WideTextOutBox læser for at tænde for script-bevidst afstand
Justering af et afsnit i praksis
Et komplet eksempel skaber et dokument, sætter en skrifttype og hælder et afsnit over i en boks med fuld justering. Den samme kode justerer latinsk og CJK tekst uden nogen flag-ændring, fordi script-bevidstheden findes inde under API'en
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;
For at tegne den samme blok som venstrejusteret, centreret eller højrejusteret, skal du bare skifte det endelige argument ud med jtLeft, jtCenter eller jtRight. Ombrydningen, placeringen af linjerne og returværdien er den samme. Den målte bredde, der bruges til alle fire veje, kommer fra GetWideTextWidth; den Unicode-bevidste forespørgsel for bredden, der måler en WideString korrekt, dér hvor den ældre bytevise måling ville måle alt forbi Latin-1 forkert, hvilket er det, der fra starten får boksen til at ombryde CJK og surrugatpar-tekst på rette sted
Justering er et af de lag i en stak af funktioner for tekstformning. Når en linje rummer scripts, der omarrangerer eller forbinder deres glyffer, sidder afstandsbeslutningerne her på toppen af arbejdet, som er beskrevet i vores artikel om kompleks script-tekstformning, og hvis en skrifttype er udstyret med nogle typografiske varianter, som du ønsker at benytte, kan du se hvordan OpenType GSUB driver stilistiske alternativer. Det hele udkommer sammen med HotPDF Component til Delphi og C++Builder, sammen med det bredere udsnit af tekst-, layout- og dokument-API'er, der er dækket via denne blog