HotPDF crta emoji u boji u PDF kroz THotPDF.DrawRegisteredColorGlyph, koji čita podatke o bojama fonta registrovanog sa RegisterUnicodeTTF i emituje ih kao nativnu PDF grafiku: COLR v0 slojeve kao popunjene konture glifova, COLR v1 paint grafove kao clip-ove, shading-e i blend mode-ove, SVG glifove kao Form XObject-e, a CBDT ili sbix bitmap-e kao slike. Sve što ne ume da preslika nativno odlazi na OnColorGlyphRasterize događaj umesto da tiho postane crni oblik
Taj poslednji deo rečenice je ceo razlog što ovaj kod postoji. Ugradite emoji font običnim putem i pregledač dobija konturu iz glyf-a ili CFF-a, popunjenu onim što se trenutno nađe kao boja popune. Nasmejano lice stiže kao crna mrlja, zastava kao pravougaonik, i ništa u pipeline-u ne prigovara
Zašto se emoji u boji u PDF-u štampa kao crna silueta?
PDF font program nema pojam o glifovima u boji. ISO 32000-1 tretira glif kao oblik obojan tekućom bojom, a tabele boja koje je OpenType kasnije dodao, naime COLR/CPAL, SVG , CBDT/CBLC i sbix, nisu deo PDF imaging modela, pa nijedan pregledač nije dužan da ih čita iz ugrađenog fonta. Boja se mora prevesti u sadržaj stranice u trenutku generisanja, dok proizvođač još drži bajtove fonta i zna koji glif hoće. Taj prevod se razlikuje po formatu, a emoji fontovi u divljini koriste sve: slojevite vektore, gradijentne paint grafikone, ugrađene SVG dokumente i PNG strike-ove. HotPDF prijavljuje rezultat kao THPDFOpenTypeColorFormat, sa vrednostima otcfNone, otcfCOLRv0, otcfCOLRv1, otcfCBDT, otcfSVG i otcfSBIX, i ispipava font u fiksnom prioritetu: prvo COLR, pa SVG, pa CBDT, pa sbix. Vektorski podaci pobeđuju bitmap-e kad god font nosi oba, što je i to što želite u dokumentu koji može biti zumiran ili štampan
Jedan poziv, pet formata: razrešavanje i crtanje glifa u boji
THotPDF.GetRegisteredColorGlyphInfo odgovara koju će putanju kodna tačka preuzeti, a DrawRegisteredColorGlyph je precrtava. Obe traže kodnu tačku u character mapi fonta poslednjeg prosleđenog RegisterUnicodeTTF-u, pa font u boji mora biti registrovani Unicode font u trenutku poziva. Funkcija crtanja vraća False kada glif nema podatke o boji ili nijedna putanja ne može da ga prikaže, i rezervu ostavlja vama
const
FormatNames: array[THPDFOpenTypeColorFormat] of string =
('none', 'COLR v0', 'COLR v1', 'CBDT', 'SVG', 'sbix');
var
Pdf: THotPDF;
Info: THPDFOpenTypeColorGlyphInfo;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.AutoLaunch := False;
Pdf.FileName := 'emoji.pdf';
Pdf.BeginDoc;
Pdf.RegisterUnicodeTTF('C:\Windows\Fonts\seguiemj.ttf');
// U+1F600, CPAL paleta 0, bitmap strike najbliži 300 ppem
if Pdf.GetRegisteredColorGlyphInfo($1F600, 0, 300, Info) then
Writeln(Format('GID %d via %s',
[Info.GlyphID, FormatNames[Info.Format]]));
if not Pdf.DrawRegisteredColorGlyph(Pdf.CurrentPage, $1F600,
72, 144, 'Segoe UI Emoji', 36, 0, 300) then
begin
// Nema podataka o boji: vrati se na jednobojnu konturu
Pdf.CurrentPage.SetFont('Segoe UI Emoji', [], 36, DEFAULT_CHARSET);
Pdf.CurrentPage.TextOut(72, 144, 0, WideString(#$D83D#$DE00));
end;
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
Dva parametra zaslužuju pažnju. PaletteIndex bira CPAL paletu, pa font koji isporučuje paletu za tamnu pozadinu može da se prebaci bez diranja glifa. TargetPixelsPerEm je bitan samo za bitmap fontove; ostavljen na nuli podrazumeva se na Round(FontSize * 96 / 72), rezoluciju ekrana, pa primer traži 300 za štampu. Iskrena granica sedi u potpisu: poziv uzima jednu kodnu tačku i preslikava je samo kroz cmap. ZWJ sekvence, modifikatori tena kože i regional-indicator zastave su GSUB ligature, pa je njihovo slaganje shaping problem vrste pokrivene u članku o OpenType GSUB alternativama, a ne nešto što ovaj ulazni punkt radi za vas
COLR v0: naslagani slojevi glifova sa paletnim bojama
COLR v0 je jednostavan slučaj i HotPDF ga prikazuje direktno: svaki bazni glif navodi slojevne glifove sa CPAL unosom boje, i svaki sloj postaje jedna obična operacija prikazivanja teksta sa sopstvenom bojom popune, naslagana po redosledu tabele. Sloj sa alfom ispod 255 dobija graphics state parameter rečnik sa odgovarajućim /ca i /CA (ISO 32000-1 §8.4.5), i svaki slojevni glif označava se kao korišćen da ga subsetter zadrži iako nijedna kodna tačka ne vodi pravo na njega. Jedan detalj iznenađuje: indeks palete 0xFFFF znači „koristi boju teksta” u OpenType specifikaciji, i HotPDF ga razrešava u crno, a ne u tekuću boju popune stranice. Za emoji fontove to retko igra ulogu; za icon fontove koji se oslanjaju na foreground unos da oboje glif, proverite izlaz pre nego što pretpostavite da će pratiti boju vašeg teksta
Kako HotPDF pretvara COLR v1 paint graf u PDF operatore?
Tako što tabele bojanja prvo parsira u ravan, ograničen graf, i tek onda svaki čvor preslikava u PDF konstrukt. COLR v1 glif nije lista slojeva nego usmeren acikličan graf paint zapisa, gde se čvorovi mogu deliti kroz PaintColrLayers i PaintColrGlyph. Parser ga ograničava na 4096 paint čvorova, 64 nivoa dubine i 1024 color stop-a, i vodi računa o svakom čvoru kao aktivnom ili završenom, pa se referenca nazad na aktivan čvor, ciklus koji zlonamerni font može da napravi iz ponovnog korišćenja slojeva, odbija umesto da se u nju ugnježdi. Osnove ofseta su mesto gde prva implementacija zabrlja. BaseGlyphPaintRecord ofseti su relativni na početak BaseGlyphList-a, LayerList paint ofseti su relativni na LayerList, a svaki Offset24 unutar paint tabele relativan je na samu tu tabelu. Razrešite sva tri naspram iste osnove i sasvim legalni glifovi padaju na proveri granica, što izgleda tačno kao pokvaren font. Kada je graf izgrađen, preslikavanje je direktno:
PaintGlyphpostavlja konturu glifa kao clip sa text rendering mode 7 (ISO 32000-1 §9.3.6), pa boji svoje dete unutar nje- Solid paint-ovi pune isečen pravougaonik; linear gradijenti postaju multi-stop axial shading-i a radial gradijenti dvobojni radial shading-i (§8.7.4.5)
- Sweep gradijenti nemaju PDF ekvivalent, pa ih HotPDF aproksimira sa 96 ravnih obojenih klinova, svaki uzorkovan sa color linije
- Transformacije se emituju kao
cm, konjugovane oko baseline porekla glifa, sa translacijama skaliranim saFontSize / UnitsPerEm PaintCompositemode-ovi 13 do 27 mapiraju se na separabilne i neseparabilne PDF blend mode-ove poput/Multiply,/Screeni/Luminosity(§11.3.5), postavljene kroz ExtGState/BMunos
Granica je eksplicitna. Porter-Duff mode-ovi 5 do 12 (src_in, xor, plus i ostali) nemaju PDF blend-mode dvojnika, repeat i reflect extend mode-ovi na linear i radial gradijentima se ne emituju, i gradijenti čiji stop-ovi nose različite alfa vrednosti ne glume se jednim opacity-jem. Radial gradijenti sa više od dva stop-a zadržavaju samo prvu i poslednju boju. HotPDF proverava ceo graf naspram ovog podržanog podskupa pre nego što napiše ijedan operator, pa nepodržani glif ostavlja stranicu netaknutom i prelazi na raster rezervu umesto da ostavi pola crteža
SVG glifovi i bitmap strike-ovi
SVG glifovi prolaze kroz isti ograničeni builder koji HotPDF koristi za uvezene SVG fajlove, i rezultat se registruje kao Form XObject (§8.10), tačno kao što je opisano u članku od SVG do Form XObject. Dokument u SVG tabeli može biti gzip-kompresovan; dekompresija ide u blokovima od 8 KB i staje čim proširena veličina pređe 32 MB, umesto da prvo inflate-uje pa proveri posle, a sam kompresovani ulaz je ograničen na 8 MB. Profil je ograničavajući namerno: skripte, ugrađene slike, spoljni URL-ovi, data: URI-jevi i nelokalne reference padaju zatvoreno. Forma se skalira tako da duža strana bude jednaka veličini fonta i sidri se na baseline, čime se y-naniže SVG koordinatni sistem preslikava na y-naviše PDF sistem. Imajte na umu da builder dobija ceo SVG dokument glifa, bez odabira glyphNNN elementa, pa fontove koji guraju mnogo glifova u jedan deljeni dokument vredi testirati pre nego što se na njih oslonite
Bitmap fontovi su pitanje izbora strike-a i pozicioniranja. Za CBDT, HotPDF bira CBLC veličinu čiji je vertikalni ppem najbliži TargetPixelsPerEm-u, prihvata image formate 17, 18 i 19, a metrike formata 19 čita iz CBLC index podtabele jer taj format ne čuva svoje. Za sbix, strike ofseti su relativni na tabelu a ofseti glifa na strike, i dupe zapis koristi grafiku drugog glifa zadržavajući sopstvene origin ofsete; ako rekurzija pregazi spoljašnji origin, slika se pomeri. PNG i JPEG payload-i se dekoduju interno, skaliraju sa FontSize / PixelsPerEmY umesto da se razvuču na veličinu fonta, i upisuju se sa soft maskom (§11.6.5.3) kad god ijedan piksel nije potpuno neprovidan. sbix TIFF payload-i se ne dekoduju i odlaze na događaj
Šta se dešava kad glif ne može da se nacrta nativno?
HotPDF okida OnColorGlyphRasterize i smešta bilo koju RGBA bitmapu koju vaš handler vrati; ako ništa nije dodeljeno, ili handler ostavi Handled na false, DrawRegisteredColorGlyph vraća False i stranica ostaje nepromenjena. Događaj se okida za COLR v1 graf van podržanog podskupa, SVG dokument koji sigurni builder odbio, i bitmap payload koji interni dekoderi ne čitaju. Handler dobija format, sirove bajtove fonta, izvučeni resurs (SVG dokument, možda još uvek gzipovan, ili bajtove bitmape; prazno za COLR v1), ID glifa, paletu i ciljnu veličinu piksela
type
TEmojiFallback = class
public
procedure Rasterize(Sender: TObject;
Format: THPDFOpenTypeColorFormat; const FontBytes: TBytes;
const AssetData: TBytes; GlyphID: Word;
PaletteIndex, PixelSize: Integer;
out Width, Height: Integer; out RGBA: TBytes;
out Handled: Boolean);
end;
procedure TEmojiFallback.Rasterize(Sender: TObject;
Format: THPDFOpenTypeColorFormat; const FontBytes: TBytes;
const AssetData: TBytes; GlyphID: Word;
PaletteIndex, PixelSize: Integer;
out Width, Height: Integer; out RGBA: TBytes;
out Handled: Boolean);
begin
Width := 0;
Height := 0;
RGBA := nil;
// RenderWithOwnEngine je vaš rasterizer, a ne HotPDF API.
// Mora da vrati tačno Width * Height * 4 bajtova RGBA.
Handled := RenderWithOwnEngine(Format, FontBytes, AssetData,
GlyphID, PaletteIndex, PixelSize, Width, Height, RGBA);
end;
// Povezivanje
Pdf.OnColorGlyphRasterize := Fallback.Rasterize;
HotPDF validira izlaz handlera pre nego što dotakne stranicu: nulte veličine, bafer čija dužina nije tačno Width * Height * 4, ili dimenzije dovoljno velike da preliju odbijaju se i poziv vraća False. Raster rezerva je i dalje raster, pa emoji prikazan ovako gubi vektorsku oštrinu; tražite PixelSize koji odgovara vašoj izlaznoj rezoluciji. Uparite putanju u boji sa proverama pokrivenosti pri crtanju iz članka o praćenju nedostajućih glifova i pipeline koji radi sa proizvoljnim korisničkim tekstom može da prijavi i glifove koji nedostaju i glifove koji su izgubili boju
Prikazivač glifova u boji, OpenType shaping stek i sigurni SVG builder isporučuju se svi u HotPDF Delphi PDF component-i, dostupnoj za Delphi i C++Builder