Skip to content

Commit

Permalink
Merge branch 'main' into pyup-scheduled-update-2024-02-26
Browse files Browse the repository at this point in the history
  • Loading branch information
khaledhosny committed Mar 10, 2024
2 parents 78550be + 974fa0c commit 0f9b40d
Show file tree
Hide file tree
Showing 11 changed files with 551 additions and 43 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ jobs:
# so that all artifacts are downloaded in the same directory specified by 'path'
merge-multiple: true
path: dist
- uses: pypa/gh-action-pypi-publish@v1.8.11
- uses: pypa/gh-action-pypi-publish@v1.8.12
with:
user: __token__
password: ${{ secrets.PYPI_PASSWORD }}
12 changes: 6 additions & 6 deletions Lib/fontTools/cu2qu/ufo.py
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ def fonts_to_quadratic(
compatibility. If this is not required, calling fonts_to_quadratic with one
font at a time may yield slightly more optimized results.
Return True if fonts were modified, else return False.
Return the set of modified glyph names if any, else return an empty set.
By default, cu2qu stores the curve type in the fonts' lib, under a private
key "com.github.googlei18n.cu2qu.curve_type", and will not try to convert
Expand Down Expand Up @@ -296,7 +296,7 @@ def fonts_to_quadratic(
elif max_err_em:
max_errors = [f.info.unitsPerEm * max_err_em for f in fonts]

modified = False
modified = set()
glyph_errors = {}
for name in set().union(*(f.keys() for f in fonts)):
glyphs = []
Expand All @@ -306,9 +306,10 @@ def fonts_to_quadratic(
glyphs.append(font[name])
cur_max_errors.append(error)
try:
modified |= _glyphs_to_quadratic(
if _glyphs_to_quadratic(
glyphs, cur_max_errors, reverse_direction, stats, all_quadratic
)
):
modified.add(name)
except IncompatibleGlyphsError as exc:
logger.error(exc)
glyph_errors[name] = exc
Expand All @@ -329,7 +330,6 @@ def fonts_to_quadratic(
new_curve_type = "quadratic" if all_quadratic else "mixed"
if curve_type != new_curve_type:
font.lib[CURVE_TYPE_LIB_KEY] = new_curve_type
modified = True
return modified


Expand All @@ -343,7 +343,7 @@ def glyph_to_quadratic(glyph, **kwargs):

def font_to_quadratic(font, **kwargs):
"""Convenience wrapper around fonts_to_quadratic, for just one font.
Return True if the font was modified, else return False.
Return the set of modified glyph names if any, else return empty set.
"""

return fonts_to_quadratic([font], **kwargs)
50 changes: 42 additions & 8 deletions Lib/fontTools/pens/basePen.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
from typing import Tuple, Dict

from fontTools.misc.loggingTools import LogMixin
from fontTools.misc.transform import DecomposedTransform
from fontTools.misc.transform import DecomposedTransform, Identity

__all__ = [
"AbstractPen",
Expand Down Expand Up @@ -195,17 +195,40 @@ class DecomposingPen(LoggingPen):
By default a warning message is logged when a base glyph is missing;
set the class variable ``skipMissingComponents`` to False if you want
to raise a :class:`MissingComponentError` exception.
all instances of a sub-class to raise a :class:`MissingComponentError`
exception by default.
"""

skipMissingComponents = True
# alias error for convenience
MissingComponentError = MissingComponentError

def __init__(self, glyphSet):
"""Takes a single 'glyphSet' argument (dict), in which the glyphs
that are referenced as components are looked up by their name.
def __init__(
self,
glyphSet,
*args,
skipMissingComponents=None,
reverseFlipped=False,
**kwargs,
):
"""Takes a 'glyphSet' argument (dict), in which the glyphs that are referenced
as components are looked up by their name.
If the optional 'reverseFlipped' argument is True, components whose transformation
matrix has a negative determinant will be decomposed with a reversed path direction
to compensate for the flip.
The optional 'skipMissingComponents' argument can be set to True/False to
override the homonymous class attribute for a given pen instance.
"""
super(DecomposingPen, self).__init__()
super(DecomposingPen, self).__init__(*args, **kwargs)
self.glyphSet = glyphSet
self.skipMissingComponents = (
self.__class__.skipMissingComponents
if skipMissingComponents is None
else skipMissingComponents
)
self.reverseFlipped = reverseFlipped

def addComponent(self, glyphName, transformation):
"""Transform the points of the base glyph and draw it onto self."""
Expand All @@ -218,8 +241,19 @@ def addComponent(self, glyphName, transformation):
raise MissingComponentError(glyphName)
self.log.warning("glyph '%s' is missing from glyphSet; skipped" % glyphName)
else:
tPen = TransformPen(self, transformation)
glyph.draw(tPen)
pen = self
if transformation != Identity:
pen = TransformPen(pen, transformation)
if self.reverseFlipped:
# if the transformation has a negative determinant, it will
# reverse the contour direction of the component
a, b, c, d = transformation[:4]
det = a * d - b * c
if det < 0:
from fontTools.pens.reverseContourPen import ReverseContourPen

pen = ReverseContourPen(pen)
glyph.draw(pen)

def addVarComponent(self, glyphName, transformation, location):
# GlyphSet decomposes for us
Expand Down
86 changes: 82 additions & 4 deletions Lib/fontTools/pens/filterPen.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from fontTools.pens.basePen import AbstractPen
from fontTools.pens.pointPen import AbstractPointPen
from __future__ import annotations

from fontTools.pens.basePen import AbstractPen, DecomposingPen
from fontTools.pens.pointPen import AbstractPointPen, DecomposingPointPen
from fontTools.pens.recordingPen import RecordingPen


Expand Down Expand Up @@ -150,8 +152,8 @@ class FilterPointPen(_PassThruComponentsMixin, AbstractPointPen):
('endPath', (), {})
"""

def __init__(self, outPointPen):
self._outPen = outPointPen
def __init__(self, outPen):
self._outPen = outPen

def beginPath(self, **kwargs):
self._outPen.beginPath(**kwargs)
Expand All @@ -161,3 +163,79 @@ def endPath(self):

def addPoint(self, pt, segmentType=None, smooth=False, name=None, **kwargs):
self._outPen.addPoint(pt, segmentType, smooth, name, **kwargs)


class _DecomposingFilterPenMixin:
"""Mixin class that decomposes components as regular contours.
Shared by both DecomposingFilterPen and DecomposingFilterPointPen.
Takes two required parameters, another (segment or point) pen 'outPen' to draw
with, and a 'glyphSet' dict of drawable glyph objects to draw components from.
The 'skipMissingComponents' and 'reverseFlipped' optional arguments work the
same as in the DecomposingPen/DecomposingPointPen. Both are False by default.
In addition, the decomposing filter pens also take the following two options:
'include' is an optional set of component base glyph names to consider for
decomposition; the default include=None means decompose all components no matter
the base glyph name).
'decomposeNested' (bool) controls whether to recurse decomposition into nested
components of components (this only matters when 'include' was also provided);
if False, only decompose top-level components included in the set, but not
also their children.
"""

# raises MissingComponentError if base glyph is not found in glyphSet
skipMissingComponents = False

def __init__(
self,
outPen,
glyphSet,
skipMissingComponents=None,
reverseFlipped=False,
include: set[str] | None = None,
decomposeNested: bool = True,
):
super().__init__(
outPen=outPen,
glyphSet=glyphSet,
skipMissingComponents=skipMissingComponents,
reverseFlipped=reverseFlipped,
)
self.include = include
self.decomposeNested = decomposeNested

def addComponent(self, baseGlyphName, transformation, **kwargs):
# only decompose the component if it's included in the set
if self.include is None or baseGlyphName in self.include:
# if we're decomposing nested components, temporarily set include to None
include_bak = self.include
if self.decomposeNested and self.include:
self.include = None
try:
super().addComponent(baseGlyphName, transformation, **kwargs)
finally:
if self.include != include_bak:
self.include = include_bak
else:
_PassThruComponentsMixin.addComponent(
self, baseGlyphName, transformation, **kwargs
)


class DecomposingFilterPen(_DecomposingFilterPenMixin, DecomposingPen, FilterPen):
"""Filter pen that draws components as regular contours."""

pass


class DecomposingFilterPointPen(
_DecomposingFilterPenMixin, DecomposingPointPen, FilterPointPen
):
"""Filter point pen that draws components as regular contours."""

pass
79 changes: 77 additions & 2 deletions Lib/fontTools/pens/pointPen.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
import math
from typing import Any, Optional, Tuple, Dict

from fontTools.pens.basePen import AbstractPen, PenError
from fontTools.misc.transform import DecomposedTransform
from fontTools.misc.loggingTools import LogMixin
from fontTools.pens.basePen import AbstractPen, MissingComponentError, PenError
from fontTools.misc.transform import DecomposedTransform, Identity

__all__ = [
"AbstractPointPen",
Expand Down Expand Up @@ -523,3 +524,77 @@ def addComponent(self, glyphName, transform, identifier=None, **kwargs):
if self.currentContour is not None:
raise PenError("Components must be added before or after contours")
self.pen.addComponent(glyphName, transform, identifier=identifier, **kwargs)


class DecomposingPointPen(LogMixin, AbstractPointPen):
"""Implements a 'addComponent' method that decomposes components
(i.e. draws them onto self as simple contours).
It can also be used as a mixin class (e.g. see DecomposingRecordingPointPen).
You must override beginPath, addPoint, endPath. You may
additionally override addVarComponent and addComponent.
By default a warning message is logged when a base glyph is missing;
set the class variable ``skipMissingComponents`` to False if you want
all instances of a sub-class to raise a :class:`MissingComponentError`
exception by default.
"""

skipMissingComponents = True
# alias error for convenience
MissingComponentError = MissingComponentError

def __init__(
self,
glyphSet,
*args,
skipMissingComponents=None,
reverseFlipped=False,
**kwargs,
):
"""Takes a 'glyphSet' argument (dict), in which the glyphs that are referenced
as components are looked up by their name.
If the optional 'reverseFlipped' argument is True, components whose transformation
matrix has a negative determinant will be decomposed with a reversed path direction
to compensate for the flip.
The optional 'skipMissingComponents' argument can be set to True/False to
override the homonymous class attribute for a given pen instance.
"""
super().__init__(*args, **kwargs)
self.glyphSet = glyphSet
self.skipMissingComponents = (
self.__class__.skipMissingComponents
if skipMissingComponents is None
else skipMissingComponents
)
self.reverseFlipped = reverseFlipped

def addComponent(self, baseGlyphName, transformation, identifier=None, **kwargs):
"""Transform the points of the base glyph and draw it onto self.
The `identifier` parameter and any extra kwargs are ignored.
"""
from fontTools.pens.transformPen import TransformPointPen

try:
glyph = self.glyphSet[baseGlyphName]
except KeyError:
if not self.skipMissingComponents:
raise MissingComponentError(baseGlyphName)
self.log.warning(
"glyph '%s' is missing from glyphSet; skipped" % baseGlyphName
)
else:
pen = self
if transformation != Identity:
pen = TransformPointPen(pen, transformation)
if self.reverseFlipped:
# if the transformation has a negative determinant, it will
# reverse the contour direction of the component
a, b, c, d = transformation[:4]
det = a * d - b * c
if a * d - b * c < 0:
pen = ReverseContourPointPen(pen)
glyph.drawPoints(pen)
Loading

0 comments on commit 0f9b40d

Please sign in to comment.