Parsear un Font
Parsear Archivos de Fuente TrueType (TTF) — Referencia
Una guía práctica e independiente de implementación para leer el formato binario TTF. Está organizada alrededor del acto de parsear: la columna vertebral del documento es 5. Cómo parsear, que recorre el archivo en orden y enlaza cada paso con el diseño de bytes de esa tabla en 6. Referencia de tablas. Lee el flujo de parseo de arriba a abajo; accede al diseño de una tabla solo cuando necesites los campos exactos.
Contenido
1. Introducción
El objetivo es tomar un archivo .ttf como un arreglo plano de bytes y
convertirlo en datos estructurados — el encabezado del font, los contornos de
glifos, las métricas, el mapa de caracteres. Este documento cubre cómo
hacerlo: la forma del archivo, el orden de lectura, cómo mover el cursor de
lectura a través de los bytes, y el diseño de bytes de cada tabla principal. Los
diseños de campos se dan como tablas simples Tipo | Nombre | Notas para que se
correspondan directamente con una struct en cualquier lenguaje.
No contiene código de programa — es la referencia de formato a partir de la cual se construye el código.
2. Modelo mental
Un archivo TTF no es un documento que se lee de principio a fin. Es un pequeño archivo comprimido (el formato se llama “sfnt” — scalable font). Sus bytes se organizan como:
- Una tabla de offsets de 12 bytes (el encabezado sfnt): qué tipo de font es y cuántas tablas contiene.
- Un directorio de tablas: un registro de tamaño fijo por tabla, cada uno indicando “la tabla con esta etiqueta vive en este offset de byte y tiene esta longitud.”
- Los datos de la tabla, ubicados sólo por los offsets en el directorio.
Núnca asumes dónde está algo. Este tipo de archivos no sigue ninguna convención de orden. Lees el offset y la longitud de una tabla desde el directorio, luego saltas allí.
Ayuda saber para qué sirven las tablas antes de parsearlas. Así es como un carácter se convierte en un glifo dibujado:
código de carácter ──(cmap)──► índice de glifo ──(loca)──► glyf (el contorno)
└─(hmtx)──► ancho de avance (el espaciado)
head y maxp son la configuración que hace que loca sea interpretable. Esta
cadena también explica por qué el orden de parseo en la sección 5 es el que es.
3. Convenciones
Endianness
Todo es big-endian (byte más significativo primero). Los dos bytes
0x00 0x09 significan 9, no 2304. No hay campos little-endian en ningún
lugar.
Tipos de datos
La especificación usa tipos con nombre. Estos son los que necesitas:
| Tipo | Bytes | Significado |
|---|---|---|
uint8 | 1 | byte sin signo |
int8 | 1 | byte con signo |
uint16 | 2 | short sin signo |
int16 | 2 | short con signo |
uint24 | 3 | entero de 24 bits sin signo |
uint32 | 4 | long sin signo |
int32 | 4 | long con signo |
Fixed | 4 | punto fijo con signo 16.16 |
FWORD | 2 | int16 en unidades de diseño del font |
UFWORD | 2 | uint16 en unidades de diseño del font |
F2Dot14 | 2 | punto fijo con signo 2.14 (escalas/variaciones) |
LONGDATETIME | 8 | int64 con signo, segundos desde 1904-01-01 00:00 UTC |
Tag | 4 | cuatro caracteres ASCII uint8, p.ej. glyf |
Offset16 | 2 | offset uint16 |
Offset32 | 4 | offset uint32 |
Version16Dot16 | 4 | versión mayor/menor empaquetada |
El signo importa. La mayoría de los helpers binarios decodifican solo
enteros sin signo. Para campos con signo (int16, int64, …), decodifica el
valor sin signo del mismo ancho y reinterpreta los bits como complemento a dos.
Si te equivocas, un número negativo pequeño (p.ej. un descender de -200) se
convierte en un positivo enorme y los límites de ancho/largo de las fuentes
suelen ser negativos, así que esto duele.
4. Estructura del archivo
La parte superior de todo TTF es un encabezado fijo seguido del directorio. Estas dos son las únicas partes que se leen linealmente; todo lo demás se alcanza por offset.
4.1 Tabla de offsets (encabezado sfnt) — 12 bytes, en el byte 0
| Tipo | Nombre | Notas |
|---|---|---|
uint32 | sfntVersion | 0x00010000 = contornos TrueType; 0x4F54544F (OTTO) = CFF/PostScript; 0x74727565 (true) en Apple. Tu verificación de “¿es un TTF?”. |
uint16 | numTables | número de registros de tabla que siguen |
uint16 | searchRange | (mayor potencia de 2 ≤ numTables) × 16 |
uint16 | entrySelector | log2(mayor potencia de 2 ≤ numTables) |
uint16 | rangeShift | numTables × 16 − searchRange |
searchRange, entrySelector y rangeShift son una optimización de búsqueda
binaria heredada. Puedes ignorar su significado, pero para un round-trip
byte-idéntico debes leerlos y re-emitirlos verbatim (o recomputarlos con las
fórmulas anteriores).
4.2 Directorio de tablas — numTables registros de 16 bytes, en el byte 12
| Tipo | Nombre | Notas |
|---|---|---|
Tag | tableTag | 4 bytes ASCII, p.ej. head, glyf |
uint32 | checksum | checksum de la tabla |
Offset32 | offset | desde el principio del archivo |
uint32 | length | la longitud real de la tabla, excluyendo bytes de relleno |
El registro i comienza en el byte 12 + i × 16.
- En fonts bien formados los registros están ordenados de forma ascendente por tag, pero los datos de la tabla a los que apuntan pueden aparecer en cualquier orden físico.
- Los datos de cada tabla están rellenados con bytes cero hasta un límite de 4
bytes.
lengthes la longitud real (sin relleno); eloffsetde la siguiente tabla tiene en cuenta el relleno.
5. Cómo parsear
Este es el núcleo del documento. El diagrama muestra el flujo de parseo completo y las dependencias entre tablas. A continuación, la secuencia exacta a seguir y la estrategia para decodificar cada tabla.
5.1 La secuencia de parseo
Dónde se ubica una tabla (físicamente) es independiente de cuándo la decodificas (lógicamente). El orden de decodificación está forzado por las dependencias de datos:
Sigue esta secuencia. Cada paso enlaza al diseño de bytes de esa tabla en la sección 7; la nota solo indica qué necesita y qué entrega el paso, no la definición completa.
- Tabla de offsets — diseño de bytes →. Leer los primeros 12
bytes; capturar
numTables. - Directorio de tablas — diseño de bytes →. Leer
numTables × 16bytes en un mapatag → (offset, length). Después de esto, cambiar de lectura lineal a búsqueda por offset. head— diseño de bytes →. EntregaunitsPerEm,indexToLocFormat, el bounding box del font.maxp— diseño de bytes →. EntreganumGlyphs.hhea— diseño de bytes →. EntreganumberOfHMetrics.hmtx— diseño de bytes →. NecesitanumberOfHMetrics(hhea) ynumGlyphs(maxp).loca— diseño de bytes →. NecesitaindexToLocFormat(head) ynumGlyphs(maxp).glyf— diseño de bytes →. Necesitalocapara delimitar cada glifo.cmap— diseño de bytes →. Independiente; el mapa de carácter → glifo.name— diseño →,post— diseño →,OS/2— diseño →,kern— diseño →. Independientes / opcionales; parsear según se necesite.
Valida sobre la marcha. sfntVersion es un valor conocido;
head.magicNumber == 0x5F0F3CF5; cada offset + length se mantiene dentro del
archivo; la última entrada de loca es igual a la longitud de la tabla glyf.
Estas verificaciones convierten entradas corruptas en errores claros en lugar de
lecturas fuera de rango.
5.2 Tablas de diseño fijo vs. variable — la estrategia de decodificación
Cómo decodificas una tabla depende de su forma:
- Tablas de diseño fijo tienen un conjunto fijo de campos de ancho fijo en un orden fijo, sin arreglos embebidos ni cadenas. El tamaño se conoce de antemano; puedes decodificar una en un solo pase posicional — siempre que el orden de campos de tu struct coincida exactamente con el orden en disco.
- Tablas de diseño variable contienen un contador, versión u offset que determina cuánto sigue — arreglos dimensionados por otro campo, cadenas, sub-tablas. El tamaño no se conoce hasta que lees; estas necesitan parsing condicional escrito a mano.
| Tabla | Clase | Por qué |
|---|---|---|
| Tabla offset | fijo | 12 bytes, campos fijos |
| Registro | fijo | 16 bytes, campos fijos |
head | fijo | 54 bytes, todos de ancho fijo |
maxp | fijo* | fijo por versión (0.5 vs 1.0) |
hhea | fijo | 36 bytes |
OS/2 | fijo* | fijo por versión (0–5) |
hmtx | variable | arreglo dimensionado por hhea.numberOfHMetrics + arreglo cola |
loca | variable | numGlyphs + 1 entradas; ancho de entrada definido por head |
glyf | variable | longitud variable por glifo, dos formatos de glifo |
cmap | variable | sub-tablas en varios formatos, enlazadas por offset |
name | variable | arreglo de registros + bloque de almacenamiento de cadenas |
post | variable* | encabezado fijo; v2.0 agrega un arreglo de nombres de glifos |
kern | variable | opcional; múltiples formatos de sub-tabla |
* = fijo una vez que conoces la versión; trata la palabra de versión como una
bifurcación.
6. Referencia de tablas
Los diseños de bytes a los que enlaza la secuencia de parseo en 5.1. Consulta solo lo que necesites.
6.1 head — encabezado del font (54 bytes, fijo)
El encabezado global del font. Contiene el tamaño de la grilla de diseño
(unitsPerEm), el bounding box que engloba todos los glifos del font, marcas
de tiempo de creación y modificación, y flags de estilo. El campo más crítico
para el parseo es indexToLocFormat: controla si la tabla loca usa offsets
de 2 o 4 bytes, por lo que head debe decodificarse antes de poder leer loca.
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | majorVersion | 1 |
uint16 | minorVersion | 0 |
Fixed | fontRevision | versión del diseñador; almacenar en bruto si no se interpreta |
uint32 | checksumAdjustment | checksum de todo el archivo; ver spec |
uint32 | magicNumber | siempre 0x5F0F3CF5 (gancho de validación) |
uint16 | flags | campo de bits |
uint16 | unitsPerEm | grilla de diseño; 16–16384, potencia de 2 para TrueType |
LONGDATETIME | created | int64, segundos desde 1904 |
LONGDATETIME | modified | int64 |
int16 | xMin | bounding box del font (¡con signo!) |
int16 | yMin | |
int16 | xMax | |
int16 | yMax | |
uint16 | macStyle | campo de bits |
uint16 | lowestRecPPEM | tamaño mínimo legible en píxeles |
int16 | fontDirectionHint | |
int16 | indexToLocFormat | 0 = loca corto, 1 = loca largo |
int16 | glyphDataFormat | 0 |
6.2 maxp — perfil máximo
Establece los requerimientos de memoria del font. Registra los conteos
máximos de puntos, contornos y profundidad del stack del intérprete en todos
los glifos, para que el rasterizador pueda pre-alocar exactamente lo que
necesita. El único campo estrictamente necesario para el parseo es numGlyphs,
que dimensiona los arreglos de loca y hmtx. La versión 0.5 (fonts CFF)
solo lleva ese campo; la versión 1.0 (TrueType) lleva el conjunto completo.
Versión 0.5 (0x00005000, fonts CFF — 6 bytes):
| Tipo | Nombre | Notas |
|---|---|---|
uint32 | version | 0x00005000 |
uint16 | numGlyphs | el único campo indispensable |
Versión 1.0 agrega (0x00010000, TrueType — 32 bytes en total):
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | maxPoints | máx. puntos en un glifo no compuesto |
uint16 | maxContours | máx. contornos en un glifo no compuesto |
uint16 | maxCompositePoints | máx. puntos en un glifo compuesto |
uint16 | maxCompositeContours | máx. contornos en un glifo compuesto |
uint16 | maxZones | 1 = sin twilight zone; 2 = twilight zone en uso |
uint16 | maxTwilightPoints | máx. puntos en la twilight zone |
uint16 | maxStorage | máx. ubicaciones de área de almacenamiento |
uint16 | maxFunctionDefs | máx. definiciones de función |
uint16 | maxInstructionDefs | máx. definiciones de instrucción |
uint16 | maxStackElements | profundidad máxima del stack |
uint16 | maxSizeOfInstructions | máx. bytes de instrucciones de glifo |
uint16 | maxComponentElements | máx. componentes en un glifo compuesto |
uint16 | maxComponentDepth | máx. profundidad de anidamiento de glifos compuestos |
6.3 hhea — encabezado horizontal (36 bytes, fijo)
Contiene las métricas necesarias para disponer glifos sobre una línea de base
horizontal: el ascendente, descendente e interlineado que los renderizadores
usan para el espaciado de líneas, y el ancho de avance máximo. Todos los
valores están en unidades de diseño del font (FUnits). El campo clave para el
parseo es numberOfHMetrics, que le dice al parser de hmtx cuántos pares
completos (ancho de avance + LSB) contiene esa tabla.
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | majorVersion | 1 |
uint16 | minorVersion | 0 |
FWORD | ascender | (int16) |
FWORD | descender | (int16, usualmente negativo) |
FWORD | lineGap | (int16) |
UFWORD | advanceWidthMax | (uint16) |
FWORD | minLeftSideBearing | (int16) |
FWORD | minRightSideBearing | (int16) |
FWORD | xMaxExtent | (int16) |
int16 | caretSlopeRise | pendiente del cursor (rise/run); 1 para vertical |
int16 | caretSlopeRun | 0 para vertical |
int16 | caretOffset | desplazamiento para resaltado inclinado; 0 si no es cursiva |
[4]int16 | (reserved) | establecer en 0 |
int16 | metricDataFormat | 0 |
uint16 | numberOfHMetrics | dimensiona la tabla hmtx |
6.4 hmtx — métricas horizontales (variable)
Almacena el ancho de avance y el left side bearing (LSB) de cada glifo. El
ancho de avance es cuánto se mueve el cursor después de dibujar el glifo; el
LSB es la distancia desde el origen del glifo hasta el borde izquierdo de su
bounding box. La tabla no tiene encabezado — son dos arreglos consecutivos
cuyos tamaños provienen de hhea.numberOfHMetrics y maxp.numGlyphs.
Dos arreglos consecutivos:
hMetrics:numberOfHMetricsentradas (dehhea), cada una:Tipo Nombre uint16advanceWidth int16lsb (left side bearing) leftSideBearings:numGlyphs − numberOfHMetricsentradas deint16.
El arreglo cola permite que las series monoespaciadas compartan un ancho de
avance: los glifos más allá de numberOfHMetrics reutilizan el último ancho de
avance y almacenan solo su side bearing.
6.5 loca — índice de ubicación (variable)
Mapea cada índice de glifo al offset de bytes de sus datos de contorno dentro
de la tabla glyf. Sin loca no puedes saber dónde empieza ningún glifo.
Almacena numGlyphs + 1 offsets — la entrada extra marca el fin del último
glifo, de modo que la longitud de cada glifo es simplemente
loca[i+1] − loca[i]. El ancho de cada entrada (2 o 4 bytes) lo establece
head.indexToLocFormat.
numGlyphs + 1 offsets hacia glyf. El + 1 existe para que la longitud de
cada glifo sea loca[i+1] − loca[i]; la entrada final marca el fin del último
glifo.
- Corto (
indexToLocFormat == 0):numGlyphs+1 × Offset16(uint16). El valor almacenado es el offset real ÷ 2 — multiplicar por 2 al leer. Esto funciona porque los datos de glifos están alineados a 2 bytes (todo offset real es par) y duplica el alcance de unuint16a ~128 KB. - Largo (
indexToLocFormat == 1):numGlyphs+1 × Offset32(uint32), el offset real directamente.
Si loca[i] == loca[i+1], el glifo i no tiene contorno (p.ej. el
espacio). Esto es común — manéjalo.
6.6 glyf — datos de glifos (variable, el difícil)
Contiene los datos de contorno reales de cada glifo — los contornos y puntos
de control que el rasterizador convierte en píxeles. No tiene encabezado de
tabla; son bloques de glifos compactados uno tras otro, localizados
completamente por loca. Cada bloque comienza con un encabezado común de 10
bytes que indica si el glifo es simple (contornos propios) o compuesto
(referencias a otros glifos), seguido de datos específicos del formato. Es la
tabla más compleja de parsear.
Sin encabezado de tabla. Solo bloques de glifos consecutivos, ubicados por
loca. Cada bloque comienza con un encabezado común:
| Tipo | Nombre | Notas |
|---|---|---|
int16 | numberOfContours | ≥ 0 → glifo simple; < 0 (usar −1) → glifo compuesto |
int16 | xMin | bounding box del glifo |
int16 | yMin | |
int16 | xMax | |
int16 | yMax |
Glifo simple (numberOfContours ≥ 0)
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | endPtsOfContours[numberOfContours] | último valor + 1 = cantidad total de puntos |
uint16 | instructionLength | bytes de hinting que siguen |
uint8 | instructions[instructionLength] | bytecode de hinting TrueType (se puede pasar opacamente) |
uint8 | flags[…] | un flag lógico por punto, comprimido (abajo) |
| — | xCoordinates[…] | codificado por delta, ancho según flags |
| — | yCoordinates[…] | codificado por delta, ancho según flags |
Cantidad total de puntos = endPtsOfContours[last] + 1. La necesitas para
saber cuántos flags y coordenadas leer.
Bits de flag (un uint8 por punto, lógicamente):
| Bit | Nombre | Significado |
|---|---|---|
0x01 | ON_CURVE_POINT | el punto está en la curva (vs. un punto de control Bézier fuera de curva) |
0x02 | X_SHORT_VECTOR | el delta x es de 1 byte (si no, 2 bytes o 0) |
0x04 | Y_SHORT_VECTOR | el delta y es de 1 byte |
0x08 | REPEAT_FLAG | el siguiente byte es un contador de repetición; repetir este flag ese número de veces adicionales |
0x10 | X_IS_SAME_OR_POSITIVE_X_SHORT_VECTOR | doble significado (abajo) |
0x20 | Y_IS_SAME_OR_POSITIVE_Y_SHORT_VECTOR | doble significado |
0x40 | OVERLAP_SIMPLE | los contornos pueden superponerse |
0x80 | reserved | establecer en 0 |
El arreglo de flags está comprimido: cuando REPEAT_FLAG (0x08) está
activado, el byte siguiente indica cuántos puntos extra comparten ese flag.
Leer flags en un bucle hasta acumular pointCount de ellos, expandiendo las
repeticiones sobre la marcha.
Decodificación de coordenadas (se muestra x; y es idéntico con
0x04/0x20). Las coordenadas se almacenan como deltas desde el punto
anterior (el delta del primer punto es desde 0), y todos los deltas x vienen
primero, luego todos los deltas y:
X_SHORT_VECTOR(0x02) activado → el delta x es 1 byte (uint8); su signo lo da0x10: activado ⇒ positivo, desactivado ⇒ negativo.X_SHORT_VECTORdesactivado:0x10activado ⇒ delta es 0 (esta x es igual a la x anterior).0x10desactivado ⇒ el delta x es unint16de 2 bytes.
Acumular deltas para obtener coordenadas absolutas.
Glifo compuesto (numberOfContours < 0)
Un bucle de componentes, cada uno referenciando otro glifo:
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | flags | flags del componente (abajo) |
uint16 | glyphIndex | el id del glifo componente |
| arg1, arg2 | — | int8/uint8 o int16/uint16 según ARG_1_AND_2_ARE_WORDS; si ARGS_ARE_XY_VALUES, offsets de posición con signo, si no, índices de coincidencia de puntos |
| transform | — | 0, 1, 2 o 4 × F2Dot14 según los flags de escala |
Bits de flag del componente:
| Bit | Nombre | Significado |
|---|---|---|
0x0001 | ARG_1_AND_2_ARE_WORDS | los args son de 16 bits (si no, de 8 bits) |
0x0002 | ARGS_ARE_XY_VALUES | los args son offsets (si no, índices de puntos) |
0x0004 | ROUND_XY_TO_GRID | |
0x0008 | WE_HAVE_A_SCALE | sigue un F2Dot14 de escala uniforme |
0x0020 | MORE_COMPONENTS | sigue otro componente; continuar el bucle |
0x0040 | WE_HAVE_AN_X_AND_Y_SCALE | siguen dos F2Dot14 |
0x0080 | WE_HAVE_A_TWO_BY_TWO | siguen cuatro F2Dot14 (matriz 2×2) |
0x0100 | WE_HAVE_INSTRUCTIONS | después del último componente: longitud uint16 + bytes de instrucción |
0x0200 | USE_MY_METRICS | |
0x0400 | OVERLAP_COMPOUND |
Continuar el bucle mientras MORE_COMPONENTS esté activado.
6.7 cmap — mapeo de carácter a glifo (variable)
Mapea códigos de carácter Unicode (u otras codificaciones) a índices de glifo. Es el puente entre el texto y los contornos — sin ella no puedes saber qué glifo dibujar para un carácter dado. La tabla contiene múltiples sub-tablas para distintas combinaciones de plataforma/codificación; eliges la más adecuada para tu caso (típicamente la sub-tabla Windows Unicode BMP, formato 4) y usas solo esa.
Encabezado:
| Tipo | Nombre |
|---|---|
uint16 | version (0) |
uint16 | numTables |
Luego numTables registros de codificación:
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | platformID | 0=Unicode, 1=Mac, 3=Windows |
uint16 | encodingID | específico de plataforma |
Offset32 | subtableOffset | desde el inicio de la tabla cmap |
Combinaciones comunes: (3,1) Windows BMP Unicode → usualmente formato 4;
(3,10) Windows Unicode completo → formato 12; (0,*) Unicode; (1,0) Mac
Roman → formato 0. Elegir la mejor sub-tabla, luego parsear según su palabra de
formato:
- Formato 0 — codificación por byte:
format,length,language, luegouint8 glyphIdArray[256]. - Formato 4 — mapeo por segmentos (BMP, el más usado):
format,length,language,segCountX2,searchRange,entrySelector,rangeShift, luego arreglos paralelosendCode[segCount], un pad reservado,startCode[segCount],idDelta[segCount],idRangeOffset[segCount], y unglyphIdArrayal final. - Formato 6 — tabla recortada: un rango contiguo de códigos.
- Formato 12 — cobertura segmentada (Unicode completo):
format(12),reserved,uint32 length,uint32 language,uint32 numGroups, luego grupos de{ uint32 startCharCode; uint32 endCharCode; uint32 startGlyphID }.
6.8 name — cadenas legibles por humanos (variable)
Almacena todos los textos legibles del font: nombre de familia, subfamilia, nombre completo, cadena de versión, aviso de copyright, marca registrada y más. Cada cadena se guarda una vez en un bloque de bytes al final de la tabla, y una lista de registros apunta a ese bloque con metadatos de plataforma, codificación, idioma e ID de nombre. Para la mayoría de los casos necesitas los IDs 1 (familia), 2 (subfamilia) y 4 (nombre completo), plataforma 3 (Windows), decodificados como UTF-16BE.
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | version | 0 o 1 |
uint16 | count | número de registros de nombre |
Offset16 | storageOffset | inicio del almacenamiento de cadenas, desde el inicio de la tabla |
Luego count registros de nombre:
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | platformID | |
uint16 | encodingID | |
uint16 | languageID | |
uint16 | nameID | qué es la cadena (1=familia, 2=subfamilia, 4=nombre completo, 5=versión, …) |
uint16 | length | longitud de la cadena en bytes |
Offset16 | stringOffset | desde storageOffset |
(La versión 1 agrega un langTagCount + registros de etiqueta de idioma antes
del almacenamiento.) Luego los bytes de cadena en bruto, codificados por
plataforma — usualmente UTF-16BE para la plataforma 3.
6.9 post — datos PostScript (variable, dependiente de versión)
Contiene metadatos específicos de PostScript: el ángulo de itálica, si el font
es monoespaciado (isFixedPitch), y opcionalmente un mapeo de índice de glifo
a nombre de glifo PostScript. El encabezado de 32 bytes siempre está presente;
el arreglo de nombres de glifos solo aparece en la versión 2.0. Para la mayoría
del trabajo de transformación solo necesitas isFixedPitch e italicAngle.
Encabezado fijo de 32 bytes:
| Tipo | Nombre |
|---|---|
Version16Dot16 | version |
Fixed | italicAngle |
FWORD | underlinePosition |
FWORD | underlineThickness |
uint32 | isFixedPitch |
uint32 | minMemType42 |
uint32 | maxMemType42 |
uint32 | minMemType1 |
uint32 | maxMemType1 |
- 1.0 (
0x00010000): solo encabezado (se asumen nombres de glifos Mac estándar). - 2.0 (
0x00020000): encabezado +uint16 numGlyphs+uint16 glyphNameIndex[numGlyphs]+ nombres en cadenas Pascal para índices ≥ 258. - 2.5: deprecado. 3.0 (
0x00030000): solo encabezado, sin nombres.
6.10 OS/2 — métricas OS/2 y Windows (fijo por versión)
La fuente principal de metadatos de clasificación del font para Windows y renderizadores multiplataforma. Contiene la clase de peso (thin → black), la clase de ancho (condensado → expandido), ascendente/descendente/interlineado tipográfico, métricas específicas de Windows, mapas de bits de cobertura Unicode y de páginas de código, la clasificación PANOSE y flags de permisos de incrustación. Esta es la tabla que modifican las transformaciones de negrita y ancho. La palabra de versión determina cuántos campos finales existen. Al leer versiones antiguas, rellenar con ceros hasta el tamaño completo del struct para que los campos faltantes queden en 0.
Versión 0 (78 bytes, todas las versiones):
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | version | 0–5 |
int16 | xAvgCharWidth | promedio ponderado del ancho de avance de minúsculas |
uint16 | usWeightClass | 100–900 (equivale a font-weight de CSS) |
uint16 | usWidthClass | 1–9, condensado → expandido |
uint16 | fsType | flags de permisos de incrustación |
int16 | ySubscriptXSize | |
int16 | ySubscriptYSize | |
int16 | ySubscriptXOffset | |
int16 | ySubscriptYOffset | |
int16 | ySuperscriptXSize | |
int16 | ySuperscriptYSize | |
int16 | ySuperscriptXOffset | |
int16 | ySuperscriptYOffset | |
int16 | yStrikeoutSize | |
int16 | yStrikeoutPosition | |
int16 | sFamilyClass | clasificación IBM de familia tipográfica |
uint8[10] | panose | clasificación PANOSE de 10 bytes |
uint32 | ulUnicodeRange1 | bits de cobertura de bloque Unicode 0–31 |
uint32 | ulUnicodeRange2 | bits 32–63 |
uint32 | ulUnicodeRange3 | bits 64–95 |
uint32 | ulUnicodeRange4 | bits 96–127 |
Tag | achVendID | identificador de vendedor de 4 caracteres |
uint16 | fsSelection | flags de estilo (cursiva, negrita, regular, …) |
uint16 | usFirstCharIndex | codepoint Unicode más bajo del font |
uint16 | usLastCharIndex | codepoint Unicode más alto del font |
int16 | sTypoAscender | ascendente tipográfico (FUnits) |
int16 | sTypoDescender | descendente tipográfico (FUnits, usualmente negativo) |
int16 | sTypoLineGap | interlineado tipográfico (FUnits) |
uint16 | usWinAscent | métrica de ascendente Windows |
uint16 | usWinDescent | métrica de descendente Windows (valor positivo) |
Versión 1 agrega (86 bytes en total):
| Tipo | Nombre | Notas |
|---|---|---|
uint32 | ulCodePageRange1 | bits de cobertura de página de código 0–31 |
uint32 | ulCodePageRange2 | bits 32–63 |
Versión 2 / 3 / 4 agrega (96 bytes en total):
| Tipo | Nombre | Notas |
|---|---|---|
int16 | sxHeight | altura de la ‘x’ minúscula (FUnits) |
int16 | sCapHeight | altura de la ‘H’ mayúscula (FUnits) |
uint16 | usDefaultChar | índice de glifo para el carácter por defecto |
uint16 | usBreakChar | índice de glifo para el carácter de separación |
uint16 | usMaxContext | longitud máxima del contexto de glifo objetivo |
Versión 5 agrega (100 bytes en total):
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | usLowerOpticalPointSize | tamaño óptico inferior ×20 |
uint16 | usUpperOpticalPointSize | tamaño óptico superior ×20 |
6.11 kern — kerning (opcional, variable)
Almacena ajustes de espaciado entre pares específicos de glifos. Donde hmtx
le da a cada glifo un único ancho de avance, kern permite decir “cuando una
‘A’ es seguida por una ‘V’, acércalas 40 unidades”. Es opcional — muchos fonts
modernos lo omiten y usan GPOS para un kerning contextual más potente. Cuando
está presente, es una secuencia de sub-tablas que cada una cubre un conjunto
de pares.
Opcional y históricamente complicado — Apple y Microsoft definen encabezados
incompatibles. Muchos fonts omiten kern (el kerning moderno vive en GPOS).
El formato Microsoft (el objetivo para fonts de la era OpenType):
Encabezado de tabla:
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | version | 0 |
uint16 | nTables | número de sub-tablas que siguen |
Encabezado por sub-tabla:
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | version | versión de sub-tabla (0) |
uint16 | length | longitud total en bytes (incluyendo este encabezado) |
uint16 | coverage | byte alto = formato (0 o 2); byte bajo = flags (ver abajo) |
Flags de coverage (byte bajo): bit 0 = horizontal, bit 1 = mínimo, bit 2 = cross-stream, bit 3 = override.
Formato 0 (pares kern ordenados — el caso común):
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | nPairs | número de pares kern |
uint16 | searchRange | (mayor potencia de 2 ≤ nPairs) × 6 |
uint16 | entrySelector | log2(mayor potencia de 2 ≤ nPairs) |
uint16 | rangeShift | nPairs × 6 − searchRange |
Luego nPairs entradas de 6 bytes cada una:
| Tipo | Nombre | Notas |
|---|---|---|
uint16 | left | índice del glifo izquierdo |
uint16 | right | índice del glifo derecho |
int16 | value | ajuste de kern en FUnits |
Los pares están ordenados por (left << 16) | right para búsqueda binaria.
Iterar sobre las sub-tablas mientras queden bytes.
7. Fuentes
- Especificación Microsoft OpenType — la referencia moderna definitiva; incorpora y reemplaza la especificación TrueType original: https://learn.microsoft.com/en-us/typography/opentype/spec/
- Manual de referencia TrueType de Apple — el formato original, autoritativo para los contornos TrueType: https://developer.apple.com/fonts/TrueType-Reference-Manual/
- ISO/IEC 14496-22 “Open Font Format” — la estandarización ISO, descargable gratuitamente desde ISO; equivalente a la especificación de Microsoft.
Cuando los dos discrepan en casos límite, tratar la especificación de Microsoft como canónica para fonts de la era OpenType.
Páginas por tabla, todas bajo
https://learn.microsoft.com/en-us/typography/opentype/spec/:
| Tabla | Página | Tabla | Página | |
|---|---|---|---|---|
| structure | otff | cmap | cmap | |
head | head | name | name | |
maxp | maxp | post | post | |
hhea | hhea | OS/2 | os2 | |
hmtx | hmtx | kern | kern | |
loca | loca | glyf | glyf |