Hvert synlig tegn i en PDF bærer en referanse til skrifttypen som tegnet det, og PDFium-komponenten lar deg følge den referansen tilbake til skrifttypeobjektet og lese hva det vet. Tilgangsenheten er tegnet, ikke dokumentet: du velger et tegn etter dets indeks i sideteksten og ber om familienavn, basisnavn, tykkelse (weight), kursivvinkel og om det underliggende snittet faktisk er med inni filen. Den siste egenskapen er den de fleste analyser egentlig er ute etter, fordi en innebygd skrifttype reiser med dokumentet og en ikke-innebygd en er et løfte om at leserens maskin tilfeldigvis har det samme skrifttypesnittet installert
Komponenten eksponerer disse gjennom de samme TPdf- og TPdfView-objektene du bruker for gjengivelse og tekstutvinning. Det er ikke noe separat "skrifttypetabell"-objekt å åpne. Når en sides tekst har blitt tolket, henger skrifttypeegenskapene på tegnindeksen, og du leser dem én glyf om gangen. Det designet passer med hvordan PDF lagrer informasjonen i utgangspunktet: en enkelt side kan bytte skrifttype dusinvis av ganger, og det eneste ærlige svaret på "hvilken skrifttype er dette dokumentet i" er "det spørs hvilket tegn du mener."
Lese skrifttypen bak ett tegn
Den minste nyttige operasjonen er å ta en tegnindeks og dumpe alt PDFium kan fortelle deg om skrifttypen. Hver skrifttypeegenskap på TPdf og TPdfView er indeksert etter tegnposisjon, så indeksen går som en rød tråd gjennom dem alle. Siden må også være den gjeldende siden for at indeksen skal løses opp mot riktig tekst, noe som betyr noe når du beveger deg forbi side én
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;
Et par av signaturene overrasker folk som kommer fra andre biblioteker. FontAscent og FontDescent tar to argumenter, tegnindeksen og en punktstørrelse, fordi PDFium rapporterer disse beregningene i glyf-rom-enheter som bare blir piksler når du skalerer dem med størrelsen teksten ble satt til. Send inn verdien du allerede leste fra FontSize[CharIndex] og du får oppstigning (ascent) og nedstigning (descent) i samme punkter som resten av oppsettet. Nedstigning kommer tilbake negativ, siden den måler under grunnlinjen. Familienavnet og basisnavnet er separate strenger med vilje: basisnavnet er den rå /BaseFont-oppføringen fra PDF-en, som ofte bærer et delmengdeprefiks som ABCDEF+, mens familienavnet er det oppryddede navnet gjengiveren løser det til
Gjøre om et klikk til en tegnindeks
I et visningsprogram vet du sjelden indeksen på forhånd. Brukeren klikker på en glyf og du må oversette pikselkoordinaten til tegnet under det. CharacterIndexAtPos gjør akkurat det, ved å ta museposisjonen og en toleranse og returnere indeksen til nærmeste tegn, eller en negativ verdi når klikket landet på hvitt rom eller tom side
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;
Toleransen er verdt å finjustere. For stram og brukere føler at de må lande på den nøyaktige stammen til en bokstav; for løs og et klikk i en marg smekker til et fjern tegn som ikke har noe å gjøre med det de mente. Tre til fem enhetspiksler er et fornuftig utgangspunkt for skjermvisning. Den returnerte indeksen er inn i den tolkede teksten på den gjeldende siden, det samme indeksrommet hver skrifttypeegenskap forventer, så du kan levere den rett til rutinen ovenfor. Å lagre den i CurrentCharIndex er valgfritt, men praktisk: visningen beholder den som sitt begrep om den fokuserte glyfen, som er kjekt hvis andre deler av brukergrensesnittet ønsker å lese utvalget uten å utlede det på nytt
Innbygging er den egenskapen som betyr noe
For det meste av ekte arbeid er det eneste spørsmålet som er verdt å svare på om hver skrifttype er innebygd. Et dokument der alle skrifttyper rir inni det gjengis likt på et trykkeris RIP, en kollegas bærbar PC, og en server helt uten grafisk brukergrensesnitt. Et dokument som støtter seg på en ikke-innebygd Helvetica gambler på at hver og en av disse maskinene har et matchende snitt, og når gamblingen mislykkes, bytter leseren inn noe nært, metrikken forskyves, og et nøye utformet skjema flyter akkurat nok om til å knekke. Å gå gjennom sideteksten og plassere skrifttyper i bøtter etter innbyggingsstatus gir deg det svaret billig
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;
To detaljer holder dette ærlig. For det første er CharacterCount per side, så en revisjon av hele dokumentet betyr å sette Pdf.PageNumber til hver side etter tur og kjøre løkken igjen, og slå sammen resultatene. For det andre inneholder tekstlaget genererte tegn som mellomrommene en leser utleder mellom ord, og de har ikke noe skrifttypeobjekt bak seg; sjekken for tomt basisnavn hopper over dem i stedet for å logge et fantom. Basisnavnet er den rette nøkkelen for de-duplisering her fordi delmengdeprefikset det bærer skiller to forskjellige delmengder av samme familie, som vanligvis er det du vil vite
Trekke ut det innebygde snittet
Når en skrifttype er innebygd, kan du lese bytene direkte. FontData returnerer det rå skrifttypeprogrammet, de samme TrueType- eller CFF-dataene som PDF-en bærer, som er nok til å skrive en frittstående skrifttypefil eller for å ta fingeravtrykk av snittet mot et kjent bibliotek. Det returnerer en tom matrise når skrifttypen ikke er innebygd, så innbyggingssjekken og lengdesjekken sammen vokter skrivingen
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;
Bytene er den innebygde delmengden, ikke den originale detaljhandelskriftstypen, så det du får tilbake dekker vanligvis bare glyfene dokumentet faktisk brukte. Det er helt riktig for etterforskning og verifisering, og dårlig egnet for gjenbruk; en delmengde av Times New Roman som inneholder tretti glyfer, er ikke en skrifttype du kan installere og skrive med. Behandle utvinning som en måte å inspisere det som ble sendt, ikke som et verktøy for skrifttypegjenoppretting. Hvis du trenger det matchende basisnavnet for å merke utdataene, les FontBaseName[CharIndex] sammen med dataene, og fjern den ledende delmengdetaggen hvis du vil ha den nakne familien
Forstå tykkelsestallet
FontWeight returnerer den numeriske tykkelsesklassen (weight class), den samme 100-til-900-skalaen CSS bruker, der 400 er vanlig (regular) og 700 er fet (bold). PDFium rapporterer uansett hva skrifttypen erklærer, som ikke alltid er et rundt hundretall; et snitt kan kunngjøre 350 eller 650, og å behandle alt på eller over 600 som "fett nok til å bety noe" holder bedre enn å teste for nøyaktig 700. Kursivvinkelen er et ledsagersignal: en verdi ulik null, vanligvis negativ, betyr at snittet er et skråstilt (oblique) eller ekte kursivt design, og null betyr oppreist. Sammen lar de deg skille en fet-kursiv kjøring fra en vanlig en uten å gjengi noe, som er den typen sjekk en preflight-passering eller en tilgjengelighetsrevisjon ønsker å gjøre i bulk
Ingen av disse lesingene krever et gjengitt bitkart. De kommer fra det tolkede tekstlaget, så et åpent dokument på riktig side er alt oppsettet du trenger, noe som gjør skrifttypeinspeksjon billig å kjøre over et helt arkiv. Hvis du kobler dette med tekstutvinning, stiller de samme tegnindeksene seg på linje med teksten du trekker ut, så en glyfs skrifttype og dens Unicode-verdi er to lesinger mot én indeks. Ledsagerartikkelen om å trekke ut tekst fra PDF-dokumenter med PDFium-komponent dekker den siden av tekstlaget i mer dybde
Skrifttypeegenskapene som vises her er en del av PDFium Delphi VCL-komponenten