WE10JL: Conversión de Texturas de Fuentes a TTF Vectorial con Python
WE10JL REVERSE ENGINEERING & MODDING
Conversión de Fuentes Bitmap a TrueType (.TTF) en Python
Análisis técnico de extracción de texturas desentrelazadas y compilación vectorial OpenType para Windows.
1. Mapeo de Archivos BMP y Estructura ASCII
Las texturas de la tipografía extraídas mediante WE Picture Decoder generan una secuencia numerada de archivos .bmp. Para garantizar la correspondencia exacta con el teclado de Windows, mapeamos los índices según la tabla ASCII estándar:
| Rango de Archivos | Mapeo de Caracteres | Offset ASCII / Código |
|---|---|---|
| 66.bmp - 75.bmp | Números (0 al 9) | 0x30 - 0x39 48 - 57 |
| 76.bmp - 101.bmp | Alfabeto Mayúsculas (A a la Z) | 0x41 - 0x5A 65 - 90 |
| 102.bmp - 127.bmp | Alfabeto Minúsculas (a a la z) | 0x61 - 0x7A 97 - 122 |
2. Inversión del Eje Y y Escala Vectorial
A diferencia de las imágenes matriciales donde el punto (0,0) está arriba a la izquierda, las fuentes TrueType sitúan la línea de base en la esquina inferior izquierda. La conversión de coordenadas se define mediante la fórmula:
vy = (altura_imagen - y - 1) * scale
Donde scale = 32 proyecta la matriz de la textura a la caja em estándar de 1024 unitsPerEm.
3. Script de Automatización (generar_ttf.py)
import os
from PIL import Image
from fontTools.fontBuilder import FontBuilder
from fontTools.pens.ttGlyphPen import TTGlyphPen
FOLDER_PATH = r"C:\temp"
OUTPUT_TTF = r"C:\temp\WE10_Font.ttf"
def crear_ttf_preciso():
glyph_order = [".notdef"]
glyphs = {}
# Glifo por defecto (.notdef)
pen = TTGlyphPen(None)
pen.moveTo((0, 0))
pen.lineTo((512, 0))
pen.lineTo((512, 1024))
pen.lineTo((0, 1024))
pen.closePath()
glyphs[".notdef"] = pen.glyph()
scale = 32
metrics = {".notdef": (512, 0)}
cmap = {32: ".notdef"}
mapeo_archivos = {}
for i, num in enumerate("0123456789"): mapeo_archivos[66 + i] = ord(num)
for i, char in enumerate("ABCDEFGHIJKLMNOPQRSTUVWXYZ"): mapeo_archivos[76 + i] = ord(char)
for i, char in enumerate("abcdefghijklmnopqrstuvwxyz"): mapeo_archivos[102 + i] = ord(char)
for num_archivo, ascii_code in mapeo_archivos.items():
path = os.path.join(FOLDER_PATH, f"{num_archivo}.bmp")
if not os.path.exists(path): continue
glyph_name = f"char_{ascii_code}"
glyph_order.append(glyph_name)
cmap[ascii_code] = glyph_name
img = Image.open(path).convert("RGB")
width, height = img.size
pen = TTGlyphPen(None)
tiene_pixeles = False
for y in range(height):
for x in range(width):
r, g, b = img.getpixel((x, y))
if (r + g + b) > 40:
tiene_pixeles = True
vx, vy = x * scale, (height - y - 1) * scale
pen.moveTo((vx, vy))
pen.lineTo((vx + scale, vy))
pen.lineTo((vx + scale, vy + scale))
pen.lineTo((vx, vy + scale))
pen.closePath()
if not tiene_pixeles:
pen.moveTo((0, 0))
pen.closePath()
glyphs[glyph_name] = pen.glyph()
metrics[glyph_name] = (width * scale, 0)
fb = FontBuilder(unitsPerEm=1024, isTTF=True)
fb.setupGlyphOrder(glyph_order)
fb.setupCharacterMap(cmap)
fb.setupHorizontalMetrics(metrics)
fb.setupGlyf(glyphs)
fb.setupHead(unitsPerEm=1024)
fb.setupHorizontalHeader(ascent=800, descent=-200)
fb.setupNameTable({
"familyName": "WE10 Retro Font",
"styleName": "Regular",
"uniqueFontIdentifier": "1.000;WE10;WE10RetroFont-Regular",
"fullName": "WE10 Retro Font",
"version": "Version 1.000",
"psName": "WE10RetroFont-Regular",
})
fb.setupOS2(sTypoAscender=800, sTypoDescender=-200, sTypoLineGap=100, usWinAscent=800, usWinDescent=200)
fb.setupPost()
fb.save(OUTPUT_TTF)
if __name__ == "__main__":
crear_ttf_preciso()
WE10JL Hex & Modding Lab © 2026 | Documentación técnica para PlayStation 2
Comentarios
Publicar un comentario