1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
|
"""Update the GDEF definition in the feature file.
We want our own because Glyphs has the habit of propagating anchors on
_everything_, even symbols that happen to contain components of latin
glyphs with anchors.
"""
from pathlib import Path
from typing import Dict, Optional
import glyphsLib.builder.constants
import glyphsLib.glyphdata
import ufo2ft.filters
import ufoLib2
def opentype_categories(ufo: ufoLib2.Font) -> Dict[str, str]:
"""Returns a public.openTypeCategories dictionary.
Building it requires anchor propagation or user care to work as
expected, as Glyphs.app also looks at anchors for classification:
* base: any glyph that has an attaching anchor (such as "top"; "_top" does
not count) and is neither classified as Ligature nor Mark using the
definitions below;
* ligature: if subCategory is "Ligature" and the glyph has at least one
attaching anchor;
* mark: if category is "Mark" and subCategory is either "Nonspacing" or
"Spacing Combining";
* composite: never assigned by Glyphs.app.
See:
* https://github.com/googlefonts/glyphsLib/issues/85
* https://github.com/googlefonts/glyphsLib/pull/100#issuecomment-275430289
"""
# Drop glyphs that don't exist in font anymore.
existing: Dict[str, str] = ufo.lib.get("public.openTypeCategories", {})
categories: Dict[str, str] = {k: v for k, v in existing.items() if k in ufo}
category_key = glyphsLib.builder.constants.GLYPHLIB_PREFIX + "category"
subcategory_key = glyphsLib.builder.constants.GLYPHLIB_PREFIX + "subCategory"
for glyph in ufo:
assert glyph.name is not None
has_attaching_anchor = False
for anchor in glyph.anchors:
name = anchor.name
if not name:
continue
if not name.startswith("_"):
has_attaching_anchor = True
# First check glyph.lib for category/subCategory overrides. Otherwise,
# use global values from GlyphData.
glyphinfo = glyphsLib.glyphdata.get_glyph(glyph.name)
category: Optional[str] = glyph.lib.get(category_key, glyphinfo.category)
subcategory: Optional[str] = glyph.lib.get(
subcategory_key, glyphinfo.subCategory
)
if subcategory == "Ligature" and has_attaching_anchor:
categories[glyph.name] = "ligature"
elif category == "Mark" and (
subcategory == "Nonspacing" or subcategory == "Spacing Combining"
):
categories[glyph.name] = "mark"
elif category == "Letter" and has_attaching_anchor:
categories[glyph.name] = "base"
return categories
# Anchors have to be propagated before we can construct the GDEF table.
if __name__ == "__main__":
source_directory = Path(__file__).parent.parent / "src"
# The list of glyph names for anchor propagation is stored in the main UFO
# (main == whatever has info=1 set in the Designspace).
main_source_path = source_directory / "Cantarell-Regular.ufo"
main_source = ufoLib2.Font.open(main_source_path)
pre_filter, _ = ufo2ft.filters.loadFilters(main_source)
for pf in pre_filter:
pf(font=main_source) # Run propagation filters on main UFO
ot_categories = opentype_categories(main_source)
for ufo_path in source_directory.glob("*.ufo"):
ufo = ufoLib2.Font.open(ufo_path)
if ot_categories:
ufo.lib["public.openTypeCategories"] = ot_categories
else:
ufo.lib.pop("public.openTypeCategories", None)
ufo.save()
|