Svaki vidljivi karakter u PDF-u nosi referencu na font koji ga je nacrtao, a PDFium Component vam omogućava da pratite tu referencu nazad do objekta fonta i pročitate šta on zna. Jedinica pristupa je karakter, a ne dokument: birate karakter prema njegovom indeksu u tekstu stranice i tražite naziv porodice (family name), osnovno ime (base name), težinu (weight), ugao kurziva (italic angle) i da li je sam font zapravo prenesen unutar datoteke. Ovo poslednje svojstvo je ono što većina analiza zapravo traži, jer ugrađeni font putuje sa dokumentom, dok je neugrađeni samo obećanje da mašina čitaoca slučajno ima instaliran isti font
Komponenta ih izlaže preko istih TPdf i TPdfView objekata koje koristite za renderovanje i ekstrakciju teksta. Ne postoji poseban objekat "tabela fontova" koji treba otvoriti. Nakon što se tekst stranice parsira, svojstva fonta vise sa indeksa karaktera i čitate ih glif po glif. Taj dizajn odgovara načinu na koji PDF uopšte čuva informacije: jedna stranica može da promeni fontove desetinama puta, a jedini iskren odgovor na pitanje "u kom fontu je ovaj dokument" jeste "zavisi na koji karakter mislite"
Čitanje fonta iza jednog karaktera
Najmanja korisna operacija je uzimanje indeksa karaktera i izbacivanje svega što vam PDFium može reći o njegovom fontu. Svako svojstvo fonta na TPdf i TPdfView je indeksirano pozicijom karaktera, tako da se indeks provlači kroz sve njih. Stranica takođe mora biti trenutna stranica da bi se indeks razrešio u odnosu na tačan tekst, što postaje važno kada pređete prvu stranicu
procedure DescribeFontAt(Pdf: TPdf; CharIndex: Integer);
var
Report: TStringList;
PtSize: Single;
begin
Report := TStringList.Create;
try
PtSize := Pdf.FontSize[CharIndex];
Report.Add('Character : ' + Pdf.Character[CharIndex]);
Report.Add('Family : ' + Pdf.FontFamilyName[CharIndex]);
Report.Add('Base name : ' + Pdf.FontBaseName[CharIndex]);
Report.Add('Weight : ' + IntToStr(Pdf.FontWeight[CharIndex]));
Report.Add('Italic : ' + IntToStr(Pdf.FontItalicAngle[CharIndex]) + ' deg');
Report.Add('Size : ' + FormatFloat('0.0', PtSize) + ' pt');
Report.Add('Ascent : ' + FormatFloat('0.0', Pdf.FontAscent[CharIndex, PtSize]));
Report.Add('Descent : ' + FormatFloat('0.0', Pdf.FontDescent[CharIndex, PtSize]));
Report.Add('Embedded : ' + BoolToStr(Pdf.FontIsEmbedded[CharIndex], True));
ShowMessage(Report.Text);
finally
Report.Free;
end;
end;
Nekoliko deklaracija može iznenaditi ljude koji dolaze iz drugih biblioteka. FontAscent i FontDescent primaju dva argumenta, indeks karaktera i veličinu u tačkama (point size), jer PDFium prijavljuje te metrike u jedinicama prostora glifova koje postaju pikseli tek kada ih skalirate veličinom na koju je tekst postavljen. Prosledite vrednost koju ste već pročitali iz FontSize[CharIndex] i dobićete ascent i descent u istim tačkama kao i ostatak rasporeda. Descent se vraća kao negativan, pošto meri ispod osnovne linije (baseline). Naziv porodice i osnovno ime su namerno odvojeni stringovi: osnovno ime je sirovi unos /BaseFont iz PDF-a, koji često nosi prefiks podskupa kao što je ABCDEF+, dok je naziv porodice očišćeno ime koje renderer na kraju razrešava
Pretvaranje klika u indeks karaktera
U čitaču retko unapred znate indeks. Korisnik klikne na glif i vi morate da prevedete koordinatu piksela u karakter ispod njega. CharacterIndexAtPos radi upravo to, uzimajući poziciju miša i toleranciju i vraćajući indeks najbližeg karaktera, ili negativnu vrednost kada klik padne na prazan prostor ili praznu stranicu
procedure TfrmMain.PdfViewMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
Index: Integer;
begin
if not PdfView.Active then
Exit;
// 4 px of slack in each direction so a near-miss still hits the glyph.
Index := PdfView.CharacterIndexAtPos(X, Y, 4.0, 4.0);
if Index < 0 then
Exit; // clicked between glyphs; leave the panel alone
PdfView.CurrentCharIndex := Index;
DescribeFontAt(PdfView.Pdf, Index);
end;
Toleranciju vredi podesiti. Ako je previše uska, korisnici će imati osećaj da moraju da kliknu na tačno stablo slova; ako je previše labava, klik na marginu može da se zakači za neki udaljeni karakter koji nema nikakve veze sa onim što su želeli. Tri do pet piksela na ekranu je razumna polazna tačka. Vraćeni indeks je u parsiranom tekstu trenutne stranice, što je isti indeksni prostor koji očekuje svako svojstvo fonta, tako da ga možete direktno proslediti gornjoj rutini. Čuvanje u CurrentCharIndex je opciono, ali zgodno: prikaz to drži kao svoj fokusirani glif, što je korisno ako drugi delovi korisničkog interfejsa žele da pročitaju selekciju bez ponovnog izračunavanja
Ugradnja je svojstvo koje je najvažnije
Za većinu stvarnog rada, jedino pitanje na koje vredi odgovoriti jeste da li je svaki font ugrađen. Dokument čiji se svi fontovi nalaze unutar njega renderuje se isto na RIP-u štamparske službe, na laptopu kolege i na serveru bez ikakvog grafičkog interfejsa (GUI). Dokument koji se oslanja na neugrađeni font Helvetica rizikuje da svaka od tih mašina ima odgovarajući font, a kada taj rizik propadne, čitač ga zamenjuje nečim sličnim, metrika se menja, i pažljivo raspoređen obrazac se pomera taman toliko da se pokvari. Prolazak kroz tekst stranice i grupisanje fontova prema statusu ugradnje daje vam taj odgovor na jeftin način
procedure ReportNonEmbeddedFonts(Pdf: TPdf);
var
Embedded, External: TStringList;
I: Integer;
Name: string;
begin
Embedded := TStringList.Create;
External := TStringList.Create;
try
Embedded.Sorted := True;
Embedded.Duplicates := dupIgnore;
External.Sorted := True;
External.Duplicates := dupIgnore;
for I := 0 to Pdf.CharacterCount - 1 do
begin
Name := Pdf.FontBaseName[I];
if Name = '' then
Continue; // generated spaces and the like have no font
if Pdf.FontIsEmbedded[I] then
Embedded.Add(Name)
else
External.Add(Name);
end;
if External.Count > 0 then
ShowMessage(IntToStr(External.Count) +
' non-embedded font(s):' + sLineBreak + External.Text)
else
ShowMessage('All ' + IntToStr(Embedded.Count) +
' font(s) on this page are embedded.');
finally
Embedded.Free;
External.Free;
end;
end;
Dva detalja održavaju ovo tačnim. Prvo, CharacterCount je po stranici, tako da provera celog dokumenta znači postavljanje Pdf.PageNumber na svaku stranicu redom i ponovno pokretanje petlje, spajajući rezultate. Drugo, tekstualni sloj sadrži generisane karaktere kao što su razmaci koje čitalac zaključuje između reči, a oni nemaju objekat fonta iza sebe; provera praznog osnovnog imena ih preskače umesto da beleži fantomski unos. Osnovno ime je ispravan ključ za uklanjanje duplikata ovde jer prefiks podskupa koji nosi razlikuje dva različita podskupa iste porodice, što je obično ono što želite da znate
Izvlačenje ugrađenog fonta
Kada je font ugrađen, možete direktno čitati njegove bajtove. FontData vraća sirovi program fonta, iste TrueType ili CFF podatke koje PDF nosi, što je dovoljno za pisanje samostalne datoteke fonta ili za proveru otiska fonta u odnosu na poznatu biblioteku. Vraća prazan niz kada font nije ugrađen, tako da provera ugradnje i provera dužine zajedno štite upisivanje
procedure SaveEmbeddedFont(Pdf: TPdf; CharIndex: Integer;
const OutputFile: string);
var
Data: TBytes;
Stream: TFileStream;
begin
if not Pdf.FontIsEmbedded[CharIndex] then
begin
ShowMessage('That glyph''s font is not embedded; nothing to extract.');
Exit;
end;
Data := Pdf.FontData[CharIndex];
if Length(Data) = 0 then
Exit;
Stream := TFileStream.Create(OutputFile, fmCreate);
try
Stream.WriteBuffer(Data[0], Length(Data));
finally
Stream.Free;
end;
ShowMessage('Wrote ' + IntToStr(Length(Data)) + ' bytes.');
end;
Bajtovi su ugrađeni podskup (subset), a ne originalni komercijalni font, tako da ono što dobijete nazad obično pokriva samo glifove koje je dokument stvarno koristio. To je upravo ono što je potrebno za forenziku i verifikaciju, a loše rešenje za ponovnu upotrebu; podskup fonta Times New Roman koji sadrži trideset glifova nije font koji možete instalirati i koristiti za kucanje. Tretirajte ekstrakciju kao način da pregledate šta je isporučeno, a ne kao alat za oporavak fonta. Ako vam treba odgovarajuće osnovno ime za označavanje izlaza, pročitajte FontBaseName[CharIndex] zajedno sa podacima, i uklonite vodeću oznaku podskupa ako želite čistu porodicu
Razumevanje broja težine
FontWeight vraća numeričku klasu težine, istu skalu od 100 do 900 koju koristi CSS, gde je 400 regular (normalno), a 700 bold (boldovano). PDFium prijavljuje šta god font deklariše, što nije uvek zaokružena stotina; font može prijaviti 350 ili 650, i tretiranje bilo čega na ili iznad 600 kao "dovoljno podebljano" funkcioniše bolje nego testiranje tačno za 700. Ugao kurziva (italic angle) je prateći signal: ne-nula vrednost, obično negativna, znači da je font ukošen ili pravi kurzivni dizajn, a nula znači uspravan. Zajedno vam omogućavaju da razlikujete bold-italic deo od običnog bez renderovanja bilo čega, što je vrsta provere koju preflight prolaz ili revizija pristupačnosti žele da obave grupno
Nijedno od ovih čitanja ne zahteva renderovanu bitmapu. Dolaze iz parsiranog tekstualnog sloja, tako da je otvoren dokument na ispravnoj stranici sve što vam treba od podešavanja, što pregled fontova čini jeftinim za pokretanje na celoj arhivi. Ako ovo uparujete sa ekstrakcijom teksta, isti indeksi karaktera se poklapaju sa tekstom koji izvlačite, tako da su font glifa i njegova Unicode vrednost dva čitanja u odnosu na jedan indeks. Prateći članak o ekstrakciji teksta iz PDF dokumenata pomoću PDFium Component detaljnije pokriva tu stranu tekstualnog sloja
Svojstva fonta prikazana ovde su deo PDFium Delphi VCL komponente