Created
November 27, 2023 22:06
-
-
Save inklesspen/4676fe4ff92a4bda275c0f8c2e14ed53 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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