Skip to content

Commit

Permalink
Merge pull request #988 from googlefonts/uvs
Browse files Browse the repository at this point in the history
Handle Unicode Variation Sequences
  • Loading branch information
khaledhosny authored Feb 19, 2024
2 parents 70594b1 + 80103b0 commit 90e3557
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 0 deletions.
31 changes: 31 additions & 0 deletions Lib/glyphsLib/builder/glyph.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,25 @@ def _clone_layer(layer, paths=None, components=None):
return new_layer


# Map of ".uvNNN" extensions to Unicode Variation Selector code points.
# If a glyph name ends with ".uvNNN" with NNN ranging from 001 to 256, then it
# is a variation sequence with uv001 being U+0xFE00 and uv256 being U+0xE01EF.
#
# The only documentation for this is the "More Improvements" section in Glyphs
# 2.6.1 announcement:
# https://glyphsapp.com/news/glyphs-2-6-1-released
# And this forum post:
# https://forum.glyphsapp.com/t/unicode-variation-selector-u-fe01/21701
USV_MAP = {
f".uv{i+1:03}": f"{c:04X}"
for i, c in enumerate(
itertools.chain(range(0xFE00, 0xFE0F + 1), range(0xE0100, 0xE01EF + 1))
)
}

USV_EXTENSIONS = tuple(USV_MAP.keys())


def to_ufo_glyph(self, ufo_glyph, layer, glyph, do_color_layers=True): # noqa: C901
"""Add .glyphs metadata, paths, components, and anchors to a glyph."""
ufo_font = self._sources[layer.associatedMasterId or layer.layerId].font
Expand All @@ -68,6 +87,18 @@ def to_ufo_glyph(self, ufo_glyph, layer, glyph, do_color_layers=True): # noqa:
else:
ufo_glyph.lib[GLYPHLIB_PREFIX + "Export"] = export

# If glyph name ends with ".uvNNN" find and the font has a glyph with the
# same name without the ".uvNNN", then add a Unicode Variation Sequence
# entry with the Unicode Variation Selector corresponding to the extension
# and the unicode of the base glyph.
if export and "." in glyph.name and glyph.name.endswith(USV_EXTENSIONS):
base_name, ext = glyph.name.rsplit(".", 1)
if base_name in glyph.parent.glyphs and glyph.parent.glyphs[base_name].unicode:
uni = glyph.parent.glyphs[base_name].unicode
usv = USV_MAP[f".{ext}"]
USV_KEY = PUBLIC_PREFIX + "unicodeVariationSequences"
ufo_font.lib.setdefault(USV_KEY, {}).setdefault(usv, {})[uni] = glyph.name

# FIXME: (jany) next line should be an API of GSGlyph?
glyphinfo = glyphsLib.glyphdata.get_glyph(ufo_glyph.name)
if self.glyphdata is not None:
Expand Down
19 changes: 19 additions & 0 deletions tests/builder/builder_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2389,6 +2389,25 @@ def test_load_kerning_bracket(ufo_module):
assert ds2.sources[3].font.kerning == {}


def test_unicode_variation_sequences(ufo_module):
font = generate_minimal_font()
add_glyph(font, "zero")["unicode"] = f"{ord('0'):04x}"
add_glyph(font, "zero.uv001")
add_glyph(font, "zero.uv255")
add_glyph(font, "u1F170")["unicode"] = "1F170"
add_glyph(font, "u1F170.uv015")
add_glyph(font, "u2EA41")["unicode"] = "2EA41"
add_glyph(font, "u2EA41.uv019")
ufo = to_ufos(font, ufo_module=ufo_module)[0]
unicodeVariationSequences = ufo.lib.get("public.unicodeVariationSequences")
assert unicodeVariationSequences == {
"FE00": {"0030": "zero.uv001"},
"FE0E": {"1F170": "u1F170.uv015"},
"E0102": {"2EA41": "u2EA41.uv019"},
"E01EE": {"0030": "zero.uv255"},
}


class _PointDataPen:
def __init__(self, **kwargs):
self.contours = []
Expand Down

0 comments on commit 90e3557

Please sign in to comment.