-
Notifications
You must be signed in to change notification settings - Fork 51
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
475 additions
and
96 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,3 +17,7 @@ htmlcov | |
|
||
# Autosaved files | ||
*~ | ||
|
||
# Files generated by tests | ||
actual* | ||
expected* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,190 @@ | ||
# Copyright 2016 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) | ||
|
||
from collections import defaultdict | ||
import os | ||
import re | ||
|
||
from glyphsLib import classes | ||
from .constants import GLYPHLIB_PREFIX | ||
|
||
UFO_ORIGINAL_KERNING_GROUPS_KEY = GLYPHLIB_PREFIX + 'originalKerningGroups' | ||
UFO_GROUPS_NOT_IN_FEATURE_KEY = GLYPHLIB_PREFIX + 'groupsNotInFeature' | ||
UFO_KERN_GROUP_PATTERN = re.compile('^public\\.kern([12])\\.(.*)$') | ||
|
||
|
||
def to_ufo_groups(self): | ||
# Build groups once and then apply to all UFOs. | ||
groups = defaultdict(list) | ||
|
||
# Classes usually go to the feature file, unless we have our custom flag | ||
group_names = None | ||
if UFO_GROUPS_NOT_IN_FEATURE_KEY in self.font.userData.keys(): | ||
group_names = set(self.font.userData[UFO_GROUPS_NOT_IN_FEATURE_KEY]) | ||
if group_names: | ||
for gsclass in self.font.classes.values(): | ||
if gsclass.name in group_names: | ||
if gsclass.code: | ||
groups[gsclass.name] = gsclass.code.split(' ') | ||
else: | ||
# Empty group: using split like above would produce [''] | ||
groups[gsclass.name] = [] | ||
|
||
# Rebuild kerning groups from `left/rightKerningGroup`s | ||
# Use the original list of kerning groups as a base, to recover | ||
# - the original ordering | ||
# - the kerning groups of glyphs that were not in the font (which can be | ||
# stored in a UFO but not by Glyphs) | ||
recovered = set() | ||
orig_groups = self.font.userData.get(UFO_ORIGINAL_KERNING_GROUPS_KEY) | ||
if orig_groups: | ||
for group, glyphs in orig_groups.items(): | ||
if not glyphs: | ||
# Restore empty group | ||
groups[group] = [] | ||
for glyph_name in glyphs: | ||
# Check that the original value is still valid | ||
match = UFO_KERN_GROUP_PATTERN.match(group) | ||
side = match.group(1) | ||
group_name = match.group(2) | ||
glyph = self.font.glyphs[glyph_name] | ||
if not glyph or getattr( | ||
glyph, _glyph_kerning_attr(glyph, side)) == group_name: | ||
# The original grouping is still valid | ||
groups[group].append(glyph_name) | ||
# Remember not to add this glyph again later | ||
# Thus the original position in the list is preserved | ||
recovered.add((glyph_name, int(side))) | ||
|
||
# Read modified grouping values | ||
for glyph in self.font.glyphs.values(): | ||
for side in 1, 2: | ||
if (glyph.name, side) not in recovered: | ||
attr = _glyph_kerning_attr(glyph, side) | ||
group = getattr(glyph, attr) | ||
if group: | ||
group = 'public.kern%s.%s' % (side, group) | ||
groups[group].append(glyph.name) | ||
|
||
# Update all UFOs with the same info | ||
for ufo in self._ufos.values(): | ||
for name, glyphs in groups.items(): | ||
# Shallow copy to prevent unexpected object sharing | ||
ufo.groups[name] = glyphs[:] | ||
|
||
|
||
def to_glyphs_groups(self): | ||
# Build the GSClasses from the groups of the first UFO. | ||
groups = [] | ||
for ufo in self.ufos: | ||
for name, glyphs in ufo.groups.items(): | ||
if _is_kerning_group(name): | ||
_to_glyphs_kerning_group(self, name, glyphs) | ||
else: | ||
gsclass = classes.GSClass(name, " ".join(glyphs)) | ||
self.font.classes.append(gsclass) | ||
groups.append(name) | ||
if self.minimize_ufo_diffs: | ||
self.font.userData[UFO_GROUPS_NOT_IN_FEATURE_KEY] = groups | ||
break | ||
|
||
# Check that other UFOs are identical and print a warning if not. | ||
for index, ufo in enumerate(self.ufos): | ||
if index == 0: | ||
reference_ufo = ufo | ||
else: | ||
_assert_groups_are_identical(self, reference_ufo, ufo) | ||
|
||
|
||
def _is_kerning_group(name): | ||
return (name.startswith('public.kern1.') or | ||
name.startswith('public.kern2.')) | ||
|
||
|
||
def _to_glyphs_kerning_group(self, name, glyphs): | ||
if self.minimize_ufo_diffs: | ||
# Preserve ordering when going from UFO group | ||
# to left/rightKerningGroup disseminated in GSGlyphs | ||
# back to UFO group. | ||
if not self.font.userData.get(UFO_ORIGINAL_KERNING_GROUPS_KEY): | ||
self.font.userData[UFO_ORIGINAL_KERNING_GROUPS_KEY] = {} | ||
self.font.userData[UFO_ORIGINAL_KERNING_GROUPS_KEY][name] = glyphs | ||
|
||
match = UFO_KERN_GROUP_PATTERN.match(name) | ||
side = match.group(1) | ||
group_name = match.group(2) | ||
for glyph_name in glyphs: | ||
glyph = self.font.glyphs[glyph_name] | ||
if glyph: | ||
setattr(glyph, _glyph_kerning_attr(glyph, side), group_name) | ||
|
||
|
||
def _glyph_kerning_attr(glyph, side): | ||
"""Return leftKerningGroup or rightKerningGroup depending on the UFO | ||
group's side (1 or 2) and the glyph's direction (LTR or RTL). | ||
""" | ||
side = int(side) | ||
if _is_ltr(glyph): | ||
if side == 1: | ||
return 'rightKerningGroup' | ||
else: | ||
return 'leftKerningGroup' | ||
else: | ||
# RTL | ||
if side == 1: | ||
return 'leftKerningGroup' | ||
else: | ||
return 'rightKerningGroup' | ||
|
||
|
||
def _is_ltr(glyph): | ||
# TODO: (jany) have a real implem? | ||
# The following one is just to make my simple test pass | ||
if glyph.name.endswith('-hb'): | ||
return False | ||
return True | ||
|
||
|
||
def _assert_groups_are_identical(self, reference_ufo, ufo): | ||
first_time = [True] # Using a mutable as a non-local for closure below | ||
|
||
def _warn(message, *args): | ||
if first_time: | ||
self.logger.warn('Using UFO `%s` as a reference for groups:', | ||
_ufo_logging_ref(reference_ufo)) | ||
first_time.clear() | ||
self.logger.warn(' ' + message, *args) | ||
|
||
# Check for inconsistencies | ||
for group, glyphs in ufo.groups.items(): | ||
if group not in reference_ufo.groups: | ||
_warn("group `%s` from `%s` will be lost because it's not " | ||
"defined in the reference UFO", group, _ufo_logging_ref(ufo)) | ||
reference_glyphs = reference_ufo.groups[group] | ||
if glyphs != reference_glyphs: | ||
_warn("group `%s` from `%s` will not be stored accurately because " | ||
"it is different from the reference UFO", group, | ||
_ufo_logging_ref(ufo)) | ||
_warn(" reference = %s", ' '.join(glyphs)) | ||
_warn(" current = %s", ' '.join(reference_glyphs)) | ||
|
||
|
||
def _ufo_logging_ref(ufo): | ||
"""Return a string that can identify this UFO in logs.""" | ||
if ufo.path: | ||
return os.path.basename(ufo.path) | ||
return ufo.info.styleName |
Oops, something went wrong.