Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(font): rewrite makeFF in Python #3714

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
30 changes: 2 additions & 28 deletions dockers/fonts/buildFonts.sh
@@ -1,29 +1,6 @@
#!/usr/bin/env bash
shopt -s extglob

used_fonts=(
KaTeX_AMS-Regular
KaTeX_Caligraphic-Bold
KaTeX_Caligraphic-Regular
KaTeX_Fraktur-Bold
KaTeX_Fraktur-Regular
KaTeX_Main-Regular
KaTeX_Main-Bold
KaTeX_Main-Italic
KaTeX_Main-BoldItalic
KaTeX_Math-Italic
KaTeX_Math-BoldItalic
KaTeX_SansSerif-Bold
KaTeX_SansSerif-Italic
KaTeX_SansSerif-Regular
KaTeX_Script-Regular
KaTeX_Size1-Regular
KaTeX_Size2-Regular
KaTeX_Size3-Regular
KaTeX_Size4-Regular
KaTeX_Typewriter-Regular
)

filetypes=( ttf woff woff2 )

set -e
Expand All @@ -48,7 +25,7 @@ if [[ ! -f fonts/Makefile ]]; then
echo "src does not look like katex-fonts" >&2
exit 1
fi
tar cfP "$FILE" ../package.json fonts
tar cfP "$FILE" fonts
popd

# build image if missing
Expand Down Expand Up @@ -76,9 +53,6 @@ tar xf fonts.tar
mv fonts temp
mkdir fonts
for filetype in "${filetypes[@]}"; do
for font in "${used_fonts[@]}"; do
echo "$filetype/$font"
mv "temp/$filetype/$font".* ./fonts/
done
mv temp/$filetype/*.$filetype ./fonts/
done
rm -rf temp fonts.tar
112 changes: 46 additions & 66 deletions src/fonts/Makefile
Expand Up @@ -24,6 +24,26 @@ CUSTOM=custom.cfg

MFTRACE_MODIFIED=lib/mftrace-modified

PFA := \
cmbsy10.pfa \
cmbx10.pfa \
cmbxti10.pfa \
cmex10.pfa \
cmmi10.pfa \
cmmib10.pfa \
cmr10.pfa \
cmss10.pfa \
cmssbx10.pfa \
cmssi10.pfa \
cmsy10.pfa \
cmti10.pfa \
cmtt10.pfa \
msam10.pfa \
msbm10.pfa \
rsfs10.pfa \
eufb10.pfb \
eufm10.pfb

all: config fonts

$(CUSTOM):
Expand All @@ -39,77 +59,36 @@ $(CUSTOM).pl: $(CUSTOM)

.PHONY: config
config: $(CUSTOM).pl
mkdir -p pfa

blacker: $(MFTRACE_MODIFIED)
$(MFTRACE_MODIFIED):
$(PERL) -I. makeBlacker 15 # values between 10 and 30 seem best

pfa: $(MFTRACE_MODIFIED)
@echo "cmr10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmr10

@echo "cmmi10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --encoding $(TETEXENCODING)/aae443f0.enc --simplify cmmi10

@echo "cmsy10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --encoding $(TETEXENCODING)/10037936.enc --simplify cmsy10

@echo "cmex10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmex10

@echo "cmbx10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmbx10

@echo "cmbxti10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmbxti10

@echo "cmti10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmti10

@echo "msam10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify --encoding $(TETEXENCODING)/10037936.enc msam10

@echo "msbm10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify --encoding $(TETEXENCODING)/10037936.enc msbm10

@echo "cmmib10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --encoding $(TETEXENCODING)/aae443f0.enc --simplify cmmib10

@echo "cmbsy10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --encoding $(TETEXENCODING)/10037936.enc --simplify cmbsy10

@echo "cmtt10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmtt10

@echo "cmss10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmss10
@echo "cmssi10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmssi10
@echo "cmssbx10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify cmssbx10

@echo "eufm10"
cp "`$(KPSEWHICH) eufm10.pfb`" eufm10.pfb
@echo "eufb10"
cp "`$(KPSEWHICH) eufb10.pfb`" eufb10.pfb

# echo "eusm10"
# $(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify eusm10
# echo "eusb10"
# $(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify eusb10

@echo "rsfs10"
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify --encoding $(BASEENCODING)/tex256.enc rsfs10

mkdir -p pfa
rm -f pfa/*
mv *.pfa pfa
mv *.pfb pfa

ff: pfa
mkdir -p ff otf
rm -f ff/* otf/*
$(PERL) -I. makeFF
# Create the pfa files
%.pfa: $(MFTRACE_MODIFIED)
$(PYTHON) $(MFTRACE_MODIFIED) --magnification 1000 --simplify $*
mv $@ pfa/$@

# Create the pfb files
%.pfb: $(MFTRACE_MODIFIED)
cp "`$(KPSEWHICH) $@`" pfa/$@

# Create all the FontForge (.ff) scripts to generate the modern .ttf font files
# from the older TeX .pfa and .pfb files. The relation of the .ff and the
# .{pfa, pfb} files is many-to-many, and not one-to-one. Makefile doesn't like
# these types of relations, thus we have a single target that creates all of
# the .ff scripts.
# As a byproduct, the script also creates the initial .ttx files for all the
# different KaTeX fonts.
ff: $(PFA)
mkdir -p ff otf ttx
rm -f ff/* otf/* ttx/*
$(PYTHON) create_ff_scripts.py
for file in `ls ttx/*.ttx | $(SED) 's|ttx/\(.*\)\.ttx|\1|'`; do \
$(TTX) -f ttx/$$file.ttx;\
mv ttx/$$file.otf otf/$$file.otf;\
done

.PHONY: fonts
fonts: ff
Expand All @@ -131,6 +110,7 @@ fonts: ff
\
echo "Generating $$file..."; \
$(PYTHON) generate_fonts.py ttf/$$file.ttf; \
rm ttf/$$file.ttf; \
done

clean:
Expand Down
180 changes: 180 additions & 0 deletions src/fonts/create_ff_scripts.py
@@ -0,0 +1,180 @@
#! /usr/bin/env python3

import mapping
import extra

m = mapping.mapping

# This will contain the scripts for every font
scripts = {}
prefix = "KaTeX_"


def create_initial_ttx(family, style, normal):
with open('lib/Space.ttx', 'r') as f:
space = f.read()

if style == 'BoldItalic':
weight_s = 'Bold Italic'
else:
weight_s = style

space = space.format(name=family, weight=style,
weight_s=weight_s, normal=normal)

with open('ttx/{0}.ttx'.format(font_name), 'w') as w:
w.write(space)


# Return the font name and the style part of the font
def font_to_font_name(font):
s = font.split('-')

if len(s) == 1:
style = 'Regular'
else:
style = s[-1]

family = s[0]

return family + '-' + style, style


for tex_font in sorted(m.keys()):
for font in sorted(m[tex_font].keys()):
font_name, style = font_to_font_name(font)

# The original perl script never sets this to bold...
normal = 'Normal'

otf = 'otf/{0}.otf'.format(font_name)

if font not in scripts:
family = font.split('-')[0]
create_initial_ttx(prefix + family, style, normal)

if style[:4] == 'Bold':
weight = 700
else:
weight = 400

if style == 'Bold':
panose = 8
else:
panose = 0

scripts[font] = [
'Open("{0}")'.format(otf),
'SetPanose([0,0,{0},0,0,0,0,0,0,0])'.format(panose),
'SetOS2Value("Weight",{0})'.format(weight),
'SetGasp(8,2,16,1,65535,3)',
'Reencode("unicode")',
'Generate("{0}")'.format(otf),
]

if tex_font[:2] == 'eu':
ext = 'pfb'
else:
ext = 'pfa'

sc = scripts[font]

for k, v in m[tex_font][font].items():

# If we do not have a list, make one to simplify the code a lot
if type(v) is not list:
values = [v]
else:
values = v

for v in values:
open_str = 'Open("pfa/{0}.{1}")'
sc.append(open_str.format(tex_font, ext))

# Selector
if type(k) is tuple:
fr, to = k
selector = '{0},{1}'.format(fr, to)
else:
selector = '{0}'.format(k)

sc.append('Select({0})'.format(selector))
sc.append('Copy()')
sc.append('Open("{0}")'.format(otf))

if type(k) is tuple:
fr, to = k

if type(v) is tuple:
raise 4

sc.append('Select(0u{0:04x},0u{1:04x})'.format(
v, v + (to - fr)))
sc.append('Paste()')
elif type(v) is tuple:
to, shift_x, shift_y = v

sc.append('Select(0u{0:04x})'.format(to))
sc.append('Paste()')
sc.append('Move({0},{1})'.format(shift_x, shift_y))
else:
sc.append('Select(0u{0:04x})'.format(v))
sc.append('Paste()')

sc.append('Generate("{0}")'.format(otf))


e = extra.extra


def append_extras():
for font, extras in e.items():
script = scripts[font]

for key in sorted(extras.keys()):
script.extend(extras[key])


def cleanup_temp_glyphs():
scripts['Main-Regular'].extend([
'Select(0u23A9)',
'Clear()',
'Select(0u23AD)',
'Clear()'
])


def write_scripts():
for font, script in scripts.items():
font_name, _ = font_to_font_name(font)

otf = 'otf/{0}.otf'.format(font_name)
ttf = 'ttf/{0}.ttf'.format(font_name)
ff = 'ff/{0}.ff'.format(font_name)

extra = [
'SelectAll()',
'RoundToInt()',
'Simplify()',
'AddExtrema()',
'Simplify()',
'ClearHints()',
'AutoHint()',
'RoundToInt()',
'Generate("{0}")'.format(otf),
'SelectAll()',
'AutoInstr()',
'Generate("{0}")'.format(ttf)
]

script.extend(extra)

s = ';\n'.join(script)

with open(ff, 'w') as f:
f.write(s)


append_extras()
cleanup_temp_glyphs()
write_scripts()