PDF kent geen concept van een variabel lettertype. Een lettertype dat in een PDF-bestand is ingesloten, is een vaste set contouren met vaste metrieken, dus een variabel lettertype moet worden teruggebracht tot één statische instantie voordat het in een document kan worden opgenomen. HotPDF voert die instancing intern uit: u inspecteert de assen van een variabel lettertype, selecteert coördinaten zoals gewicht 620 of breedte 87,5, en de bibliotheek verwerkt die waarden in een compleet, zelfstandig lettertypeprogramma dat elke conforme PDF-lezer kan renderen
De reden waarom dit ertoe doet, is praktisch en niet theoretisch. Lettergieterijen leveren steeds vaker één variabel bestand in plaats van een dozijn statische gewichten, en ontwerpteams kiezen waarden die geen enkele benoemde instantie biedt. Zonder instancing valt een rapportgenerator terug op de standaardinstantie, waardoor de ontwerpbeslissing verloren gaat, of sluit de generator het hele variabele lettertype in en hoopt dat de viewer as-coördinaten respecteert waarvan hij onmogelijk weet kan hebben, iets wat geen enkele lezer verplicht is te doen
Wat moet instancing eigenlijk herbouwen?
Een OpenType-variabel lettertype slaat één standaardcontour per glyph op, plus een set deltawaarden geïndexeerd op positie in de ontwerpruimte. Het toepassen van een as-coördinaat is niet gewoon een kwestie van een getal in een header schrijven; het betekent het doorlopen van de gvar-tabel, het interpoleren van deltawaarden voor de gevraagde locatie, het verplaatsen van punten en vervolgens het herberekenen van alles wat van die punten was afgeleid. HotPDF herbouwt de glyph-contouren, de lange loca-tabel, de volledige horizontale en verticale metrieken, de globale bounding box van het lettertype en de sfnt-checksumaanpassing
Net zo belangrijk is wat wordt verwijderd. Een statische instantie mag fvar, avar, gvar, HVAR, VVAR, MVAR, STAT of cvar niet behouden, en een verouderde DSIG moet ook verwijderd worden, omdat de ondertekende bytes niet meer bestaan. Als een van deze achterblijft, ontstaat een lettertype dat beweert variabel te zijn terwijl het contouren draagt die al zijn verplaatst, en lezers die variaties wél toepassen, passen ze dan een tweede keer toe
Phantoompunten, en de valkuil van dubbele toepassing
De subtielste regel in het hele proces betreft de metrieken. In gvar omvat het puntenaantal voor een glyph de contourpunten, of de componentpunten voor een samengestelde glyph, plus vier phantoompunten die de linker zijmarge, de voorschotbreedte en hun verticale equivalenten coderen. Die phantoompunten zijn zelf ook onderhevig aan deltawaarden
Dus wanneer een lettertype een gvar-tabel heeft, leidt HotPDF de horizontale en verticale metrieken af van de geïnterpoleerde phantoompunten en past het niet ook nog eens HVAR of VVAR toe. Beide toepassen is de klassieke fout: dezelfde variatie wordt tweemaal toegepast en elke voorschotbreedte komt iets te breed uit, wat zich uit als tekst die geleidelijk naar rechts verschuift over een uitgevulde regel. Alleen wanneer een lettertype geen gvar heeft, verwerkt de bibliotheek de metriekvariatie-opslag rechtstreeks in hmtx of vmtx
Twee verdere details houden de geometrie eerlijk. Phantoompunten nemen nooit deel aan contourinterpolatie, dus punten die niet expliciet zijn vermeld voor een eenvoudige glyph worden per contour afgeleid met IUP, waarbij de phantoompunten worden uitgesloten. En samengestelde glyphs krijgen hun deltawaarden toegepast op componentoffsets die XY-parameters gebruiken, waarna de kindgrenzen recursief worden herberekend. Die recursie is begrensd in diepte en wordt op cycli gecontroleerd, omdat een vijandige of gewoon kapotte componentgraaf anders eindeloos zou kunnen doorrecursiëren
De ontwerpruimte inspecteren voordat u kiest
De eerste aanroep in elke instancing-workflow is InspectVariableFont, die de assen rapporteert en de benoemde instanties die de gieterij heeft gedefinieerd. As-records bevatten de vier-byte tag, de minimum-, standaard- en maximumwaarden, vlaggen en een naam-ID; benoemde instanties bevatten een subfamilienaam-ID, vlaggen, een optionele PostScript-naam-ID en één coördinaat per as:
var
Pdf: THotPDF;
Axes: THPDFVariableFontAxisArray;
Instances: THPDFVariableFontNamedInstanceArray;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.InspectVariableFont('C:\Fonts\Inter.ttf', Axes, Instances) then
begin
for I := 0 to High(Axes) do
Writeln(Format('%s min=%.1f default=%.1f max=%.1f',
[string(Axes[I].Tag), Axes[I].MinimumValue,
Axes[I].DefaultValue, Axes[I].MaximumValue]));
Writeln(Format('%d named instance(s) defined', [Length(Instances)]));
end
else
Writeln('not a variable font - embed it as an ordinary TrueType face');
finally
Pdf.Free;
end;
end;
Het rapporteren van het asbereik is belangrijk omdat as-waarden worden begrensd tot het bereik dat het lettertype opgeeft, niet tot het bereik dat uw UI aanbiedt. Een schuifregelaar waarmee een gebruiker gewicht 1000 kan aanvragen voor een lettertype waarvan de wght-as stopt bij 900, moet in de interface worden gecorrigeerd, niet stilzwijgend op het lettertype-niveau, anders komt de afgedrukte uitvoer niet overeen met de voorbeeldweergave
Coördinaten selecteren en het document genereren
As-selectie is toestandsgebonden en geldt voor lettertypen die daarna worden geregistreerd. SetVariableFontAxis neemt een vier-byte afdrukbare ASCII-tag en een eindige waarde, en wijst al het overige af met een exceptie in plaats van het stilzwijgend te negeren. ClearVariableFontAxes reset de selectie, en GetVariableFontAxisSelections rapporteert wat er momenteel openstaat, wat het waard is om te loggen in rapportmotoren waar meerdere codepaden hetzelfde documentobject kunnen hebben aangeraakt. De familie zelf wordt op naam geselecteerd via SetFont, precies zoals elk ander ingesloten TrueType-lettertype:
begin
Pdf := THotPDF.Create(nil);
try
Pdf.BeginDoc;
Pdf.SetVariableFontAxis('wght', 620); // semibold, geen benoemde instantie
Pdf.SetVariableFontAxis('wdth', 87.5); // enigszins smal
Pdf.CurrentPage.SetFont('Inter', [], 11);
Pdf.CurrentPage.TextOut(72, 720, 0, 'Quarterly results');
Pdf.ClearVariableFontAxes; // terug naar de standaardinstantie
Pdf.CurrentPage.SetFont('Inter', [], 10);
Pdf.CurrentPage.TextOut(72, 700, 0, 'Prepared by the finance team');
Pdf.EndDoc;
finally
Pdf.Free;
end;
end;
De gebeurtenis OnVariableFontInstance wordt geactiveerd zodra elke instantie is geproduceerd en rapporteert de gebruikte as-waarden, wat de goedkoopste manier is om in een log te bewijzen wat een bepaalde PDF daadwerkelijk bevat. Omdat elke afzonderlijke coördinatenset een afzonderlijk lettertypeprogramma oplevert, behandelt u as-selecties als onderdeel van uw font-cachesleutel; de cachingmechaniek wordt beschreven in de persistente font-subsetcache
Hoe instancing samenwerkt met subsetting en shaping
Instancing gebeurt vóór subsetting, en die volgorde is de juiste. Het geïnstantieerde lettertype is een normaal statisch TrueType-lettertype, dus de gewone subsetter behandelt het als elk ander: hij berekent de glyph-closure, behoudt de glyphs die het document daadwerkelijk gebruikt en laat de rest vallen. Het punt om op te letten is dat twee verschillende as-selecties van dezelfde familie twee verschillende lettertypeprogramma's zijn, dus een document dat gewicht 400 en gewicht 620 mengt, sluit twee subsets in, niet één gedeeld lettertype met twee instanties
Shaping wordt in principe niet beïnvloed en is het waard om in de praktijk te controleren. Layoutfuncties bevinden zich in GSUB en GPOS, die instancing behoudt, dus ligaturen en stilistische alternatieven blijven werken zoals beschreven in OpenType GSUB stilistische alternatieven. Wat verandert, is de positionering: een smalle instantie heeft smallere voorschotten dan de standaard, dus elke lay-out die tekst heeft gemeten vóór instancing, heeft de verkeerde breedtes gemeten. Meet met dezelfde as-selectie waarmee u zult renderen, en de discrepantie verdwijnt
Nog één defensieve opmerking uit de implementatie, nuttig voor iedereen die dit pad uitbreidt. Lettertypen zonder verticale metrieken evalueren nog steeds dynamische array-argumenten op de Delphi-aanroepplek, dus parse-time arrays worden altijd toegewezen in plaats van te vertrouwen op een HasVerticalMetrics-controle die een lege index kortsluit. Het is het soort taalniveau-detail dat een ogenschijnlijk beveiligde tak verandert in een access violation, precies bij de lettertypen waarmee u niet hebt getest
Ondersteuning voor variabele lettertypen past in dezelfde lettertypepijplijn als insluiten, subsetting en glyph-closure, die uitgebreider wordt beschreven in font-subsetclosure en geshapete glyphs. De volledige typografiefunctieset voor Delphi en C++Builder staat vermeld op de HotPDF Delphi PDF-componentpagina