Skip to content

Instantly share code, notes, and snippets.

@miguelsousa
Created August 20, 2023 17:28
Show Gist options
  • Select an option

  • Save miguelsousa/66ee9504039a8b64c605defa316516c1 to your computer and use it in GitHub Desktop.

Select an option

Save miguelsousa/66ee9504039a8b64c605defa316516c1 to your computer and use it in GitHub Desktop.
Converts a Type 1 font into a OpenType-CFF font
# Copyright 2023 Adobe, Inc. All Rights Reserved.
# Author: Miguel Sousa
"""
Converts a Type 1 font into a OpenType-CFF font.
"""
import argparse
from fontTools.agl import toUnicode
from fontTools.fontBuilder import FontBuilder
from fontTools.misc.cliTools import makeOutputFileName
from fontTools.pens.basePen import NullPen
from fontTools.pens.t2CharStringPen import T2CharStringPen
from fontTools.t1Lib import T1Font
def convert_font(input_path):
t1 = T1Font(input_path)
t1.parse()
# Collect 'name' table strings
font_name = t1.font.get('FontName', '')
font_info = t1.font.get('FontInfo', {})
full_name = font_info.get('FullName', '')
font_version = font_info.get('version', '')
name_strings = dict(
familyName=font_info.get('FamilyName', ''),
styleName=font_info.get('Weight', ''),
fullName=full_name,
psName=font_name,
version=f"Version {font_version}",
uniqueFontIdentifier=f"{font_version};{font_name}",
)
# Collect glyph names
encoding = t1.font.get('Encoding')
char_names = t1.font.get('CharStrings')
gnames = []
for gname in encoding:
if gname not in gnames:
gnames.append(gname)
for gname in char_names:
if gname not in gnames:
gnames.append(gname)
# Infer 'cmap' table values from glyph names
cmap = {}
for gname in gnames:
char = toUnicode(gname)
if char:
cmap[ord(char)] = gname
# Collect charstrings and advance widths
char_strings = {}
adv_widths = {}
gset = t1.getGlyphSet()
npen = NullPen()
for gname in gnames:
glyph = gset[gname]
glyph.draw(npen)
gwidth = round(glyph.width)
adv_widths[gname] = gwidth
t2pen = T2CharStringPen(gwidth, None)
glyph.draw(t2pen)
cs = t2pen.getCharString()
char_strings[gname] = cs
fb = FontBuilder(1000, isTTF=False)
fb.setupGlyphOrder(gnames)
fb.setupCharacterMap(cmap)
fb.setupCFF(font_name, {"FullName": full_name}, char_strings, {})
lsb = {}
for gname, cs in char_strings.items():
xmin = 0
gbbox = cs.calcBounds(None)
if gbbox:
xmin, *_ = gbbox
lsb[gname] = xmin
metrics = {
gname: (adv_width, lsb[gname])
for gname, adv_width in adv_widths.items()
}
fb.setupHorizontalMetrics(metrics)
fb.setupHorizontalHeader(ascent=824, descent=-200)
fb.setupNameTable(name_strings, mac=False)
fb.setupOS2(sTypoAscender=824, usWinAscent=824, usWinDescent=200)
fb.setupPost()
save_path = makeOutputFileName(input_path, extension='.otf')
fb.save(save_path)
def main(args=None):
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('input_path', help='path to Type 1 font file')
opts = parser.parse_args(args)
convert_font(opts.input_path)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment