Besedilo v PDF ne sedi samo na osi X in raste desno, dokler ne doseže robnika. Vsaka pisava nosi tabelo napredovanj -- razdaljo, ki jo vsak znak potisne risalno kazalo naprej -- in te razdalje se razlikujejo med pisavami, stilom (odebeljenost, poševnica), velikostjo in parami kerningov. Preden postavite katerikoli odstavek, ki ga nameravate razlomiti na vrstice, morate izmeriti besede. Izmeriti pomeni poklicati enako logiko, ki jo bo kalkulator upodabljanja pozneje uporabil za umeščanje črnila, in dobiti rezultat v enakomernih enotah: točkah, ki se preslikajo nazaj v koordinate PDF
PDFium VCL to izpostavlja prek TPdfMeasureHelper, pomožnega razreda, ki meri brez pisanja na katerokoli stran. Preostanek tega članka pojasnjuje, zakaj je ta razred pomožni razred in ne metoda na TPdf, kaj je v rezultatu, kako se obnašajo degenerirani vhodi in kako zgraditi prepromo neumno pohlepno zavijanje besed na vrhu merjenja
Zakaj je merjenje pomožni razred in ne nova metoda na TPdf
Prva zasnova bi bila merjenje dodati kot metodo na TPdf: Pdf.MeasureText(Font, Size, Text). Težava je v tem, da merjenje ne potrebuje odprte strani, ne potrebuje dokumenta in ne potrebuje ničesar iz stanja TPdf. Vse, kar potrebuje, je opis pisave in niz za merjenje. Dodajanje te metode na TPdf bi napačno nakazovalo, da merjenje in stran-IO delujeta skupaj, kar bi stranko spodbudila k držanju nepotrebno odprtega dokumenta samo zato, ker potrebuje širino za svojo logiko postavitve
Pomožni razred loči skrbi jasno. TPdfMeasureHelper sprejme opis pisave pri gradnji, izpostavi meritve in se sprosti, ko jo stranka ne potrebuje več, neodvisno od katerega koli dokumenta. Vzorec je, ko stranka meri celoten odstavek z enim samim objektom pomočnika, ne da bi bil kateri koli TPdf odprt
var
Helper: TPdfMeasureHelper;
W: Double;
begin
Helper := TPdfMeasureHelper.Create('Helvetica', 12.0);
try
W := Helper.MeasureTextWidth('Hello, world');
Writeln(Format('%.2f pt', [W]));
finally
Helper.Free;
end;
end;
Merjenje brez dotikanja strani
Ko imate instanco TPdfMeasureHelper, so meritve ene same metode: MeasureText vrne celotno metriko -- škednje, vzpon, spust in napredovanje -- medtem ko je MeasureTextWidth bližnjica, ki vrne le napredovanje (vodoravno razdaljo, ki jo niz zaseda). Za prelom besed je napredovanje edina vrednost, ki jo potbujete, in MeasureTextWidth se izogne razpakovanju celotne metrike za vsak klic
Nobena metoda ne dotakne odprte strani in ne piše na nobene notranje podatkovne strukture, ki bi bile skupne z drugimi klici. Vsi klici merjenja so pri dani instanci pomočnika varni za zaporedni klic toliko časa, kolikor obstaja instanca
var
Helper: TPdfMeasureHelper;
Metrics: TPdfTextMetrics;
begin
Helper := TPdfMeasureHelper.Create('Times-Roman', 14.0);
try
Metrics := Helper.MeasureText('Ag');
Writeln(Format('ascent=%.2f descent=%.2f advance=%.2f',
[Metrics.Ascent, Metrics.Descent, Metrics.Advance]));
finally
Helper.Free;
end;
end;
Koordinate in enote rezultata
Vse vrednosti, ki jih vrne TPdfMeasureHelper, so v točkah PDF -- enaki koordinatni sistem, ki ga TPdf.CurrentPage.TextOut in klici poti used za umeščanje vsebine. Ena točka je 1/72 palca. Vzpon je razdalja od osnove do vrha tipografske pokrovne meje, negativna vrednost je redkost (pisave z deskendenti, ki se razširjajo nad linijo z velikimi črkami, kar je nenavadno). Spust je razdalja od osnove navzdol do spodnjega roba deskendenta, ponavadi negativna, ker koordinate PDF merijo od dna strani navzgor. Napredovanje je celotna vodoravna razdalja od začetnega položaja do položaja za zadnjim znakom, kar je vrednost, ki jo dodate X-koordinati, da dobite začetni položaj naslednjega niza
Ker so enote točke, vrednost napredovanja neposredno primerljiva z X-koordinato, ki jo posredujete TextOut. Ce izmerili besedo in dobili napredovanje 54,3, bo postavitev besedila z TextOut z levim robom pri X = 100 in to besedo pritisnila desni rob blizu X = 154,3 -- ne 155 ali 153. To ravnanje z meritvami kot neposrednimi koordinatami je tisto, kar naredi izračun postavitve predvidljiv
Kaj se zgodi na degeneriranem vhodu
Prazen niz vrne napredovanje nič in metrike nič za vzpon, spust in napredovanje. Niz, sestavljen izključno iz presledkov, vrne napredovanje za eno ali več enot presledka, odvisno od skupne širine presledka pisave -- presledki niso ničelne širine, kar je vredno omeniti, ker jih koda za prelom besed pogosto odreže in pričakuje, da bodo meritve v skladu. Niz, ki vsebuje znake, ki jih pisava ne vsebuje, zamenja manjkajoče glife z nadomestnim glifom -- navadno praznina ali kvadrat -- in napredovanje za tiste znake je napredovanje nadomestnega glifa, ne nič. Napredovanje za katerikoli niz je torej vedno nenegativno in za neprazni niz vedno pozitivno
Pohlepno zavijanje besed, zgrajeno na merjenju
Pohlepni zavijalec besed razlomi niz v vrstice: doda besede na tekočo vrstico, dokler bi naslednja beseda prekoračila širino stolpca, nato pa začne novo vrstico. Merjenje zagotavlja kriterij za prekinitev vrstice. Za niz z besedami, ločenimi s presledki, je postopek: tokenizirajte niz na besede, iterira čez besede z akumulacijo napredovanj in vsakič, ko kumulativna vrednost presega širino stolpca, prekinite in začnite novo vrstico
function WordWrap(const Text: string; Helper: TPdfMeasureHelper;
ColWidth: Double): TStringList;
var
Words: TStringList;
Line: string;
LineW, WordW: Double;
I: Integer;
begin
Result := TStringList.Create;
Words := TStringList.Create;
try
Words.Delimiter := ' ';
Words.DelimitedText := Text;
Line := '';
LineW := 0;
for I := 0 to Words.Count - 1 do
begin
WordW := Helper.MeasureTextWidth(Words[I]);
if (LineW > 0) and (LineW + Helper.MeasureTextWidth(' ') + WordW > ColWidth) then
begin
Result.Add(Line);
Line := Words[I];
LineW := WordW;
end
else
begin
if Line <> '' then
begin
Line := Line + ' ' + Words[I];
LineW := LineW + Helper.MeasureTextWidth(' ') + WordW;
end
else
begin
Line := Words[I];
LineW := WordW;
end;
end;
end;
if Line <> '' then
Result.Add(Line);
finally
Words.Free;
end;
end;
Kje to sodi
Merjenje je predpogoj za katerikoli modul postavitve, ki gradi vsebino PDF programatično in ne zaupa upodabljalniku, da zaokroži besedilo. Obrazci s fiksnimi polji, etiketni natiski z variabilnimi dolžinami besedila, generatorji računov in poročil -- vsi morajo vedeti, koliko besedila ustreza regiji pred pisanjem. TPdfMeasureHelper je edina pot, ki vrne to vrednost v enotah, ki se neposredno preslikajo v pisalni API brez prilagoditve ali kalibracije. Pomožni razred se dobavi kot del komponente PDFium VCL za Delphi in C++Builder, skupaj z API-ji za upodabljanje, ekstrakcijo in merjenje, obravnavanimi drugje na tem blogu