Skip to content

Commit

Permalink
Very rough WIP G3 support
Browse files Browse the repository at this point in the history
- Convert data to G3 format on load. convert back when writing G2
- Some adjustments to be able to run the builder from within Glyphs 3
- compare the result with what Glyphs would write and adjust (e.g. com.schriftgestalt keys in the libs) TODO: restore support for the old lib keys
  • Loading branch information
schriftgestalt committed Jun 29, 2023
1 parent 7c87b6c commit c75966e
Show file tree
Hide file tree
Showing 29 changed files with 1,848 additions and 1,237 deletions.
293 changes: 98 additions & 195 deletions Lib/glyphsLib/builder/axes.py

Large diffs are not rendered by default.

70 changes: 42 additions & 28 deletions Lib/glyphsLib/builder/bracket_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,38 @@
from fontTools.varLib.featureVars import overlayFeatureVariations

from glyphsLib import util
from glyphsLib.classes import LAYER_ATTRIBUTE_AXIS_RULES
from glyphsLib.util import designspace_min_max
from .constants import (
BRACKET_GLYPH_TEMPLATE,
GLYPHLIB_PREFIX,
)

def _bracket_info(layer, axes):
# Returns a region expressed as a {axis_tag: (min, max)} box
# (dictionary), once the axes have been computed
if not layer.isBracketLayer():
return {}

info = {}
for axis in axes:
rule = layer.attributes[LAYER_ATTRIBUTE_AXIS_RULES].get(axis.axisId)
if "min" not in rule and "max" not in rule:
continue
# Rules are expressed in designspace coordinates,
# so map appropriately.
designspace_min, designspace_max = designspace_min_max(axis)
axis_min = rule.get("min", designspace_min)
axis_max = rule.get("max", designspace_max)
if isinstance(axis_min, str):
axis_min = float(axis_min)
if isinstance(axis_max, str):
axis_max = float(axis_max)
if axis_max == axis.minimum and axis_max == axis.maximum:
# It's full range, ignore it.
continue
info[axis.tag] = (axis_min, axis_max)
return info

def to_designspace_bracket_layers(self):
"""Extract bracket layers in a GSGlyph into free-standing UFO glyphs with
Expand All @@ -30,7 +57,7 @@ def to_designspace_bracket_layers(self):
rules = []
tag_to_name = {axis.tag: axis.name for axis in self._designspace.axes}
for layer in self.bracket_layers:
box_with_tag = layer._bracket_info(self._designspace.axes)
box_with_tag = _bracket_info(layer, self._designspace.axes)
glyph_name = layer.parent.name
# It'd be nice to use tag for both glyph names and designspace
# rules, but given designspace refers to axes by name, our hand
Expand Down Expand Up @@ -201,13 +228,13 @@ def find_component_use(self):
for master, layers in master_layers.items():
for glyph_name, layer in layers.items():
my_bracket_layers = [
layer._bracket_info(self._designspace.axes)
_bracket_info(layer, self._designspace.axes)
for layer in alternate_layers[master][glyph_name]
]
for comp in layer.components:
# Check our alternate layer set-up agrees with theirs
components_bracket_layers = [
layer._bracket_info(self._designspace.axes)
_bracket_info(layer, self._designspace.axes)
for layer in alternate_layers[master][comp.name]
]
if my_bracket_layers != components_bracket_layers:
Expand All @@ -230,7 +257,7 @@ def find_component_use(self):
# And now, fix the problem.
for (glyph_name, master), needed_brackets in problematic_glyphs.items():
my_bracket_layers = [
layer._bracket_info(self._designspace.axes)
_bracket_info(layer, self._designspace.axes)
for layer in alternate_layers[master][glyph_name]
]
if my_bracket_layers:
Expand Down Expand Up @@ -259,28 +286,15 @@ def synthesize_bracket_layer(old_layer, box, axes):
new_layer.layerId = ""
new_layer.associatedMasterId = old_layer.layerId

if new_layer.parent.parent.formatVersion == 2:
axis, (bottom, top) = next(iter(box.items()))
designspace_min, designspace_max = util.designspace_min_max(axes[0])
if designspace_min == bottom:
new_layer.name = old_layer.name + f" ]{top}]"
else:
new_layer.name = old_layer.name + f"[{bottom}]"
else:
new_layer.attributes = dict(
new_layer.attributes
) # But we do need our own version of this
new_layer.attributes["axisRules"] = []
for axis in axes:
if axis.tag in box:
new_layer.attributes["axisRules"].append(
{
"min": box[axis.tag][0],
"max": box[axis.tag][1],
}
)
else:
new_layer.attributes["axisRules"].append({})

assert new_layer._bracket_info(axes) == box
new_layer.attributes = dict(
new_layer.attributes
) # But we do need our own version of this
new_layer.attributes[LAYER_ATTRIBUTE_AXIS_RULES] = {}
for axis in axes:
if axis.tag in box:
new_layer.attributes[LAYER_ATTRIBUTE_AXIS_RULES][axis.axisId] = {
"min": box[axis.tag][0],
"max": box[axis.tag][1],
}
assert _bracket_info(new_layer, axes) == box
return new_layer
26 changes: 16 additions & 10 deletions Lib/glyphsLib/builder/builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@
BRACKET_GLYPH_RE,
FONT_CUSTOM_PARAM_PREFIX,
)
from .axes import WEIGHT_AXIS_DEF, WIDTH_AXIS_DEF, find_base_style, class_to_value
from .axes import find_base_style, class_to_value
from glyphsLib.util import LoggerMixin

from glyphsLib.classes import GSAxis
from .sources import _to_glyphs_source

class UFOBuilder(LoggerMixin):
"""Builder for Glyphs to UFO + designspace."""
Expand Down Expand Up @@ -292,7 +293,7 @@ def to_ufo_layers(self):
)
continue

if not layer.name and not layer._is_bracket_layer():
if not layer.name and not layer.isBracketLayer():
# Empty layer names are invalid according to the UFO spec.
if self.minimize_glyphs_diffs:
self.logger.warning(
Expand All @@ -306,15 +307,15 @@ def to_ufo_layers(self):
# set to non-export (in which case makes no sense to have Designspace rules
# referencing non existent glyphs).
if (
layer._is_bracket_layer()
layer.isBracketLayer()
and glyph.export
and ".background" not in layer.name
):
self.bracket_layers.append(layer)
elif (
self.minimal
and layer.layerId not in master_layer_ids
and not layer._is_brace_layer()
and not layer.isBraceLayer()
):
continue
else:
Expand Down Expand Up @@ -501,6 +502,8 @@ def font(self):
# considers them to be special layers and will handle them itself.
self._font = self.glyphs_module.GSFont()

self.to_glyphs_axes()

if "com.schriftgestaltung.formatVersion" in self.designspace.lib:
self._font.formatVersion = self.designspace.lib["com.schriftgestaltung.formatVersion"]
if "com.schriftgestaltung.appVersion" in self.designspace.lib:
Expand All @@ -517,11 +520,14 @@ def font(self):
for glyph_name in source.font.lib[GLYPH_ORDER_KEY]
if not BRACKET_GLYPH_RE.match(glyph_name)
]

if source.copyInfo:
self._font.familyName = source.familyName
self.to_glyphs_font_attributes(source, master, is_initial=(index == 0))
self.to_glyphs_master_attributes(source, master)
self._font.masters.insert(len(self._font.masters), master)
self._sources[master.id] = source
_to_glyphs_source(self, master)
master.post_read()

# First, move free-standing bracket glyphs back to layers to avoid dealing
# with GSLayer transplantation.
Expand Down Expand Up @@ -655,16 +661,16 @@ def _fake_designspace(self, ufos):
# Make weight and width axis if relevant
for info_key, axis_def in zip(
("openTypeOS2WeightClass", "openTypeOS2WidthClass"),
(WEIGHT_AXIS_DEF, WIDTH_AXIS_DEF),
(GSAxis("Weight", "wght"), GSAxis("Width", "wdth")),
):
axis = designspace.newAxisDescriptor()
axis.tag = axis_def.tag
axis.tag = axis_def.axisTag
axis.name = axis_def.name
mapping = []
for ufo in ufos:
user_loc = getattr(ufo.info, info_key)
if user_loc is not None:
design_loc = class_to_value(axis_def.tag, user_loc)
design_loc = class_to_value(axis_def.axisTag, user_loc)
mapping.append((user_loc, design_loc))
ufo_to_location[id(ufo)][axis_def.name] = design_loc

Expand Down Expand Up @@ -728,7 +734,7 @@ def _fake_designspace(self, ufos):
from .masters import to_glyphs_master_attributes
from .names import to_glyphs_family_names, to_glyphs_master_names
from .paths import to_glyphs_paths
from .sources import to_glyphs_sources
from .sources import _to_glyphs_source
from .user_data import (
to_glyphs_family_user_data_from_designspace,
to_glyphs_family_user_data_from_ufo,
Expand Down
5 changes: 4 additions & 1 deletion Lib/glyphsLib/builder/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@
from glyphsLib.types import parse_datetime

UFO_FORMAT = "%Y/%m/%d %H:%M:%S"

GLYPHS_FORMAT = "%Y-%m-%d %H:%M:%S +0000"

def to_ufo_time(datetime_obj):
"""Format a datetime object as specified for UFOs."""
return datetime_obj.strftime(UFO_FORMAT)

def to_glyphs_time(datetime_obj):
"""Format a datetime object as specified for UFOs."""
return datetime_obj.strftime(GLYPHS_FORMAT)

def from_ufo_time(string):
"""Parses a datetime as specified for UFOs into a datetime object."""
Expand Down
4 changes: 2 additions & 2 deletions Lib/glyphsLib/builder/components.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ def to_ufo_components(self, ufo_glyph, layer):
pen = ufo_glyph.getPointPen()
for index, component in enumerate(layer.components):
component_name = component.name
if layer._is_color_palette_layer():
if layer.isColorPaletteLayer():
# Glyphs handles components for color layers in a special way. If
# the component glyph has color layers of its own, the component
# use the first color layer with the same color index, otherwise it
# fallback to the default layer. We try to do that here as well.
font = layer.parent.parent
component_glyph = font.glyphs[component_name]
color_layers = [
l for l in component_glyph.layers if l._is_color_palette_layer()
l for l in component_glyph.layers if l.isColorPaletteLayer()
]
for i, l in enumerate(color_layers):
if l._color_palette_index() == layer._color_palette_index():
Expand Down
31 changes: 17 additions & 14 deletions Lib/glyphsLib/builder/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@
APP_VERSION_LIB_KEY = GLYPHS_PREFIX + "appVersion"
FORMATVERSION_LIB_KEY = GLYPHS_PREFIX + "formatVersion"
KEYBOARD_INCREMENT_KEY = GLYPHS_PREFIX + "keyboardIncrement"
KEYBOARD_INCREMENT_BIG_KEY = GLYPHS_PREFIX + "keyboardIncrementBig"
KEYBOARD_INCREMENT_HUGE_KEY = GLYPHS_PREFIX + "keyboardIncrementHuge"
GRID_SIZE_KEY = GLYPHS_PREFIX + "gridSize"
GRID_SUBDIVISION_KEY = GLYPHS_PREFIX + "gridSubDivision"
MASTER_ORDER_LIB_KEY = GLYPHS_PREFIX + "fontMasterOrder"

SCRIPT_LIB_KEY = GLYPHLIB_PREFIX + "script"
Expand All @@ -67,14 +71,13 @@
SMART_COMPONENT_AXES_LIB_KEY = GLYPHS_PREFIX + "smartComponentAxes"

EXPORT_KEY = GLYPHS_PREFIX + "export"
WIDTH_KEY = GLYPHS_PREFIX + "width"
WEIGHT_KEY = GLYPHS_PREFIX + "weight"
WIDTH_KEY = GLYPHS_PREFIX + "widthClass"
WEIGHT_KEY = GLYPHS_PREFIX + "weightClass"
FULL_FILENAME_KEY = GLYPHLIB_PREFIX + "fullFilename"
MANUAL_INTERPOLATION_KEY = GLYPHS_PREFIX + "manualInterpolation"
# Following typo kept for backwards compatibility
INSTANCE_INTERPOLATIONS_KEY = GLYPHS_PREFIX + "intanceInterpolations"

CUSTOM_PARAMETERS_KEY = GLYPHS_PREFIX + "customParameters"
CUSTOM_PARAMETERS_BLACKLIST = [
# These are stored in the official descriptor attributes.
"familyName",
Expand Down Expand Up @@ -133,24 +136,24 @@
UFO_NOTE_KEY = GLYPHLIB_PREFIX + "ufoNote"

UFO_DATA_KEY = GLYPHLIB_PREFIX + "ufoData"
FONT_USER_DATA_KEY = GLYPHLIB_PREFIX + "fontUserData"
FONT_USER_DATA_KEY = GLYPHLIB_PREFIX + "font.userData"
LAYER_LIB_KEY = GLYPHLIB_PREFIX + "layerLib"
LAYER_NAME_KEY = GLYPHLIB_PREFIX + "layerName"
GLYPH_USER_DATA_KEY = GLYPHLIB_PREFIX + "glyphUserData"
NODE_USER_DATA_KEY = GLYPHLIB_PREFIX + "nodeUserData"


GLYPHS_COLORS = (
"0.85,0.26,0.06,1",
"0.99,0.62,0.11,1",
"0.65,0.48,0.2,1",
"0.97,1,0,1",
"0.67,0.95,0.38,1",
"0.04,0.57,0.04,1",
"0,0.67,0.91,1",
"0.18,0.16,0.78,1",
"0.5,0.09,0.79,1",
"0.98,0.36,0.67,1",
"0.85,0.26,0.06,1", # red
"0.99,0.62,0.11,1", # orange
"0.65,0.48,0.2,1", # brown
"0.97,0.9,0.0,1", # yellow
"0.67,0.95,0.38,1", # green
"0.04,0.57,0.04,1", # dark green
"0.06,0.6,0.98,1", # cyan
"0,0.2,0.88,1", # blue
"0.5,0.09,0.79,1", # violet
"0.98,0.36,0.67,1", # pink
"0.75,0.75,0.75,1",
"0.25,0.25,0.25,1",
)
Expand Down
Loading

0 comments on commit c75966e

Please sign in to comment.