Kiekvienas matomas PDF simbolis turi nuorodą į šriftą, kuriuo jis buvo nupieštas, o PDFium VCL leidžia sekti šią nuorodą iki šrifto objekto ir perskaityti jo duomenis. Prieigos vienetas yra simbolis, o ne dokumentas: pasirenkate simbolį pagal jo indeksą puslapio tekste ir užklausia šeimos pavadinimo, bazinio pavadinimo, storio, pasvirimo kampo ir to, ar pats šriftas yra įterptas faile. Ši paskutinė savybė yra tai, ko dažniausiai siekiama analizėse, nes įterptas šriftas keliauja kartu su dokumentu, o neįterptas šriftas tėra pažadas, kad skaitytojo kompiuteryje bus įdiegtas toks pat šriftas.
Komponentas šias savybes pateikia per tuos pačius TPdf ir TPdfView objektus, kuriuos naudojate atvaizdavimui ir teksto išgavimui. Nėra jokio atskiro atidaromo „šriftų lentelės“ objekto. Kai puslapio tekstas išanalizuojamas, šrifto savybės susiejamos su simbolio indeksu, o jas skaitote po vieną glifą. Toks dizainas atitinka tai, kaip PDF iš pradžių saugo informaciją: viename puslapyje šriftai gali keistis dešimtis kartų, o vienintelis sąžiningas atsakymas į klausimą „koks yra šio dokumento šriftas“ yra „tai priklauso nuo to, apie kurį simbolį kalbate“.
Šrifto, esančio už vieno simbolio, skaitymas
Smulkiausia naudinga operacija yra paimti simbolio indeksą ir išvesti viską, ką PDFium gali pasakyti apie jo šriftą. Kiekviena šrifto savybė TPdf ir TPdfView objektuose yra indeksuojama pagal simbolio poziciją, todėl indeksas eina per jas visas. Puslapis taip pat turi būti nustatytas kaip dabartinis, kad indeksas sutaptų su teisingu tekstu, o tai tampa svarbu perėjus pirmąjį puslapį.
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;
Keletas parašų stebina žmones, atėjusius iš kitų bibliotekų. FontAscent ir FontDescent priima du argumentus: simbolio indeksą ir dydį taškais (point size), nes PDFium praneša šiuos metrikos duomenis glifų erdvės vienetais, kurie virsta pikseliais tik tada, kai juos masteliuojate pagal teksto nustatytą dydį. Perduokite reikšmę, kurią jau perskaitėte iš FontSize[CharIndex], ir gausite viršutinį bei apatinį nukrypimus (ascent ir descent) tais pačiais taškais, kaip ir likęs maketas. Apatinis nukrypimas grąžinamas kaip neigiama reikšmė, nes jis matuojamas žemiau bazinės linijos. Šeimos pavadinimas ir bazinis pavadinimas sąmoningai yra atskiros eilutės: bazinis pavadinimas yra neapdorotas /BaseFont įrašas iš PDF failo, dažnai turintis poaibių prefiksą, pvz., ABCDEF+, o šeimos pavadinimas yra išvalytas pavadinimas, kurį išsprendžia atvaizdavimo variklis.
Spustelėjimo pavertimas simbolio indeksu
Peržiūros programoje retai kada iš anksto žinote indeksą. Vartotojas spusteli glifą ir turite paversti pikselio koordinatę po juo esančiu simboliu. CharacterIndexAtPos atlieka būtent tai – priima pelės poziciją bei paklaidą ir grąžina artimiausio simbolio indeksą arba neigiamą reikšmę, jei buvo spustelėta tuščia vieta.
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;
Verta suderinti paklaidą (tolerance). Jei ji per maža, vartotojams atrodys, kad jie turi pataikyti tiksliai į raidės liniją; jei per didelė – spustelėjimas paraštėje gali pritraukti tolimą simbolį, kuris neturi nieko bendro su jų ketinimu. Nuo trijų iki penkių įrenginio pikselių yra protingas atspirties taškas žiūrėjimui ekrane. Grąžinamas indeksas yra nukreiptas į išanalizuotą dabartinio puslapio tekstą, t. y. į tą pačią indeksų erdvę, kurios tikisi kiekviena šrifto savybė, todėl galite jį tiesiogiai perduoti aukščiau pateiktai procedūrai. Įrašyti jį į CurrentCharIndex nėra privaloma, bet patogu: peržiūros komponentas tai laiko kaip sufokusuotą glifą, o tai naudinga, jei kitos vartotojo sąsajos dalys nori nuskaityti pasirinkimą jo neišvedinėdamos iš naujo.
Įterpimas yra svarbiausia savybė
Daugeliu realių atvejų vienintelis klausimas, į kurį verta atsakyti, yra tai, ar kiekvienas šriftas yra įterptas. Dokumentas, kurio visi šriftai yra įterpti jame, bus vienodai atvaizduojamas spaustuvės RIP įrenginyje, kolegos nešiojamame kompiuteryje ir serveryje, kuris išvis neturi grafinės sąsajos. Dokumentas, kuris naudoja neįterptą Helvetica šriftą, rizikuoja, kad kiekviename iš šių įrenginių bus įdiegtas toks pat šriftas, o kai ši rizika nepasiteisina, skaitytuvas pakeitė jį panašiu šriftu, pasikeičia metrikos ir kruopščiai suplanuota forma persigrupuoja tiek, kad sugriūtų. Teksto peržiūra ir šriftų suskirstymas pagal įterpimo būseną leidžia pigiai gauti šį atsakymą.
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;
Dvi detalės padeda išlaikyti tikslumą. Pirma, CharacterCount skaičiuojamas vienam puslapiui, todėl viso dokumento auditui reikia nustatyti Pdf.PageNumber kiekvienam puslapiui iš eilės ir vėl paleisti ciklą, sujungiant rezultatus. Antra, teksto sluoksnis turi sugeneruotų simbolių, tokių kaip tarpai tarp žodžių, kuriuos skaitytuvas numano, o šie neturi jokio šrifto objekto; tuščio bazinio pavadinimo patikra juos praleidžia, užuot užregistravusi tuščiuos objektus. Bazinis pavadinimas čia yra tinkamas raktas dublikatų šalinimui, nes jo turimas poaibių prefiksas leidžia atskirti du skirtingus tos pačios šeimos poaibius, o tai paprastai ir norima sužinoti.
Įterpto šrifto išgavimas
Kai šriftas yra įterptas, galite tiesiogiai perskaityti jo baitus. FontData grąžina neapdorotą šrifto programą – tuos pačius TrueType arba CFF duomenis, kuriuos perneša PDF failas, o to pakanka norint įrašyti atskirą šrifto failą arba palyginti šriftą su žinoma biblioteka. Jei šriftas neįterptas, grąžinamas tuščias masyvas, todėl įterpimo ir ilgio patikrinimas apsaugo failo įrašymą.
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;
Šie baitai yra įterptas poaibis, o ne originalus platinamas šriftas, todėl tai, ką gaunate, paprastai apima tik tuos glifus, kuriuos dokumentas iš tikrųjų panaudojo. Tai puikiai tinka patikrai bei verifikacijai, bet netinka pakartotiniam naudojimui; Times New Roman poaibis, kuriame yra trisdešimt glifų, nėra šriftas, kurį galėtumėte įdiegti ir naudoti rašymui. Vertinkite šį išgavimą kaip būdą patikrinti, kas buvo atsiųsta, o ne kaip šrifto atkūrimo įrankį. Jei jums reikia atitinkamo bazinio pavadinimo rezultatui pavadinti, perskaitykite FontBaseName[CharIndex] kartu su duomenimis ir pašalinkite pradinę poaibio žymą, jei norite matyti tik šeimos pavadinimą.
Šrifto storio reikšmės supratimas
FontWeight grąžina skaitinę storio klasę, tą pačią 100–900 skalę, kurią naudoja CSS, kur 400 yra įprastas (regular), o 700 – paryškintas (bold). PDFium praneša tai, ką deklaruoja pats šriftas, o tai ne visada būna apvalus šimtas; šriftas gali pranešti apie 350 arba 650 reikšmę, todėl bet koks storis, lygus arba didesnis nei 600, laikomas pakankamai paryškintu, kas yra patikimesnė patikra nei testavimas tiksliai dėl 700. Pasvirimo kampas (italic angle) yra susijęs signalas: ne nulinė reikšmė, paprastai neigiama, reiškia, kad šriftas yra įstrižas (oblique) arba tikrasis kursyvas (italic), o nulinė reikšmė reiškia tiesų šriftą. Kartu jie leidžia atskirti paryškinto kursyvo fragmentą nuo įprasto teksto nieko neatvaizduojant – būtent tokius tikrinimus norima atlikti dideliais kiekiais atliekant pirminį tikrinimą arba prieinamumo auditą.
Nė vienam iš šių nuskaitymų nereikia atvaizduoto taškinio paveikslėlio. Jie gaunami iš išanalizuoto teksto sluoksnio, todėl viskas, ko reikia, yra atidarytas dokumentas teisingame puslapyje, kas leidžia pigiai atlikti šriftų patikrą visame archyve. Jei tai derinate su teksto išgavimu, tie patys simbolių indeksai sutampa su jūsų išgaunamu tekstu, todėl glifo šriftas ir jo Unicode reikšmė yra du nuskaitymai vienam indeksui. Susijęs straipsnis apie teksto išgavimą iš PDF dokumentų naudojant PDFium VCL giliau nagrinėja šią teksto sluoksnio pusę.
Čia parodytos šrifto savybės yra PDFium Delphi VCL komponento dalis.