Poziv koji postavlja tekst na PDF stranicu je jednostavan. Date AddText metodi string, font, veličinu i poziciju, i glifovi se pojave. Ono što ne uradi je to da vam kaže koliko će taj string biti širok nakon što bude iscrtan, niti prelomi dug string u nekoliko redova. Jedan jedini poziv nacrta jedan tok (run) teksta na jednoj poziciji. Ukoliko je tok širi od kolone u koju ste želeli da ga smestite, on prosto prelazi preko ivice, i ništa vas u tom crtačkom pozivu ne upozorava. Onog trenutka kada poželite pasus a ne samo običnu labelu, deo slagalice koji nedostaje je širina stringa u odabranom fontu i veličini, izmerena pre nego što se obavežete da ga ucrtate na stranicu
Ovo je klasičan problem raspoređivanja (layout problem). Da biste prelomili pasus u kolonu, morate znati, reč po reč, koliko će vodoravnog prostora (horizontal space) zauzeti svaka linija kandidat, i morate to znati pre nego što išta nacrtate. Prelom reči (word wrap) predstavlja mernu petlju umotanu oko crtačkog poziva, a binding (vezivanje koda) koji vrši isključivo iscrtavanje daje vam samo drugu polovinu priče. Podrška za merenje teksta u komponenti PDFium Component zatvara taj jaz sa dve funkcije, MeasureText i MeasureTextWidth, koje prijavljuju renderovan opseg stringa a da pri tom ne stave nikakav trag ni na jednu stranicu
Zašto je merenje pomoćnik klase (class helper) a ne nova metoda u TPdf
Podrška za merenje stiže kao Delphi class helper za TPdf, i on živi u svojoj sopstvenoj jedinici (unit), radije nego da se nove metode zakucaju za samu klasu TPdf. Class helper predstavlja mogućnost samog jezika (language feature) koja dozvoljava kačenje (attach) novih metoda na postojeći tip izvan njegove matične deklaracije. Čim unit uđe u vidokrug (scope), nove metode se pozivaju isto kao da sasvim pripadaju klasi, pa se tako helper metoda čita kao Pdf.MeasureTextWidth(...), bez zasebnog objekta koji bi morao da se konstruiše ili prepušta (pass around)
Razlog za ovakvo raspoređivanje (layering) u slojevima je separacija. Centralni tip TPdf ostaje netaknut takav kakav jeste, bez dodavanja novih polja niti diranja već uspostavljenih potpisa (signature), tako da projekat kom nikada ne zatreba layout algoritam sa sobom ne mora nikad ni da vuče kod za merenje. Onaj projekat kome zaista zatreba, u uses klauzulu doda tek jednu jedinicu i metode se odmah pale (light up). Sposobnost (capability) postaje isključivo stvar izbora (opt-in) pri granularnosti jednog pojedinačnog unit-a, što predstavlja najčistiji način ekstendovanja tipa koji ne posedujete niti pak želite da poremetite
uses
PDFium, FPdfView, FPdfEdit,
FPdfMeasure; // the helper unit; brings MeasureText into scope on TPdf
// With the unit in scope the methods read as members of TPdf:
var
W, H: Double;
begin
Pdf.MeasureText('Subtotal', 'Helvetica', 11, W, H);
// W and H are now the rendered width and height in PDF user units
end;
Merenje bez dodirivanja stranice
Merenje mora u potpunosti biti slobodno od bilo kakvih pratećih efekata (side effects). Ono ima zadatak da prijavi vrednost širine (width) a da ne ostavi ama baš išta za sobom, jer se proces poziva veoma mnogo puta (many times) tokom donošenja odluke u layout proceduri pa tako, u suprotnom, stranica mora da izgleda apsolutno baš onako kako bi izgledala da niste proveli ni sekund u nekakvom merenju. Tehnika (technique) koja sve ovo čini mogućim u stvari jeste izgradnja takozvanog tekst objekta (text object) kog jednostavno upitate za dimenzije, i potom odbacite (throw away) a pre nego se uopšte zalepi na neku od stranica
Sledosled se sastoji od četiri poziva upućenih na PDFium API. FPDFPageObj_NewTextObj instancira jedan tekstualni objekat naspram (against) samog dokumenta, i to nakon obezbeđenog imena tipa fonta i željene veličine. FPDFText_SetText setuje na string i potom ga prosleđuje objektu na nošenje (carries). FPDFPageObj_GetBounds očitava bounding box celog objekta. I konačno, FPDFPageObj_Destroy uništava i oslobađa sam taj objekat iz upotrebe. Ključna stvar (Crucially), ne postoji apsolutno ništa unutar celog pomenutog sleda (sequence) što uopšte dodiruje stranicu, kao ni page-insertion API ruter. Sve je urađeno, kreirano, upitano i uklonjeno, dakle, u potpunoj izolaciji; stoga, dokument po povratku kontrolera ka aplikaciji biva nedirnut, a vi za uzvrat dobijate tek privremenu izviđačku probu (throwaway probe) isključivo u vidu onih četriju brojeva u pomenutom bounding box-u
Ovo predstavlja robustan (robust) način izvođenja iz razloga što PDFium (programski) uopšte ni ne izbacuje na površinu zgodan set za (advance width) širine pomaka meren glif po glif (per-glyph) koji biste naknadno vi sami mogli sumirati na nekom svom mestu. Merna mehanika (metrics) za glifove zapravo zavisi u najvećoj meri od programa za font (font program), kao i od kodiranja, pa i načina u kom PDFium lično učitava font; pri svemu ovome napominjemo da naprosto ne posedujete (there is no) ikakav public-facing (javni) API kontroler kojim biste iznudili to takozvano unapređeno koračanje (advance) po karakterima (character in a string). Nasuprot tome, kada govorimo o pravoj meri u obliku bounding box-a vezanog ne za tamo neke glifove no upravo pravi tekst objekat na sceni u momentu izvođenja proračuna na bazi mehanizma za proces crtanja na licu mesta (lay the glyphs out for drawing), ovim dobijate oslikani pravi obim umesto grube aproksimacije (approximation). Konstrusanisanje ovog privremenog i namenski osmišljenog (disposable) objekta radi provere njegovih granica predstavlja zaista najpouzdaniju opciju merenja obezbeđenu u celoj biblioteci
// The shape of MeasureText, expressed against the verified PDFium calls.
// A text object is built, measured, and destroyed; no page is involved.
procedure TPdfMeasureHelper.MeasureText(const Text, Font: WString;
FontSize: Single; out Width, Height: Double);
var
TextObject: FPDF_PAGEOBJECT;
L, B, R, T: Single;
begin
Width := 0;
Height := 0;
if Self.Document = nil then
Exit;
TextObject := FPDFPageObj_NewTextObj(Self.Document,
FPDF_BYTESTRING(AnsiString(Font)), FontSize);
if TextObject = nil then
Exit;
try
if FPDFText_SetText(TextObject, FPDF_WIDESTRING(WideString(Text))) = 0 then
Exit;
if FPDFPageObj_GetBounds(TextObject, L, B, R, T) <> 0 then
begin
Width := R - L;
Height := T - B;
end;
finally
FPDFPageObj_Destroy(TextObject); // probe discarded, page untouched
end;
end;
Koordinate i jedinice rezultata
Kao odraz (comes back) ovog bounding box-a dolaze u susret (as four edges) četri osnovne ivice – levo, dno, desno, gore – i ovde dve dimenzije jednostavno istupaju obavljanjem akcije oduzimanja (subtraction). Širina predstavlja iznos u kom je 'desno minus levo', dočim sa druge strane visina predstavlja iznos u smislu vrednosti relacije 'gore minus dno'. Uzevši skupa u obzir obe, one su izražene usključivo kroz tkz. korisničke jedinice PDF fajla u kom se radi (PDF user units), dok je ta tkz. jedna merna jedinica propisana (where one unit is) tačno u iznosu u kom iznosi jedna sedamdeset-druga inča na istom onom koordinatnom polju u kojem naposletku postavljate i fiksirate položaj pomenutog teksta neposredno na ciljanoj stranici; shodno time na ovom stupnju radova prosto ne egzistiraju pritajene, skrivene 'uređaj-jedinice' poput piksela (pixel). Veličina koja npr. nosi vrednost od tkz. 36 širinskih mernih polja, prevedeno će značiti jednostavno 'pola inča' (half an inch) stranice, pa i bez obzira na uvek prateću rezoluciju eventualnog crtanja u datom i bilo kom drugom trenutku (rendering resolution)
Prilikom osmatranja vertikalnih putanja (vertical axis), one zaista operišu onako kako je to PDF originalno prefinio (defines it) pri čemu se parametar po Y-osi u realnosti na više (upward) linearno umnožava, a zbog čega smo ranije pominjali da visina (height) predstavlja rezultat proistekao iz formule 'top minus bottom', i nikako preokrenute kontra varijante. To je onaj krucijalan detalj koji biva od neprocenjive važnosti ukoliko sa onim ranijim kursorom zaroni dole u polje same kolone na niže. Kao obavezan korak vi naprosto morate najpre da odmerite linijsku visinu po formaciji, te da oduzimanjem tog merenja iz postojeće bazne (current baseline) crte detektujete sledeću jer uistinu "nadole po strani" za tu osu ustvari znači smer prema umanjenim vrednostima na Y ordinati. Ukoliko npr. imate pred sobom cilj crtanja koji za primat nudi ne papir po sebi no tkz. površinu na ekranu (screen rather than paper), to prevashodno iziskuje pretvaranje ovakvih korisničkih PDF mera unutar kordinatnog okruženja pretvaranjem (convert user units to device pixels) prema samoj rezoluciji monitora po principu relacije formulisane kao: tkz. jedinica na bazi korisničke vrednosti namnožena parametrima za lokalizovani DPI u sistemu a onda prosta radnja deobe sa pomenutih konvertovanih u uobičajenim odnosima od oko broja tkz. 72, i za takav račun na izlazu proističu isključivo ti očekivani 'pikseli' pa potom takva dimenzija po koloni na nivou te propisane izabrane tačke (set in points) može onda podeliti u rečenom radu prema već izmerenom propisanom nizu obezbedivši priliku u kom možete smelo formirati onaj već objašnjeni i nametnuti lom (where the break goes)
Šta se dešava u slučaju degenerisanog (degenerate) ulaza
U slučaju ovakvih postavki funkcije su ispisane sa tendencijom ispadanja iz koda – a sve u izrazitoj potpunoj tišini operacija (fail quietly). Znači u onim mogućim neželjenim uslovima po kojima naposletku zapravo nema ni reči o predatom validno otvorenim i prisutnim dokumentom izvan granice regularnosti a i ako ne postoji ni uspostavljena solucija instanciranja na osnovu uobičajenog tekstualnog (text object) sistema instanciranja po redu, izlaz tj rezultat biva iskazan vrednošću na potpunoj 'nuli' umesto preuzimanjem opcije lansiranjem nekog neposrednog programskog zastoja (raised exception). Upravo su širina i visina već uspostavljene (initialised to zero) pri inicijalnom kodu operacija a vrednosti će tek naknadno bivane prekucane (overwritten) tek onda po povratku ukoliko pomenuta merna oblast unutrašnjeg boksa pođe za rukom sa stoprocentnom operabilnosti unutar uspeha. Proslieđen potpuno besadržajni (empty string), pomenuti dokument u nedostajanju (missing document), ili iole specifični tip pri zahtevanom font tipu, nepoistovećeni pod uslovima za biblioteke koja se bori u uspostavljenom sistemu pa ne ume uspostaviti kao (cannot resolve into an object) standardni regularni objekat – svaka varijanta od pomenutih prosto uzvraća povratnu formaciju tek čistom, pukom "nulom" i obezbeđenjem otklanjanja zastoja na sistem (returns zero rather than throwing)
Tim izborom cela petlja zadužena oko prikupljanja merenja je jednostavno osigurana po obliku odvijanja. Naime to se ogleda zato što takve tkz. procedure za prebrojavanje u kodu (loop) koja pretrčavaju obavljajući posao prolaze preko niza u broju hiljada po redu reči u sekundi prosto ne odražava logično i pouzdano rešenje ukoliko pokušamo upustiti rukovanje uz (not the place for exception handling) izbacivanjima grešaka prilikom prebačaja obavljenog u vidu svake radne interacije po sebi (every iteration). Na nesreću cena za ovako donetu (cost is that the caller) olakšicu prevashodno nosi samo i jedino klijent od kojeg se traži kontrola izvedena kroz (carries the check). Zapazite, svaka 'nula' širinskog dela je pre signal upozorenja za zaustavljanje, to nipošto (not a fact about the text) ne odražava činjenice dobijenim o datom tekstu po formi, dakle svaka linija sa potencijalnom funkcijom vršenja deobe uz pomoć tkz. dimenzijske širine (divides by a measured width) ukoliko i bez dvoumljenja ukoliko nalaže očekivane prisutne isključive uspostavljene vrednosti koje nužno nose nula-vrednost (guard against zero) pri bilo kojoj sumnji poverenja u sam tok merne aktivnosti ukoliko iziskuje izričito (positive value). Svaka nula prosto mora stoga pri svakoj varijanti prevashodno nositi značaj propisan opcijama reči: 'nemogućnost' tj (could not measure) pa je ugovor tada sasvim osiguran a pri pogledu (the contract is clear); opcija ignorisanja kao loša navika ovde od tkz. degenerisanog (degenerate input quietly) neispeglanog unetog parametra, automatski pridonosi stvaranjem dizajnerskog promašaja po bazi layut postavki, dakle, stvoriće Vam isključivu vizualizaciju kolonom glifova zbijenim jedan uz drugi naslonjenim ili upropaštenim naslaganjem vizuelnog karaktera pri poklapanju onog tkz (overlapping glyphs) stanja
Pohlepni (greedy) prelom reči (word wrap) izgrađen na merenju
Imajući u vidu i čvrsto raspolaganju postojanje one tkz funkcije širine u sopstvenoj upotrebi unutar toka izvršenja rada (function in hand), uspostavljanje algoritamske sekvence preloma (word wrap) obavlja se i predstavlja ustvari formu ukratko pohlepne iteracije tkz u kodu (short greedy loop). Zatim za to podelite unutar niza sve one pasuse prevashodno na pojedinačne zasebne rečenične reči; dok istodobno održavate u fokusu tkz aktuelnu radnu crtu u toku praćenja pa onda po pravilu izričito merenjem za bukvalno svaku detektovanu pomenutu reč (each word you measure) proveravate kakvo bi to stanje donelo kao oblik same tkz. linije (what the line would be) tek nakon potencijalnog spajanja pri premeštanju tj dodavanju (appended that word). Kroz fazu u kojoj tkz ogledni i testirani probni obim testiranog retka sve uredno upada ukalupljen bez prolivanja ili napuštanja samog prostranjenog formata po zadatoj osi same merene i posmatrane širine odnoseće kolone prosto (fits the column width) vi na miru dopunjavate tj. gomilate dodavanjima u istom smeru dalje na toj linijici za čitanje po redu tkz. obavljanjem; u momentu izlaska u preliv i eventualno kada se to izlije, tačnije (when it would overflow) automatski, naprosto bacate pod opciju pranja aktuelne formirane testirane i utemeljene tekuće linije sve ujedno uparujući to pozivom AddText po formaciji pa onda oslobađanjem otvarate novo prateće redno poglavlje upotrebom na preostali neugrađen u nizu komadić u formi te upravo prelivene ostale (word that did not fit). Količina i samo nagomilavanje ovde po tom režimu na taj način proistekla isključivo samo korišćenjem MeasureTextWidth; i onda po definiciji stoga na stranicu upada (reaches the page) apsolutno u redu jedino onda ona propisana već formulisana ugrađena po ubedljivosti unapred provere koja nam pri propisanoj testirano formiranoj relaciji to izuzetno nesumnjivo već ukalupi u meru (confirmed fits)
procedure WrapParagraph(Pdf: TPdf; const Para, Font: WString;
FontSize: Single; X, TopY, ColumnWidth, LineHeight: Double);
var
Words: TArray<string>;
Line, Trial: WideString;
I: Integer;
Y: Double;
begin
Words := string(Para).Split([' ']);
Line := '';
Y := TopY;
for I := 0 to High(Words) do
begin
if Line = '' then
Trial := Words[I]
else
Trial := Line + ' ' + Words[I];
// Measure the candidate line before drawing anything.
if (Line <> '') and (Pdf.MeasureTextWidth(Trial, Font, FontSize) > ColumnWidth) then
begin
Pdf.AddText(Line, Font, FontSize, X, Y); // flush the line that fit
Y := Y - LineHeight; // Y decreases going down
Line := Words[I]; // overflowing word starts next line
end
else
Line := Trial;
end;
if Line <> '' then
Pdf.AddText(Line, Font, FontSize, X, Y); // flush the final line
end;
Algoritam meri i testira pre toga formiran (measures the trial line) radije nego tkz procesiranjem same reči i potom uspostavljanjem merenja pre nego po pukom dodavanjima na onaj ukupan iznos na celoj sumi; iz jednostavnog razloga gde prosto ona konačna reč (width of a line) u iznosu apsolutno i sasvim nema ništa zajedničko tj uistinu nipošto i ne ispoljava samo obično obavljanje iznosa odnosa ukupan prosti sum na baziranim i uparenim odabranim nađotim i skupljenim jedinkama unutar te tkz iste redovne po nizu sume po formiranim i prebrojenim unutar opsega te (sum of the widths of its words). Tako npr. tkz onaj odeljujući neštampani međusobni razmak lociranih tj prisutnih i pozicioniranih razmaka, stvara i nosi odlikovani zbir a formirani odmereni pređeni red proračunatog isključivo sticanjem merila taj podatak detektuje izvanredno (captures that directly). Propisano obično i ustaljeno 'pohlepno pravilo' ukoliko (greedy rule) i po zahtevanom iznosu i opsegu obezbeđuje utrpavanje apsolutno niza tih reči sve ukoliko sama propisana posmatrana i ograničena kolonar-širina tako dopušta i po proceduri sprovodi odlomljavanjem preostalog formiranjem tj pucanjem reza sve odmah onamo odmah zatim posle uočavanja da bi to probilo formu; te da sve što se utemeljilo i upasovalo (break at the last one that fits); pa shodno u formaciji, upravo formulišući istovetan po algoritmu oblik ukalupljivanja onoga posmatranog procepa iz prostora od i obavljanja pukog iscrtavanja golim navedenim načinom poziva (raw AddText) naprama tj u obrisu sa onim suprotno utemeljeno proračunato kreiranim tj na delu prave tkz. pune varijante punopravnog niza onog takozvanog tkz paragrafa. Pa i crtački reon algoritamskog (drawing call) pri radnom zadatku uopšte ni nije onaj teži složeni i nezahvalni deo obavljanja. Jedino ono što bi pod obavezu nužno prethodilo merenjem pri samim i svim ujedno uslovima preuzimanja obaveze; ukratko to upravo leži tek unutar iznošenja na svetlosti iznetog propisanog tkz pomagača pri ovom poslu (what the helper provides)
Gde se ovo uklapa
U celoj posmatranoj i proračunato formulisanoj proceduri sam taj odmeravajući merni pristup funkcioniše u sredini tačno na i po granici tkz. sloja između generativnog uspostavljanja teksta za prosljeđivanje sadržaja iz njega tj dovođenja tkz render procesiranja i finaliziranja samog algoritma tj procesa i tu po samom poretku vrlo odlično stupa u formaciju ukalupljivanja tkz sprezanjem (pairs naturally) ka posmatrano radnom dokumentarnom tipu od kreiranja ni na čemu (from-scratch document workflow). Ukoliko uspostavljate tj tkz sklapate tkz proces pri ispunjenju spiska po planiranim postavljenjima tj pri uspostavljanju sadržaja unutar uloge redoslednosti tj tkz spajanjem nizanja obrisom reči tkz kreiranog poretka za postavljanje tekstualnog tipa nadasve pri samom procesu od kreiranja preuzimanjem posmatrano u smislu početne nulte radne okoline tek onaj neophodni tkz. primarni rad nalazi se obrađen na i u članku formulisanim u kreiranje PDF dokumenata od nule (from scratch) korišćenjem komponente PDFium component u Delphiju, u i preko kojega sama procedura sa postavljanom primenom i rešenjem opisanom unutar onog pominjanog AddText rutiranja za izradu a tu sa istim preuzimanjem rada za operativno podešavanje samog tipa formatiranja papira biva tako u obimu i formi (covered in full) pokriveno po najosnovnijem redosledu operativnosti. U onim trenucima u radnoj obradi kada samo lice primenjenog oblika (font you are measuring matters) postavljanom tkz slovnom upotrebom fonta dobija na uobličenju a na podjednakoj primernoj važnosti formulišućeg parametarskog polja pa baš koliko pri svem posmatranom a i tkz ukoliko bitnom onom istom stringu – pri razlozima posmatranog proizašlih jedino u tome da sama dimenzija merenja po propisanom tkz merilu glifova po opsegu odnosi prevashodno jedino prema preuzetom uspostavljenom i detektovanom operacionom dizajnu uspostavljenom od izabranog tipa (face) za onaj sprovedeni postupak u analiziranje PDF font property postavki sa komponentom PDFium u Delphiju, sa usmerenom i ukazanom primenom pa i o onome na koji način uspostavljena softverska aplikacija i rečena zadata spomenuta propisana i objašnjavana biblioteka uspešno uzvraćanjem u procedurama uzima u vid posmatranog proračunavanja pri prijavi u sistem tkz formacijskih (reports the font information) informacija vezanim u nizu ka poslatim usmeravanim uspostavljanim merama iz unutar ranijih već sprovedenih tkz opisanih tkz mernih propisanih okvira za granično navođenje u tipu onih formata naziva bounding obuhvaćenih prostranih okvira. Apsolutno obe pomenute procedure formiraju bazu bazirajući sistem uspostavki iz posmatranog izgrađenom tipu i primenjivanih za vezivanje programiranih (binding) poziva, tj iz same i jedine predložene biblioteke u PDFium Component namenjenu u tipu tkz u obliku i opciji posmatrane varijante po programerskom formiranju tkz. tipa programera propisanog na relaciji ka kodu i rešenju namenjenu prevashodno za programske alate tkz programskih koding rešenja na osnovnom bazirano i formirano za namenu po Delphiju te ujedno rešenja obazirući se na tkz operativu i rad sa alatima okrenutim tkz Lazarus opciji otvorenog okruženja u open source kod varijantama – a sve navedeno upravo na propisanim navođenim mestima po ugrađivanju unutar sprovođenja i usmeravanja operativnosti pri iznetim razrađenim procesnim rešenjima uspostavljanim u posmatranoj i uspostavljenoj komponenti na usmeravani i tkz helper merni (measurement helper ships) pomoćnik sa uspostavljanom propisanom opcijom dopreme i rutiranja tik paralelno preuzetih opcija tkz. operacije naspram samog API formiranja uspostavljanih funkcija sa isporučivanjima alata (ships alongside the document, page, and text APIs) izgrađenih unutar procesiranja nad tim naloženim PDF formatom u i radnom postupku nad tom tipskom rešenju iz formirane pri dokument proceduri te sa izgrađenim sistemima operisanja iz oblika za tip formata pri formiranju po stranicama, kao na i opcijom teksta opisanih već priličitim navodnim iznetim primerima prikazivanim negde po onome opisanom na mestima sa izvučenim delovima formiranim i postavljenim po ovom obliku navoda propisanih na čitavom okruženju bloga