Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do less #674

Merged
merged 9 commits into from
Sep 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Lib/glyphsLib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ def load_to_ufos(
family_name=None,
propagate_anchors=None,
ufo_module=None,
minimal=False,
):
"""Load an unpacked .glyphs object to UFO objects."""

Expand All @@ -68,6 +69,7 @@ def load_to_ufos(
family_name=family_name,
propagate_anchors=propagate_anchors,
ufo_module=ufo_module,
minimal=minimal,
)


Expand All @@ -85,6 +87,7 @@ def build_masters(
store_editor_state=True,
write_skipexportglyphs=False,
ufo_module=None,
minimal=False,
):
"""Write and return UFOs from the masters and the designspace defined in a
.glyphs file.
Expand Down Expand Up @@ -121,6 +124,7 @@ def build_masters(
store_editor_state=store_editor_state,
write_skipexportglyphs=write_skipexportglyphs,
ufo_module=ufo_module,
minimal=minimal,
)

# Only write full masters to disk. This assumes that layer sources are always part
Expand All @@ -131,7 +135,7 @@ def build_masters(
assert source.font is ufos[source.filename]
continue

if create_background_layers:
if create_background_layers and not minimal:
ufo_create_background_layer_for_all_glyphs(source.font)

ufo_path = os.path.join(master_dir, source.filename)
Expand Down
8 changes: 8 additions & 0 deletions Lib/glyphsLib/builder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def to_ufos(
generate_GDEF=True,
store_editor_state=True,
write_skipexportglyphs=False,
minimal=False,
):
"""Take a GSFont object and convert it into one UFO per master.

Expand All @@ -44,6 +45,10 @@ def to_ufos(

If generate_GDEF is True, write a `table GDEF {...}` statement in the
UFO's features.fea, containing GlyphClassDef and LigatureCaretByPos.

If minimal is True, it is assumed that the UFOs will only be used in
font production, and unnecessary steps (e.g. converting background layers)
will be skipped.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe list exactly what is dropped

"""
builder = UFOBuilder(
font,
Expand All @@ -54,6 +59,7 @@ def to_ufos(
generate_GDEF=generate_GDEF,
store_editor_state=store_editor_state,
write_skipexportglyphs=write_skipexportglyphs,
minimal=minimal,
)

result = list(builder.masters)
Expand All @@ -73,6 +79,7 @@ def to_designspace(
generate_GDEF=True,
store_editor_state=True,
write_skipexportglyphs=False,
minimal=False,
):
"""Take a GSFont object and convert it into a Designspace Document + UFOS.
The UFOs are available as the attribute `font` of each SourceDescriptor of
Expand Down Expand Up @@ -107,6 +114,7 @@ def to_designspace(
generate_GDEF=generate_GDEF,
store_editor_state=store_editor_state,
write_skipexportglyphs=write_skipexportglyphs,
minimal=minimal,
)
return builder.designspace

Expand Down
86 changes: 46 additions & 40 deletions Lib/glyphsLib/builder/builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ def __init__(
generate_GDEF=True,
store_editor_state=True,
write_skipexportglyphs=False,
minimal=False,
):
"""Create a builder that goes from Glyphs to UFO + designspace.

Expand Down Expand Up @@ -115,6 +116,7 @@ def __init__(
self.store_editor_state = store_editor_state
self.bracket_layers = []
self.write_skipexportglyphs = write_skipexportglyphs
self.minimal = minimal

if propagate_anchors is None:
propagate_anchors = font.customParameters["Propagate Anchors"]
Expand Down Expand Up @@ -186,13 +188,53 @@ def _is_vertical(self):

@property
def masters(self):
"""Get an iterator over master UFOs that match the given family_name.
"""
"""Get an iterator over master UFOs that match the given family_name."""
if self._sources:
for source in self._sources.values():
yield source.font
return

# TODO(jamesgk) maybe create one font at a time to reduce memory usage
# TODO: (jany) in the future, return a lazy iterator that builds UFOs
# on demand.
self.to_ufo_font_attributes(self.family_name)

self.to_ufo_layers()

for master_id, source in self._sources.items():
ufo = source.font
master = self.font.masters[master_id]
if self.propagate_anchors:
self.to_ufo_propagate_font_anchors(ufo)
for layer in list(ufo.layers):
self.to_ufo_layer_lib(master, ufo, layer)

# to_ufo_custom_params may apply "Replace Features" or "Replace Prefix"
# parameters so it requires UFOs have their features set first; at the
# same time, to generate a GDEF table we first need to have defined the
# glyphOrder, exported the glyphs and propagated anchors from components.
self.to_ufo_master_features(ufo, master)
self.to_ufo_custom_params(ufo, master)

if self.write_skipexportglyphs:
# Sanitize skip list and write it to both Designspace- and UFO-level lib
# keys. The latter is unnecessary when using e.g. the ufo2ft.compile*FromDS`
# functions, but the data may take a different path. Writing it everywhere
# can save on surprises/logic in other software.
skip_export_glyphs = self._designspace.lib.get("public.skipExportGlyphs")
if skip_export_glyphs is not None:
skip_export_glyphs = sorted(set(skip_export_glyphs))
self._designspace.lib["public.skipExportGlyphs"] = skip_export_glyphs
for source in self._sources.values():
source.font.lib["public.skipExportGlyphs"] = skip_export_glyphs

self.to_ufo_groups()
self.to_ufo_kerning()

for source in self._sources.values():
yield source.font

def to_ufo_layers(self):
# Store set of actually existing master (layer) ids. This helps with
# catching dangling layer data that Glyphs may ignore, e.g. when
# copying glyphs from other fonts with, naturally, different master
Expand All @@ -203,11 +245,6 @@ def masters(self):
# stores background data from "associated layers"
supplementary_layer_data = []

# TODO(jamesgk) maybe create one font at a time to reduce memory usage
# TODO: (jany) in the future, return a lazy iterator that builds UFOs
# on demand.
self.to_ufo_font_attributes(self.family_name)

# Generate the main (master) layers first.
for glyph in self.font.glyphs:
for layer in glyph.layers.values():
Expand Down Expand Up @@ -258,44 +295,13 @@ def masters(self):
and ".background" not in layer.name
):
self.bracket_layers.append(layer)
elif self.minimal and layer.layerId not in master_layer_ids:
continue
else:
ufo_layer = self.to_ufo_layer(glyph, layer)
ufo_glyph = ufo_layer.newGlyph(glyph.name)
self.to_ufo_glyph(ufo_glyph, layer, layer.parent)

for master_id, source in self._sources.items():
ufo = source.font
master = self.font.masters[master_id]
if self.propagate_anchors:
self.to_ufo_propagate_font_anchors(ufo)
for layer in list(ufo.layers):
self.to_ufo_layer_lib(master, ufo, layer)

# to_ufo_custom_params may apply "Replace Features" or "Replace Prefix"
# parameters so it requires UFOs have their features set first; at the
# same time, to generate a GDEF table we first need to have defined the
# glyphOrder, exported the glyphs and propagated anchors from components.
self.to_ufo_master_features(ufo, master)
self.to_ufo_custom_params(ufo, master)

if self.write_skipexportglyphs:
# Sanitize skip list and write it to both Designspace- and UFO-level lib
# keys. The latter is unnecessary when using e.g. the ufo2ft.compile*FromDS`
# functions, but the data may take a different path. Writing it everywhere
# can save on surprises/logic in other software.
skip_export_glyphs = self._designspace.lib.get("public.skipExportGlyphs")
if skip_export_glyphs is not None:
skip_export_glyphs = sorted(set(skip_export_glyphs))
self._designspace.lib["public.skipExportGlyphs"] = skip_export_glyphs
for source in self._sources.values():
source.font.lib["public.skipExportGlyphs"] = skip_export_glyphs

self.to_ufo_groups()
self.to_ufo_kerning()

for source in self._sources.values():
yield source.font

@property
def designspace(self):
"""Get a designspace Document instance that links the masters together
Expand Down
9 changes: 5 additions & 4 deletions Lib/glyphsLib/builder/glyph.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,11 @@ def to_ufo_glyph(self, ufo_glyph, layer, glyph): # noqa: C901
else:
ufo_glyph.width = width

self.to_ufo_background_image(ufo_glyph, layer)
self.to_ufo_guidelines(ufo_glyph, layer)
self.to_ufo_glyph_background(ufo_glyph, layer)
self.to_ufo_annotations(ufo_glyph, layer)
if not self.minimal:
self.to_ufo_background_image(ufo_glyph, layer)
self.to_ufo_guidelines(ufo_glyph, layer)
self.to_ufo_glyph_background(ufo_glyph, layer)
self.to_ufo_annotations(ufo_glyph, layer)
self.to_ufo_hints(ufo_glyph, layer)
self.to_ufo_glyph_user_data(ufo_font, glyph)
self.to_ufo_layer_user_data(ufo_glyph, layer)
Expand Down
1 change: 1 addition & 0 deletions Lib/glyphsLib/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ def glyphs2ufo(options):
store_editor_state=not options.no_store_editor_state,
write_skipexportglyphs=options.write_public_skip_export_glyphs,
ufo_module=__import__(options.ufo_module),
minimal=False,
)


Expand Down
6 changes: 3 additions & 3 deletions tests/builder/builder_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -885,7 +885,7 @@ def _run_guideline_test(self, data_in, expected):
guide.angle = guide_data["angle"]
layer.guides.append(guide)
glyph.layers.append(layer)
ufo = self.to_ufos(font)[0]
ufo = self.to_ufos(font, minimal=False)[0]
self.assertEqual([dict(g) for g in ufo["a"].guidelines], expected)

def test_set_guidelines(self):
Expand Down Expand Up @@ -918,7 +918,7 @@ def test_supplementary_layers(self):
sublayer.width = 0
sublayer.name = "SubLayer"
glyph.layers.append(sublayer)
ufo = self.to_ufos(font)[0]
ufo = self.to_ufos(font, minimal=False)[0]
self.assertEqual([l.name for l in ufo.layers], ["public.default", "SubLayer"])

def test_duplicate_supplementary_layers(self):
Expand All @@ -941,7 +941,7 @@ def test_duplicate_supplementary_layers(self):
sublayer.name = "SubLayer"
glyph.layers.append(sublayer)
with CapturingLogHandler(builder.logger, level="WARNING") as captor:
ufo = self.to_ufos(font)[0]
ufo = self.to_ufos(font, minimal=False)[0]

self.assertEqual(
[l.name for l in ufo.layers], ["public.default", "SubLayer", "SubLayer #1"]
Expand Down
4 changes: 2 additions & 2 deletions tests/builder/components_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

def test_background_component_decompose(datadir):
font = glyphsLib.GSFont(str(datadir.join("Recursion.glyphs")))
ds = to_designspace(font)
ds = to_designspace(font, minimal=False)

for source in ds.sources:
for layer in source.font.layers:
Expand Down Expand Up @@ -48,4 +48,4 @@ def test_background_component_decompose_missing(datadir):
layer.components.append(GSComponent("xxx"))

with pytest.raises(MissingComponentError):
to_designspace(font)
to_designspace(font, minimal=False)
2 changes: 1 addition & 1 deletion tests/builder/designspace_gen_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ def test_designspace_generation_bracket_no_export_glyph(datadir, ufo_module):
font.glyphs["E"].export = False

designspace = to_designspace(
font, write_skipexportglyphs=True, ufo_module=ufo_module
font, write_skipexportglyphs=True, ufo_module=ufo_module, minimal=False
)

assert "E" in designspace.lib.get("public.skipExportGlyphs")
Expand Down
4 changes: 2 additions & 2 deletions tests/builder/lib_and_user_data_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ def test_layer_lib_into_master_user_data(ufo_module):
"layerLibKey2": "ufo2 layerLibValue2"
}

(ufo1, ufo2) = to_ufos(font)
(ufo1, ufo2) = to_ufos(font, minimal=False)

assert ufo1.layers["public.default"].lib["layerLibKey1"] == "ufo1 layerLibValue1"
assert "layerLibKey1" not in ufo1.layers["sketches"].lib
Expand Down Expand Up @@ -321,7 +321,7 @@ def test_glif_lib_equivalent_to_layer_user_data(ufo_module):
assert "glifLibKeyB" not in default_layer.userData.keys()
assert middleground.userData["glifLibKeyB"] == "glifLibValueB"

(ufo,) = to_ufos(font)
(ufo,) = to_ufos(font, minimal=False)

assert ufo["a"].lib["glifLibKeyA"] == "glifLibValueA"
assert "glifLibKeyA" not in ufo.layers["middleground"]["a"]
Expand Down
4 changes: 2 additions & 2 deletions tests/builder/to_glyphs_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def test_guidelines(ufo_module):
assert horizontal.position.y == 20
assert horizontal.angle == 0

(ufo,) = to_ufos(font)
(ufo,) = to_ufos(font, minimal=False)

for obj in [ufo, ufo["a"]]:
angled, vertical, horizontal = obj.guidelines
Expand Down Expand Up @@ -470,7 +470,7 @@ def test_dont_copy_advance_to_the_background_unless_it_was_there(tmpdir, ufo_mod
saved_font = classes.GSFont(path)

for font in [font, saved_font]:
(ufo,) = to_ufos(font)
(ufo,) = to_ufos(font, minimal=False)

assert ufo["a"].width == 100
assert ufo.layers["public.background"]["a"].width == 0
Expand Down