Tabela GSUB w pliku OpenType zawiera reguły podstawiania glifów - to właśnie tam font przechowuje informację, by zamiast standardowej formy litery użyć np. wariantu kaligraficznego albo cyfr podpisanych. Aplikacja zapytuje o podstawienie, wskazując glif źródłowy i znacznik cechy; font zwraca glif docelowy lub sygnalizuje, że dla tej kombinacji nie ma wariantu. HotPDF odpytuje te tabele bezpośrednio z czystego kodu Delphi, bez pośrednictwa biblioteki HarfBuzz
Dlaczego czyste Delphi zamiast HarfBuzz
HarfBuzz to kompletny silnik kształtowania tekstu napisany w C. Zintegrowanie go z projektem Delphi wymaga bindingów i zestawu bibliotek dll, a wynikowy potok stosuje całe pakiety cech, a nie selektywne zapytania. Implementacja HotPDF pyta tabelę GSUB bezpośrednio, co daje pełną kontrolę nad tym, które cechy są stosowane i kiedy, bez zewnętrznych zależności i bez dodatkowych plików do dostarczenia
Hierarchia GSUB od góry do dołu
Tabela GSUB ma strukturę pięciu poziomów. Na szczycie znajduje się sama tabela GSUB. Poniżej leżą listy skryptów, opisujące systemy pisma obsługiwane przez font. Każdy skrypt zawiera listę cech, a każda cecha wskazuje na co najmniej jeden lookup. Lookup to zestaw reguł podstawiania - to on dokonuje rzeczywistej zamiany glifów. Odpytanie o wariant stylistyczny polega na przejściu tej hierarchii: znalezieniu odpowiedniej cechy po znaczniku, a następnie sprawdzeniu lookupów powiązanych z tą cechą
Tabele pokrycia decydują o tym, kto uczestniczy
Każdy lookup zawiera tabelę pokrycia, która określa zbiór glifów wejściowych, do których dana reguła może mieć zastosowanie. Przed zastosowaniem podstawienia HotPDF sprawdza, czy glif źródłowy należy do zakresu pokrycia tego lookupu. Jeśli nie - lookup jest pomijany bez żadnego podstawienia. Tabele pokrycia mogą być zakodowane jako posortowana lista identyfikatorów glifów lub jako ciągłe zakresy; HotPDF obsługuje oba formaty
Podstawienie pojedyncze - dwa formaty
Lookup typu 1 (Single Substitution) zamienia jeden glif na jeden inny. Istnieją dwa sposoby jego zakodowania. Format 1 stosuje stałe przesunięcie: identyfikator glifu docelowego = identyfikator glifu źródłowego + delta. Każdy glif objęty pokryciem przesuwa się o tę samą wartość. Format 2 przechowuje jawną tablicę: dla każdego glifu ze zbioru pokrycia tabela zawiera bezpośredni identyfikator glifu zastępczego. Warianty stylistyczne niemal zawsze używają formatu 2, ponieważ alternatywne kształty rzadko układają się w prosty ciąg arytmetyczny
// GetSingleSubstituteGlyph zwraca identyfikator glifu zastępczego lub -1, jeśli brak wariantu
function GetSingleSubstituteGlyph(
FontHandle: THandle;
GlyphId: Word;
const FeatureTag: AnsiString): Integer;
Co oznaczają znaczniki cech stylistycznych
Znacznik cechy to czterobajtowy kod ASCII identyfikujący cel podstawienia. salt to "Stylistic Alternates" - cechy pokrewne stosowane łącznie. ss01 przez ss20 to dwadzieścia niezależnych zestawów stylistycznych, każdy z własnym zakresem glifów. aalt to "Access All Alternates" - lista wszystkich dostępnych wariantów dla danego glifu. titl to warianty tytułowe - często wersaliki z zmodyfikowaną kreską. subs i sups to odpowiednio indeks dolny i górny. ordn to warianty porządkowe (jak "a" w "1a"). frac to warianty ułamkowe - zmniejszone cyfry z kreską ułamkową
function ResolveAlternate(
FontHandle: THandle;
CodePoint: Cardinal;
const FeatureTag: AnsiString): Integer;
var
GlyphId: Integer;
begin
// Najpierw przetłumacz punkt kodowy na identyfikator glifu przez cmap
GlyphId := GetGlyphIdFromCodePoint(FontHandle, CodePoint);
if GlyphId < 0 then Exit(-1);
// Następnie sprawdź podstawienie GSUB dla żądanej cechy
Result := GetSingleSubstituteGlyph(FontHandle, GlyphId, FeatureTag);
end;
cmap format 12 i płaszczyzny uzupełniające
Podtabela cmap formatu 12 obsługuje pełny zakres Unicode, w tym płaszczyzny uzupełniające (punkty kodowe powyżej U+FFFF). Każdy wpis to zakres: startCodePoint, endCodePoint i startGlyphId. Glif dla danego punktu kodowego jest identyfikowany jako startGlyphId + (codePoint - startCodePoint). HotPDF przeszukuje te zakresy binarnie, co gwarantuje sprawne działanie nawet przy dużych fontach CJK posiadających tysiące wpisów w cmap
// Przykład dla punktu kodowego z płaszczyzny uzupełniającej
var
GlyphId: Integer;
begin
// U+1F600 leży poza BMP - wymaga cmap formatu 12
GlyphId := GetGlyphIdFromCodePoint(FontHandle, $1F600);
if GlyphId >= 0 then
// Dalsze przetwarzanie glifu...
end;
Gdzie kończą się te zapytania
Zapytania GSUB HotPDF odpowiadają na pytanie: "Jaki glif zastępuje dany glif w kontekście tej cechy?" Nie stosują one kaskadowego kształtowania tekstu, nie obsługują podstawień kontekstowych (lookup typu 4-8) ani nie realizują pozycjonowania (tabela GPOS). Działają dobrze dla wariantów stylistycznych, ułamków i form porządkowych - wszędzie tam, gdzie jedno wejście odpowiada jednemu wyjściu. Jeśli potrzebujesz pełnego kształtowania tekstu z ligatami kontekstowymi i złożonymi regułami pisma, musisz sięgnąć po dedykowany silnik kształtowania
Metody GetSingleSubstituteGlyph i GetGlyphIdFromCodePoint opisane na tej stronie są częścią komponentu HotPDF dla Delphi i C++Builder