Skip to content

Instantly share code, notes, and snippets.

@inklesspen
Created November 27, 2023 22:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save inklesspen/4676fe4ff92a4bda275c0f8c2e14ed53 to your computer and use it in GitHub Desktop.
Save inklesspen/4676fe4ff92a4bda275c0f8c2e14ed53 to your computer and use it in GitHub Desktop.
import math
import fontPens.marginPen
import fontTools.designspaceLib
import fontTools.feaLib.parser
import ufoLib2
DOT_BELOW_Y_MAX = -50
def x_center(bbox: ufoLib2.objects.misc.BoundingBox):
width = bbox.xMax - bbox.xMin
return round(bbox.xMin + width / 2)
def h_center(font, glyphname):
if font.info.italicAngle:
# extrapolate a lower center of h.
slope = math.tan(math.radians(90 + font.info.italicAngle))
# based on eyeballing, do about 40 y units
italic_x_delta = round(40 / slope)
marginpen = fontPens.marginPen.MarginPen(font, 0)
font[glyphname].draw(marginpen)
return (
x_center(
ufoLib2.objects.misc.BoundingBox(
marginpen.getMargins()[0], 0, marginpen.getMargins()[1], 0
)
)
- italic_x_delta
)
return x_center(font[glyphname].getBounds(font.layers.defaultLayer))
def process_ufo(ufopath):
font = ufoLib2.objects.Font.open(ufopath)
# font.glyphOrder always returns a new list. so we must save a copy, modify it,
# and add it back into the font.
glyph_order = font.glyphOrder
# create dotbelowcomb
newg = font["dotaccentcomb"].copy("dotbelowcomb")
newg.unicodes = [0x0323]
y_delta = -(
font["dotaccentcomb"].getBounds(font.layers.defaultLayer).yMax - DOT_BELOW_Y_MAX
)
newg.move((0, y_delta))
assert len(font["dotaccentcomb"].anchors) == 1
assert font["dotaccentcomb"].anchors[0].name == "_top"
newg.anchors = [
ufoLib2.objects.Anchor(
x=font["dotaccentcomb"].anchors[0].x, y=0, name="_bottom"
)
]
font.addGlyph(newg)
font.lib["public.postscriptNames"]["dotbelowcomb"] = "uni0323"
# insert in glyph list _after_ dotaccentcomb
glyph_order.insert(glyph_order.index("dotaccentcomb") + 1, "dotbelowcomb")
# create hdotbelow
x_delta = h_center(font, "h") - x_center(
font["dotbelowcomb"].getBounds(font.layers.defaultLayer)
)
newg = ufoLib2.objects.Glyph(
name="hdotbelow",
unicodes=[0x1E25],
width=font["h"].width,
components=[
ufoLib2.objects.Component(
"dotbelowcomb",
transformation=fontTools.misc.transform.Offset(x=x_delta, y=0),
),
ufoLib2.objects.Component("h"),
],
)
font.addGlyph(newg)
# insert in glyph list _before_ i
glyph_order.insert(glyph_order.index("i"), "hdotbelow")
# create Hdotbelow
x_delta = h_center(font, "H") - x_center(
font["dotbelowcomb"].getBounds(font.layers.defaultLayer)
)
newg = ufoLib2.objects.Glyph(
name="Hdotbelow",
unicodes=[0x1E24],
width=font["H"].width,
components=[
ufoLib2.objects.Component(
"dotbelowcomb",
transformation=fontTools.misc.transform.Offset(x=x_delta, y=0),
),
ufoLib2.objects.Component("H"),
],
)
font.addGlyph(newg)
# insert in glyph list _before_ I
glyph_order.insert(glyph_order.index("I"), "Hdotbelow")
# create hdotbelow.sc
x_delta = h_center(font, "h.sc") - x_center(
font["dotbelowcomb"].getBounds(font.layers.defaultLayer)
)
newg = ufoLib2.objects.Glyph(
name="hdotbelow.sc",
width=font["h.sc"].width,
components=[
ufoLib2.objects.Component(
"dotbelowcomb",
transformation=fontTools.misc.transform.Offset(x=x_delta, y=0),
),
ufoLib2.objects.Component("h.sc"),
],
)
font.addGlyph(newg)
# insert in glyph list _before_ i.sc
glyph_order.insert(glyph_order.index("i.sc"), "hdotbelow.sc")
# create uni02BC (modifier letter apostrophe)
newg = ufoLib2.objects.Glyph(
name="uni02BC",
unicodes=[0x02BC],
width=font["quoteright"].width,
components=[ufoLib2.objects.Component("quoteright")],
)
font.addGlyph(newg)
# insert in glyph list _after_ quotesingle
glyph_order.insert(glyph_order.index("quotesingle") + 1, "uni02BC")
equivs = [
("h", "hdotbelow"),
("H", "Hdotbelow"),
("h.sc", "hdotbelow.sc"),
]
# add hdotbelow, Hdotbelow, and hdotbelow.sc to groups
for group in font.groups.values():
for find, add in equivs:
if find in group:
group.append(add)
group.sort()
# there are no kerning pairs in font.kerning involving these glyphs
for kern_pair in font.kerning:
for find, add in equivs:
assert kern_pair[0] != find
assert kern_pair[1] != find
# update font family name
font.info.familyName += " Straylight"
font.info.styleMapFamilyName += " Straylight"
font.glyphOrder = glyph_order
font.save(overwrite=True)
def process_designspace(dspath):
dsd = fontTools.designspaceLib.DesignSpaceDocument.fromfile(dspath)
for source in dsd.sources:
process_ufo(source.path)
rawxml = open(dspath).read()
modified = rawxml.replace("Ibarra Real Nova", "Ibarra Real Nova Straylight")
with open(dspath, mode="w") as dsfile:
dsfile.write(modified)
def main():
process_designspace("./ibarrareal/sources/IbarraRealNova.designspace")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment