PDFium Component zapisuje text do stránky PDF na úrovni glyfů pomocí AddCidType2Text a vkládá data TrueType, která dodáte, jako font CID Type 2. Přiřadí každé instanci glyfu sekvenční CID, vygeneruje explicitní mapu CID-to-GID a CMap ToUnicode a sloučí glyfy na stejné základní čáře do jednoho nativního textového objektu. Volitelný subsetting udržuje soubor malý, s explicitní zásadou pro to, co se stane, když subsetting selže
Zápis na úrovni glyfů potřebujete, když už byl text tvarovaný. Arabština, dévanágarí a jakékoli písmo s kontextovými formami produkují posloupnost identifikátorů glyfů, která už se nemapuje jedna ku jedné na znaky, takže API, které přebírá řetězec a název fontu, výsledek vyjádřit nedokáže. Předání glyfů a pozic přímo je jediný způsob, jak dostat správně tvarovaný text komplexního písma do PDF
Proč každá instance glyfu dostane vlastní CID
Lákavá optimalizace je deduplikace: jedno CID na odlišný identifikátor glyfu, znovu použité všude, kde se glyf objeví. Vyprodukuje to menší font a rozbije to extrakci textu, protože stejný glyf může legitimně odpovídat různému unicodovému obsahu na různých místech
Jasným případem je ligaturní glyf. Stejný glyf "ffi" může zastupovat tři znaky v jednom slově a po jiném tvarovacím rozhodnutí i něco jiného jinde. Mapa ToUnicode je klíčovaná podle CID, takže sdílené CID smí nést jen jedno mapování, a ať už jedna z těchto dvou textových instancí tento souboj prohraje, stane se neextrahovatelnou
Proto si každá instance glyfu bere vlastní CID a každé mapování smí obsahovat libovolný počet jednotek kódu UTF-16. Glyf bez smysluplného textu — dekorativní prvek, glyf jen pro pozicování — se mapuje explicitně na U+200B, mezeru nulové šířky, takže má každé CID pozorovatelný výsledek extrakce místo mezery
uses
PDFium;
var
Pdf: TPdf;
Glyphs: TPdfCidGlyphs;
Options: TPdfCidFontOptions;
Report: TPdfCidFontReport;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'label.pdf';
Pdf.LoadDocument;
Pdf.PageNumber := 1;
// Jedna položka na tvarovaný glyf: id glyfu, text, který zastupuje,
// a jeho posun a offsety v textovém prostoru
SetLength(Glyphs, 3);
Glyphs[0].GlyphID := 402; Glyphs[0].UnicodeText := 'ffi';
Glyphs[0].Advance := 18.4;
Glyphs[1].GlyphID := 71; Glyphs[1].UnicodeText := 'c';
Glyphs[1].Advance := 9.8;
Glyphs[2].GlyphID := 74; Glyphs[2].UnicodeText := 'e';
Glyphs[2].Advance := 9.2;
Options := TPdfCidFontOptions.Default; // preferuje subset
Options.VerifyExtraction := True;
if Pdf.AddCidType2Text(LoadFileBytes('NotoSans.ttf'), Glyphs,
11, 72, 700, Options, Report) then
Writeln(Format('%d glyphs, %d unique, font %d -> %d bytes, match=%s',
[Report.GlyphCount, Report.UniqueGlyphCount,
Report.OriginalFontBytes, Report.EmbeddedFontBytes,
BoolToStr(Report.ExtractionMatches, True)]));
finally
Pdf.Free;
end;
end;
Subsetting a kontroly, které vám návratový kód nedá
Subsetting fontu na skutečně použité glyfy je rozdíl mezi vložením 300 KB a vložením 12 KB, a u dokumentu s několika fonty rozhoduje o tom, zda je soubor odeslatelný e-mailem. Platformní cesta subsettingu přijímá seznam glyfů přímo, což se k tomuto API dokonale hodí, protože volající už zná každý použitý identifikátor glyfu
Co vám nedá, je jistota. Volání subsettingu může nahlásit úspěch a vrátit nepoužitelný výstup, takže komponenta před přijetím výsledku ověří tři vlastnosti: výstup musí být menší než originál, musí se znovu naparsovat jako platný sfnt s čitelnou tabulkou maxp a musí zachovat glyfy až po nejvyšší požadovaný původní identifikátor. Jakékoli selhání znamená, že se subset odmítne
Co se stane dál, je zásada volajícího. Pod pcfemSubsetPreferred, výchozí volbou, se odmítnutý subset propadne k vložení celého fontu, takže stránka je správná, jen větší. Pod pcfemSubsetRequired operace selže dřív, než se stránka upraví, což chce pipeline omezená velikostí. pcfemFull subsetting úplně přeskočí. Report vám řekne, kterou cestu si vzal, přes SubsetAttempted, SubsetApplied, UsedFullFontFallback a SubsetErrorCode
Verifikace, která nemůže vyprodukovat falešně pozitivní výsledek
Se zapnutým VerifyExtraction komponenta potvrdí, že to, co zapsala, lze přečíst zpátky. Naivní způsob, jak to udělat, je extrahovat celou stránku a hledat očekávaný řetězec, a je to špatně: stránka, která už daný text obsahovala, projde kontrolou i tehdy, když byl nový text zapsán chybně
Místo toho se textová stránka znovu sestaví a rukojeti objektů vložené tímto voláním se čtou jednotlivě, v pořadí vložení, a spojí se dohromady. Výsledek se porovná s očekávaným textem a report vystaví oba řetězce spolu s booleanem, takže lze neshodu diagnostikovat, ne jen odhalit
Zapněte to při vývoji a v jakémkoli pipeline, kde je extrahovatelnost požadavkem — prohledávatelné archivy, shoda s přístupností, navazující text mining. Cena je jedno znovusestavení textové stránky na volání, což je důvod, proč to není zapnuté ve výchozím stavu v úzkých smyčkách
Rozpočty a atomické selhání
Bajty fontu, instance glyfů a jednotky kódu Unicode jsou každé omezeny stropem před alokací a formát fontu, index TTC, rozsah identifikátoru glyfu a hodnoty geometrie se validují dřív, než se cokoli zapíše. Geometrie musí být konečná, což zní zjevně, dokud vám tvarovací engine nedodá NaN v posunu z poškozeného fontu
Selhání je atomické na úrovni stránky. Pokud selže načtení fontu, zápis objektů, generování obsahu nebo verifikace extrakce, každý objekt vložený tímto voláním se odstraní v opačném pořadí a obsah stránky se znovu vygeneruje. Neúspěšné volání zanechá stránku takovou, jaká byla, ne s napůl hotovým textovým během
Kam to zapadá v textovém pipeline
Rozdělení práce stojí za jasné vyslovení. Tvarování — přeměna znaků na umístěné glyfy — není úkolem tohoto API; patří tvarovacímu enginu a vlastní obousměrnou podporu komponenty a podporu komplexních písem popisuje článek zpracování emoji, CJK a náhradních párů. Toto API je to, co voláte poté, s během glyfů, který tvarovač vyprodukoval
Pro obyčejný latinský text bez kontextového tvarování jsou správným nástrojem jednodušší API pro text založená na řetězcích a produkují menší kód. Po zápisu CID Type 2 sáhněte, když máte tvarovaný výstup, když potřebujete přesnou kontrolu nad identifikátory glyfů, nebo když musí být font vložen z bajtů, které držíte, ne rozřešen podle jména — alternativou rozřešení fontu je mechanismus poskytovatele popsaný v článku řízení náhrady fontu PDF
Jedna poznámka k nasazení: vkládání fontu je stejnou měrou licenční otázka jako technická. Fonty se liší v tom, zda je vkládání vůbec povoleno, povoleno jen pro zobrazení, nebo povoleno pro úpravy. Knihovna vloží jakékoli bajty, které jí dáte, a kontrola licence je vaše odpovědnost, ne odpovědnost souborového formátu
Zápis textu na úrovni glyfů, poskytování fontů a extrakce textu sdílejí v Delphi, C++Builder a Lazarus stejný model stránky; úplné API popisuje stránka PDFium Component pro Delphi