Motor de sustitución OpenType GSUB

Superficie de capacidades de sustitución de glifos (v2.119.43 - v2.119.50)

 

Modelado árabe  CFF / Subconjunto OpenType

El motor OpenType GSUB (Glyph SUBstitution) dentro de HotPDF permite consultar, controlar e incrustar todo tipo de sustitución de glifos que declare una fuente OpenType: ligaduras, alternas estilísticas, variantes contextuales, formas de modelado árabe / Indic, formas alternativas CJK y más. Todos los LookupType de OpenType GSUB del 1 al 8 están implementados y expuestos como una superficie de consulta solo de capacidades; quien llama controla la emisión de texto y decide qué glifo sustituto escribir en el flujo de contenido de la página

 

API pública

type

  TGSUBStringArray = array of AnsiString;

 

// LookupType 1 - Single Substitution (one glyph -> one glyph)

function GetSingleSubstituteGlyph(InputGID: Word; const FeatureTag: AnsiString): Word;

 

// LookupType 2 - Multiple Substitution (one glyph -> sequence of glyphs)

function GetMultipleSubstituteGlyphs(InputGID: Word; const FeatureTag: AnsiString;

  var OutGIDs: array of Word): Boolean;

 

// LookupType 3 - Alternate Substitution (one glyph -> one of N alternates)

function GetAlternateGlyphCount(InputGID: Word; const FeatureTag: AnsiString): Integer;

function GetAlternateGlyph(InputGID: Word; const FeatureTag: AnsiString;

  AlternateIndex: Integer): Word;

 

// LookupType 4 - Ligature Substitution (N glyphs -> one ligature)

function ApplyLigatureSubstitution(const InputGIDs: array of Word;

  StartIndex: Integer; const FeatureTag: AnsiString;

  out OutGID: Word; out ConsumedCount: Integer): Boolean;

 

// LookupType 5 + 6 - Contextual / Chained Contextual Substitution

function ApplyContextualSubst(const InputGIDs: array of Word;

  StartIndex: Integer; const FeatureTag: AnsiString;

  var OutGIDs: array of Word;

  out ConsumedLen: Integer): Boolean;

 

// LookupType 8 - Reverse Chained Contextual Single Substitution

function ApplyReverseChainedContextualSubst(const InputGIDs: array of Word;

  StartIndex: Integer; const FeatureTag: AnsiString;

  out OutGID: Word): Boolean;

 

// Script / LangSys selection (Phase 7)

procedure SetGSUBScript(const ScriptTag: AnsiString);

procedure SetGSUBLanguage(const LangTag: AnsiString);

function GetGSUBScripts: TGSUBStringArray;

function GetGSUBLanguages(const ScriptTag: AnsiString): TGSUBStringArray;

function GetGSUBFeatures(const ScriptTag, LangTag: AnsiString): TGSUBStringArray;

 

// TTF subsetter closure (Phase 9)

procedure MarkUnicodeGlyphUsed(GID: Word);

 

Descripción

El motor se activa después de que RegisterUnicodeTTF ha analizado una fuente y almacenado en caché sus tablas GSUB / GDEF / cmap. Cada consulta de sustitución recorre la cadena ScriptList / LangSysList / FeatureList / LookupList de la fuente y deriva al controlador LookupType correspondiente. Los 12 métodos anteriores constituyen la superficie pública completa; todo lo demás (recorrido de cmap, análisis de ScriptList, búsqueda de tablas Coverage, resolución de ClassDef, respeto de LookupFlag, desempaquetado de envoltorios Extension y despacho anidado de SequenceLookupRecord) vive detrás de esa superficie

 

Contrato defensivo en toda la superficie: las fuentes sin tabla GSUB, las etiquetas de función que no tienen 4 bytes, las funciones que el script / idioma seleccionado no anuncia, los GID que ninguna subtabela cubre y los glifos de entrada ignorados por LookupFlag devuelven siempre una operación segura sin efecto (False / OutGID = InputGID / OutGIDs vacíos / ConsumedCount = 1) para que los llamadores nunca vean excepciones en los casos rutinarios de "no se aplica ninguna sustitución"

 

Matriz de LookupType

LookupType 1 (Sustitución simple) - un glifo se asigna a un sustituto. Funciones canónicas: salt, ss01-ss20, smcp, onum, liga cuando LookupType 1 está conectado, además de las formas posicionales árabes init / medi / fina / isol en las fuentes que las canalizan por GSUB. Usa GetSingleSubstituteGlyph.

LookupType 2 (Sustitución múltiple) - un glifo se divide en una secuencia de glifos sustitutos. Uso canónico: ccmp para composición y descomposición de glifos (las letras latinas acentuadas precompuestas se separan en base + signos combinantes para el posicionamiento posterior de marcas). Usa GetMultipleSubstituteGlyphs.

LookupType 3 (Sustitución alternativa) - un glifo se asigna a una de N alternativas. Funciones canónicas: aalt (acceso a todas las alternativas), salt cuando está conectado como Type 3, titl (alternativas de título), y conjuntos estilísticos ss01-ss20 cuando el diseñador de la fuente ofrece más de una alternativa por posición. Usa GetAlternateGlyphCount + GetAlternateGlyph.

LookupType 4 (Sustitución de ligaduras) - N glifos de entrada se combinan en una sola ligadura. Funciones canónicas: liga (ligaduras estándar: fi / fl / ffi / ffl), clig (ligaduras contextuales), dlig (ligaduras discrecionales), hlig (ligaduras históricas), rlig (ligaduras obligatorias - Arabic LAM-ALEF y similares), ligaduras de escrituras índicas (akhn, pres, blws, psts). Usa ApplyLigatureSubstitution.

LookupType 5 (Sustitución contextual) + LookupType 6 (Sustitución contextual encadenada) - coincide con una secuencia de glifos de entrada y despacha búsquedas anidadas en posiciones específicas dentro de la coincidencia. Las tres variantes de formato (1 secuencia literal, 2 secuencia ClassDef, 3 secuencia Coverage) están implementadas; el despachador SequenceLookupRecord vuelve a entrar en la LookupList y maneja búsquedas anidadas de Single / Multiple / Alternate (first) / Ligature con seguimiento activo de MatchPositions. Funciones canónicas: rclt (alternativas contextuales requeridas - init/medi/fina/isol árabes cuando GSUB las controla), clig, calt, modelado índico pres / blws / psts / half / pstf / cjct. Usa ApplyContextualSubst (un único punto de entrada cubre LookupType 5 y 6)

LookupType 7 (Sustitución de extensión) - capa pura de indirección que la especificación OpenType define para fuentes cuya subtabla de sustitución queda más allá del alcance de 16 bits de la LookupList. Cada API pública sigue de forma transparente la indirección Offset32 de 32 bits hasta la subtabla real de LookupType 1 / 2 / 3 / 4 / 5 / 6 / 8. Desbloquea fuentes CJK / índicas pesadas (Noto Sans CJK, Noto Sans Devanagari) cuyo GSUB supera 64 KB. No hay API separada - el desempaquetado es automático

LookupType 8 (Sustitución simple contextual encadenada inversa) - sustitución 1:1 consciente del contexto cuya característica distintiva es que los llamadores deben aplicarla en orden de exploración inverso sobre una ejecución de varios glifos (fin -> inicio) porque cada sustituto puede depender del contexto futuro de vistazo que todavía no debe haberse sustituido. Uso canónico: alternativas contextuales árabes / siríacas / n'ko / índicas cuya forma final depende del glifo siguiente. Usa ApplyReverseChainedContextualSubst; el llamador controla el bucle de exploración inversa.

 

Selección de Script / LangSys

De forma predeterminada, el motor prefiere el script DFLT (o el primer script que declare la fuente) y el LangSys predeterminado. Llame a SetGSUBScript('latn' / 'arab' / 'cyrl' / 'hani' / 'kana' / 'deva' / 'beng' / 'taml' / etc.) y SetGSUBLanguage('ENG ' / 'TUR ' / 'AZE ' / 'JAN ' / 'KOR ' / 'ARA ' / etc., relleno con un espacio final hasta 4 bytes) para fijar las consultas a un par script/idioma concreto. Una cadena vacía restaura la línea base de la ruta predeterminada. Las selecciones persisten entre consultas y se limpian al llamar a RegisterUnicodeTTF('', nil)

 

Semántica estricta frente a respaldo: un ScriptTag desconocido hace que las consultas posteriores devuelvan resultados vacíos sin efecto (de modo que los llamadores puedan detectar que su script elegido no está disponible); un LangTag desconocido retrocede al LangSys predeterminado del script según la convención OpenType. GetGSUBScripts / GetGSUBLanguages / GetGSUBFeatures enumeran lo que la fuente cargada anuncia realmente

 

Respeto de LookupFlag y GDEF

Cada consulta lee el LookupFlag de cada tabla Lookup (y el markFilteringSet uint16 opcional al final cuando useMarkFilteringSet está activado) y omite los glifos de entrada marcados para ignorar. Se respetan los bits definidos por la especificación: ignoreBaseGlyphs (0x0002, omite la clase 1 de GDEF), ignoreLigatures (0x0004, omite la clase 2), ignoreMarks (0x0008, omite la clase 3), useMarkFilteringSet (0x0010) y el byte alto markAttachmentType. Se analizan tanto ClassDef Format 1 como 2; se aceptan encabezados GDEF v1.0 / v1.1 / v1.2. Las fuentes sin tabla GDEF retroceden a "ningún glifo se ignora", de modo que la salida sigue siendo idéntica a nivel de bytes para los llamadores que usan fuentes sin GDEF

 

Cierre del subconfigurador TTF (MarkUnicodeGlyphUsed)

El subconjunto TTF v2.84.0 de HotPDF deriva su conjunto de glifos usados a partir de FUnicodeUsedCps a través de la cmap. Los glifos sustitutos GSUB (alternativas estilísticas, ligaduras, variantes contextuales - todo lo que devuelven las 7 APIs de consulta anteriores) normalmente no tienen un punto de código que llegue a ellos por la cmap, así que antes eran invisibles para el subconjunto y el lector consumidor renderizaba .notdef en su lugar

 

Después de emitir cualquier GID devuelto por GetSingleSubstituteGlyph / GetMultipleSubstituteGlyphs / GetAlternateGlyph / ApplyLigatureSubstitution / ApplyContextualSubst / ApplyReverseChainedContextualSubst en una secuencia de texto PDF, llama a MarkUnicodeGlyphUsed(GID) una vez por cada GID emitido para incorporarlo al subconjunto incrustado. El helper es idempotente, defensivo (los GID fuera de rango se descartan en silencio) y se integra con la pasada de cierre de glifos compuestos v2.84.0: los llamadores solo necesitan marcar el GID sustituto de nivel superior - los componentes compuestos se incorporan automáticamente

 

Flujo de trabajo típico (versalitas latinas)

 

PDF.RegisterUnicodeTTF('myFont', 'C:\\Windows\\Fonts\\arial.ttf');

PDF.SetGSUBScript('latn');

PDF.SetGSUBLanguage('');  // default LangSys

SmallCapGID := PDF.GetSingleSubstituteGlyph(InputGID, 'smcp');

if SmallCapGID <> InputGID then

begin

  // emit SmallCapGID into the page content stream...

  PDF.MarkUnicodeGlyphUsed(SmallCapGID);  // pull into subset

end;

 

Flujo de trabajo típico (ligadura árabe LAM-ALEF)

 

PDF.SetGSUBScript('arab');

Run := [LamGID, FathaGID, AlefGID];  // post-cmap GIDs

if PDF.ApplyLigatureSubstitution(Run, 0, 'rlig', LigGID, ConsumedCount) then

begin

  // emit LigGID + advance by ConsumedCount

  PDF.MarkUnicodeGlyphUsed(LigGID);

end;

 

Alcance y limitaciones

El motor es una superficie de consulta de solo capacidades: responde "qué haría GSUB aquí", pero no ejecuta una canalización automática de modelado (disposición de clase Harfbuzz, reordenamiento consciente de clústeres para Indic, resolución BiDi, posicionamiento GPOS, adjunción de marcas). Los llamadores son responsables de conducir el bucle de exploración, elegir qué sustituto o alternativa emitir, llamar a MarkUnicodeGlyphUsed para cada GID sustituto emitido y aplicar cualquier posicionamiento GPOS o de marcas que requiera una fuente

 

El modelado árabe / persa / urdu del lado productor (ligadura obligatoria LAM-ALEF + Arabic Presentation Forms-A) está implementado como una canalización integrada separada que se ejecuta automáticamente durante la emisión de texto - vea Modelado árabe / persa / urdu

 

Trazado de versiones

v2.119.43 Sustitución simple + fase 1. v2.119.44 Sustitución múltiple + alternativa (fase 2). v2.119.45 Ligadura (fase 3). v2.119.46 Extensión + GDEF + respeto de LookupFlag (fase 4). v2.119.47 Sustitución contextual + contextual encadenada + despachador SequenceLookupRecord (fase 5). v2.119.48 Sustitución contextual encadenada inversa - matriz LookupType 1-8 cerrada (fase 6). v2.119.49 API de selección Script / LangSys (fase 7). v2.119.50 Cierre del subconjunto TTF mediante MarkUnicodeGlyphUsed (fase 9; la fase 8 fue una prueba de integración de modelado del lado productor y se dividió en 8a-8f para revisiones futuras)

 

Ver también: Modelado árabe / persa / urdu, Funciones de subconjunto de fuentes CFF / OpenType, THotPDF.EnableFontSubsetting