Fiecare caracter vizibil dintr-un PDF poartă o referință spre fontul care l-a desenat, iar PDFium Component vă lasă să urmăriți acea referință înapoi până la obiectul font și să citiți ce știe el. Unitatea de acces este caracterul, nu documentul: alegeți un caracter după indexul lui din textul paginii și cereți numele familiei, numele de bază, greutatea, unghiul de italic și dacă fața de bază este într-adevăr purtată în interiorul fișierului. Ultima proprietate este cea pe care o urmăresc de fapt majoritatea analizelor, pentru că un font încorporat călătorește împreună cu documentul, în timp ce unul neîncorporat este o promisiune că mașina cititorului se întâmplă să aibă instalat același caracter tipografic
Componenta le expune prin aceleași obiecte TPdf și TPdfView pe care le folosiți pentru randare și pentru extragerea textului. Nu există un obiect separat de tip „tabel de fonturi” care să fie deschis. Odată ce textul unei pagini a fost parsat, proprietățile fontului atârnă de indexul caracterului, iar dumneavoastră le citiți glifă cu glifă. Designul acesta se potrivește cu felul în care PDF stochează informația de la bun început: o singură pagină poate schimba fonturile de zeci de ori, iar singurul răspuns onest la întrebarea „în ce font este documentul acesta” este „depinde la ce caracter vă referiți”
Citirea fontului din spatele unui caracter
Cea mai mică operație utilă este să luați un index de caracter și să afișați tot ce vă poate spune PDFium despre fontul lui. Fiecare proprietate de font din TPdf și TPdfView este indexată după poziția caracterului, așa că indexul le străbate pe toate. Pagina trebuie să fie și pagina curentă pentru ca indexul să se rezolve față de textul corect, ceea ce contează odată ce treceți dincolo de prima pagină
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;
Câteva dintre semnături îi surprind pe cei care vin de la alte biblioteci. FontAscent și FontDescent primesc două argumente, indexul caracterului și un corp de literă, pentru că PDFium raportează acele metrici în unități din spațiul glifelor, care devin pixeli abia după ce le scalați cu dimensiunea la care a fost așezat textul. Transmiteți valoarea pe care ați citit-o deja din FontSize[CharIndex] și obțineți ascendenta și descendenta în aceleași puncte ca restul aranjării. Descendenta vine negativă, pentru că se măsoară sub linia de bază. Numele familiei și numele de bază sunt șiruri separate în mod intenționat: numele de bază este intrarea brută /BaseFont din PDF, care poartă adesea un prefix de subset precum ABCDEF+, în timp ce numele familiei este numele curățat la care îl rezolvă motorul de randare
Transformarea unui clic într-un index de caracter
Într-un vizualizator rareori știți indexul dinainte. Utilizatorul dă clic pe o glifă, iar dumneavoastră trebuie să traduceți coordonata în pixeli în caracterul de sub ea. CharacterIndexAtPos face exact asta: primește poziția mouse-ului și o toleranță și returnează indexul celui mai apropiat caracter sau o valoare negativă când clicul a nimerit în spațiu alb ori în pagină goală
procedure TfrmMain.PdfViewMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
Index: Integer;
begin
if not PdfView.Active then
Exit;
// 4 px de joc în fiecare direcție, ca un clic ratat la limită să prindă totuși glifa.
Index := PdfView.CharacterIndexAtPos(X, Y, 4.0, 4.0);
if Index < 0 then
Exit; // clic între glife; lasă panoul în pace
PdfView.CurrentCharIndex := Index;
DescribeFontAt(PdfView.Pdf, Index);
end;
Toleranța merită reglată. Prea strânsă și utilizatorii simt că trebuie să nimerească exact bara unei litere; prea largă și un clic într-o margine se lipește de vreun caracter îndepărtat, care nu are nicio legătură cu ce voiau. Trei până la cinci pixeli de dispozitiv este un punct de plecare rezonabil pentru vizionarea pe ecran. Indexul returnat trimite în textul parsat al paginii curente, același spațiu de indici pe care îl așteaptă orice proprietate de font, așa că îl puteți da direct rutinei de mai sus. Păstrarea lui în CurrentCharIndex este opțională, dar comodă: vizualizarea îl ține drept noțiune proprie de glifă focalizată, ceea ce ajută dacă alte părți ale interfeței vor să citească selecția fără să o deducă din nou
Încorporarea este proprietatea care contează
Pentru majoritatea lucrărilor reale, singura întrebare care merită un răspuns este dacă fiecare font este încorporat. Un document ale cărui fonturi călătoresc toate în interiorul lui se randează la fel pe RIP-ul unei tipografii, pe laptopul unui coleg și pe un server fără nicio interfață grafică. Un document care se sprijină pe un Helvetica neîncorporat pariază că fiecare dintre acele mașini are o față potrivită, iar când pariul eșuează, cititorul substituie ceva apropiat, metricile se deplasează, iar un formular aranjat cu grijă se reformatează exact cât să se strice. Parcurgerea textului paginii și gruparea fonturilor după starea de încorporare vă dă ieftin acest răspuns
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; // spațiile generate și altele asemenea nu au 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;
Două detalii mențin lucrurile oneste. Întâi, CharacterCount este per pagină, așa că un audit pe întregul document înseamnă setarea lui Pdf.PageNumber pe fiecare pagină pe rând și rularea buclei din nou, cu îmbinarea rezultatelor. Apoi, stratul de text conține caractere generate, precum spațiile pe care un cititor le deduce între cuvinte, iar acestea nu au niciun obiect font în spate; verificarea numelui de bază gol le sare, în loc să înregistreze o fantomă. Numele de bază este cheia potrivită pentru deduplicare aici, pentru că prefixul de subset pe care îl poartă distinge două subseturi diferite ale aceleiași familii, iar de obicei exact asta vreți să aflați
Extragerea feței încorporate
Când un font este încorporat, îi puteți citi octeții direct. FontData returnează programul brut al fontului, aceleași date TrueType sau CFF pe care le poartă PDF-ul, ceea ce ajunge pentru a scrie un fișier de font de sine stătător sau pentru a lua amprenta feței față de o bibliotecă cunoscută. Returnează un tablou gol când fontul nu este încorporat, așa că verificarea încorporării și verificarea lungimii păzesc împreună scrierea
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;
Octeții sunt subsetul încorporat, nu fontul comercial original, așa că ce primiți înapoi acoperă de obicei doar glifele pe care documentul le-a folosit efectiv. Asta este exact ce trebuie pentru expertiză și verificare și se potrivește prost cu reutilizarea; un subset din Times New Roman care conține treizeci de glife nu este un font pe care să îl instalați și cu care să scrieți. Tratați extragerea ca pe un mod de a inspecta ce a fost livrat, nu ca pe un instrument de recuperare a fonturilor. Dacă aveți nevoie de numele de bază potrivit pentru a eticheta rezultatul, citiți FontBaseName[CharIndex] alături de date și îndepărtați eticheta de subset de la început dacă vreți familia goală
Cum se citește numărul de greutate
FontWeight returnează clasa numerică de greutate, aceeași scală de la 100 la 900 pe care o folosește CSS, unde 400 este normal, iar 700 este aldin. PDFium raportează ce declară fontul, ceea ce nu este întotdeauna o sută rotundă; o față poate anunța 350 sau 650, iar tratarea oricărei valori de la 600 în sus drept „suficient de aldină cât să conteze” rezistă mai bine decât testarea exactă pentru 700. Unghiul de italic este un semnal însoțitor: o valoare nenulă, de obicei negativă, înseamnă că fața este un design oblic sau un italic veritabil, iar zero înseamnă drept. Împreună, ele vă permit să deosebiți o secvență aldină-italică de una normală fără să randați nimic, adică genul de verificare pe care o trecere de preflight sau un audit de accesibilitate vrea să o facă în masă
Niciuna dintre aceste citiri nu are nevoie de o imagine randată. Ele vin din stratul de text parsat, așa că un document deschis pe pagina potrivită este toată pregătirea de care aveți nevoie, ceea ce face inspecția fonturilor ieftin de rulat pe o arhivă întreagă. Dacă împerecheați asta cu extragerea textului, aceiași indici de caracter se aliniază cu textul pe care îl scoateți, așa că fontul unei glife și valoarea ei Unicode sunt două citiri pe același index. Articolul însoțitor despre extragerea textului din documente PDF cu PDFium Component acoperă mai în detaliu acea latură a stratului de text
Proprietățile de font arătate aici fac parte din PDFium Delphi VCL Component