Skip to content

Commit

Permalink
Merge pull request #235 from belluzj/ufo_roundtrip
Browse files Browse the repository at this point in the history
Split the UFO builder into 1 file per "feature"
  • Loading branch information
belluzj authored Oct 23, 2017
2 parents 08dc1a2 + dcebcf3 commit cbc3d7b
Show file tree
Hide file tree
Showing 26 changed files with 1,501 additions and 1,121 deletions.
5 changes: 2 additions & 3 deletions Lib/glyphsLib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@


def load_to_ufos(file_or_path, include_instances=False, family_name=None,
propagate_anchors=True, debug=False):
propagate_anchors=True):
"""Load an unpacked .glyphs object to UFO objects."""

if hasattr(file_or_path, 'read'):
Expand All @@ -55,8 +55,7 @@ def load_to_ufos(file_or_path, include_instances=False, family_name=None,
logger.info('Loading to UFOs')
return to_ufos(font, include_instances=include_instances,
family_name=family_name,
propagate_anchors=propagate_anchors,
debug=debug)
propagate_anchors=propagate_anchors)


def build_masters(filename, master_dir, designspace_instance_dir=None,
Expand Down
46 changes: 44 additions & 2 deletions Lib/glyphsLib/builder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,48 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from .glyphs import to_glyphs
import logging

from .ufo import to_ufos, logger
from glyphsLib import classes
import defcon

from .builders import UFOBuilder, GlyphsBuilder

logger = logging.getLogger(__name__)


def to_ufos(font, include_instances=False, family_name=None,
propagate_anchors=True, ufo_module=defcon):
"""Take .glyphs file data and load it into UFOs.
Takes in data as Glyphs.app-compatible classes, as documented at
https://docu.glyphsapp.com/
If include_instances is True, also returns the parsed instance data.
If family_name is provided, the master UFOs will be given this name and
only instances with this name will be returned.
"""
builder = UFOBuilder(
font,
ufo_module=ufo_module,
family_name=family_name,
propagate_anchors=propagate_anchors)

result = list(builder.masters)

if include_instances:
return result, builder.instance_data
return result


def to_glyphs(ufos, designspace=None, glyphs_module=classes):
"""
Take a list of UFOs and combine them into a single .glyphs file.
This should be the inverse function of `to_ufos`,
so we should have to_glyphs(to_ufos(font)) == font
"""
builder = GlyphsBuilder(
ufos, designspace=designspace, glyphs_module=glyphs_module)
return builder.font
29 changes: 19 additions & 10 deletions Lib/glyphsLib/anchors.py → Lib/glyphsLib/builder/anchors.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,18 @@

from fontTools.misc.transform import Transform

__all__ = ['propagate_font_anchors']
__all__ = ['to_ufo_propagate_font_anchors']


def propagate_font_anchors(ufo):
def to_ufo_propagate_font_anchors(self, ufo):
"""Copy anchors from parent glyphs' components to the parent."""

processed = set()
for glyph in ufo:
propagate_glyph_anchors(ufo, glyph, processed)
_propagate_glyph_anchors(ufo, glyph, processed)


def propagate_glyph_anchors(ufo, parent, processed):
def _propagate_glyph_anchors(ufo, parent, processed):
"""Propagate anchors for a single parent glyph."""

if parent.name in processed:
Expand All @@ -42,7 +42,7 @@ def propagate_glyph_anchors(ufo, parent, processed):
to_add = {}
for component in parent.components:
glyph = ufo[component.baseGlyph]
propagate_glyph_anchors(ufo, glyph, processed)
_propagate_glyph_anchors(ufo, glyph, processed)
if any(a.name.startswith('_') for a in glyph.anchors):
mark_components.append(component)
else:
Expand All @@ -53,18 +53,18 @@ def propagate_glyph_anchors(ufo, parent, processed):
# don't add if parent already contains this anchor OR any associated
# ligature anchors (e.g. "top_1, top_2" for "top")
if not any(a.name.startswith(anchor_name) for a in parent.anchors):
get_anchor_data(to_add, ufo, base_components, anchor_name)
_get_anchor_data(to_add, ufo, base_components, anchor_name)

for component in mark_components:
adjust_anchors(to_add, ufo, component)
_adjust_anchors(to_add, ufo, component)

# we sort propagated anchors to append in a deterministic order
for name, (x, y) in sorted(to_add.items()):
anchor_dict = {'name': name, 'x': x, 'y': y}
parent.appendAnchor(glyph.anchorClass(anchorDict=anchor_dict))


def get_anchor_data(anchor_data, ufo, components, anchor_name):
def _get_anchor_data(anchor_data, ufo, components, anchor_name):
"""Get data for an anchor from a list of components."""

anchors = []
Expand All @@ -84,7 +84,7 @@ def get_anchor_data(anchor_data, ufo, components, anchor_name):
anchor_data[anchor.name] = t.transformPoint((anchor.x, anchor.y))


def adjust_anchors(anchor_data, ufo, component):
def _adjust_anchors(anchor_data, ufo, component):
"""Adjust anchors to which a mark component may have been attached."""

glyph = ufo[component.baseGlyph]
Expand All @@ -93,5 +93,14 @@ def adjust_anchors(anchor_data, ufo, component):
# only adjust if this anchor has data and the component also contains
# the associated mark anchor (e.g. "_top" for "top")
if (anchor.name in anchor_data and
any(a.name == '_' + anchor.name for a in glyph.anchors)):
any(a.name == '_' + anchor.name for a in glyph.anchors)):
anchor_data[anchor.name] = t.transformPoint((anchor.x, anchor.y))


def to_ufo_glyph_anchors(self, glyph, anchors):
"""Add .glyphs anchors to a glyph."""

for anchor in anchors:
x, y = anchor.position
anchor_dict = {'name': anchor.name, 'x': x, 'y': y}
glyph.appendAnchor(anchor_dict)
36 changes: 36 additions & 0 deletions Lib/glyphsLib/builder/blue_values.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2015 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import (print_function, division, absolute_import,
unicode_literals)


def to_ufo_blue_values(self, ufo, master):
"""Set postscript blue values from Glyphs alignment zones."""

alignment_zones = master.alignmentZones
blue_values = []
other_blues = []
for zone in sorted(alignment_zones):
pos = zone.position
size = zone.size
val_list = blue_values if pos == 0 or size >= 0 else other_blues
val_list.extend(sorted((pos, pos + size)))

ufo.info.postscriptBlueValues = blue_values
ufo.info.postscriptOtherBlues = other_blues


def to_glyphs_blue_values(self, ufo, master):
pass
Loading

0 comments on commit cbc3d7b

Please sign in to comment.