def transformationAtCenter(matrix, centerPoint): """Helper function for rotate(), scale() and skew() to apply a transformation with a specified center point. >>> transformationAtCenter((2, 0, 0, 2, 0, 0), (0, 0)) (2, 0, 0, 2, 0, 0) >>> transformationAtCenter((2, 0, 0, 2, 0, 0), (100, 200)) (2, 0, 0, 2, -100, -200) >>> transformationAtCenter((-2, 0, 0, 2, 0, 0), (100, 200)) (-2, 0, 0, 2, 300, -200) >>> t = Transform(*transformationAtCenter((0, 1, 1, 0, 0, 0), (100, 200))) >>> t.transformPoint((100, 200)) (100, 200) >>> t.transformPoint((0, 0)) (-100, 100) """ if centerPoint == (0, 0): return matrix t = Transform() cx, cy = centerPoint t = t.translate(cx, cy) t = t.transform(matrix) t = t.translate(-cx, -cy) return tuple(t)
def test_reverseTransform(self): t = Transform(2, 0, 0, 3, 1, 6) reverse_t = t.reverseTransform((4, 3, 2, 1, 5, 6)) assert reverse_t == Transform(8, 6, 6, 3, 21, 15) t = Transform(4, 3, 2, 1, 5, 6) reverse_t = t.transform((2, 0, 0, 3, 1, 6)) assert reverse_t == Transform(8, 6, 6, 3, 21, 15)
def transform(self, transform, center=(0, 0)): cx, cy = center t = Transform() t = t.translate(cx, cy) t = t.transform(transform) t = t.translate(-cx, -cy) matrix = skia.Matrix() matrix.setAffine(t) self.path.transform(matrix)
def _flattenComponent(glyphSet, component): """Returns a list of tuples (baseGlyph, transform) of nested component.""" glyph = glyphSet[component.baseGlyph] # Any contour will cause components to be decomposed if not glyph.components or len(glyph) > 0: transformation = Transform(*component.transformation) return [(component.baseGlyph, transformation)] all_flattened_components = [] for nested in glyph.components: flattened_components = _flattenComponent(glyphSet, nested) for i, (name, tr) in enumerate(flattened_components): flat_tr = Transform(*component.transformation) flat_tr = flat_tr.translate(tr.dx, tr.dy) flat_tr = flat_tr.transform((tr.xx, tr.xy, tr.yx, tr.yy, 0, 0)) flattened_components[i] = (name, flat_tr) all_flattened_components.extend(flattened_components) return all_flattened_components
def propagate_anchors(ufo): """Copy anchors from parent glyphs' components to the parent.""" from fontTools.misc.transform import Transform def get_anchor(glyph, name): return next((a for a in glyph.anchors if a.name == name), None) for parent in ufo: added_here = {} # don't propagate anchors for mark glyphs if any(a.name.startswith('_') for a in parent.anchors): continue # try to get anchors from base (first) components glyph = parent transformation = Transform() while glyph.components: component = glyph.components[0] glyph = ufo[component.baseGlyph] transformation = transformation.transform(component.transformation) for anchor in glyph.anchors: if get_anchor(parent, anchor.name) is None: added_here[anchor.name] = transformation.transformPoint( (anchor.x, anchor.y)) # adjust anchors to which a mark has been attached for component in parent.components[1:]: glyph = ufo[component.baseGlyph] transformation = Transform(*component.transformation) for anchor in glyph.anchors: if (anchor.name in added_here and get_anchor(glyph, '_' + anchor.name) is not None): added_here[anchor.name] = transformation.transformPoint( (anchor.x, anchor.y)) for name, (x, y) in added_here.items(): anchor_dict = {'name': name, 'x': x, 'y': y} parent.appendAnchor(glyph.anchorClass(anchorDict=anchor_dict))
def parsePositions(baseGlyph, markGlyph, font, markTransformMap, advanceWidth, advanceHeight): xx, xy, yx, yy, x, y = 1, 0, 0, 1, advanceWidth, advanceHeight baseGlyphX = baseGlyphY = baseGlyph markFixedX = markFixedY = False flipX = flipY = False if positionSplit in markGlyph: markGlyph, position = markGlyph.split(positionSplit) if positionXYSplit in position: positions = position.split(positionXYSplit) if len(positions) == 6: xx, xy, yx, yy, positionX, positionY = positions xx = float(xx) xy = float(xy) yx = float(yx) yy = float(yy) elif len(positions) == 2: positionX, positionY = positions else: raise GlyphBuilderError( "mark positions should have 6 or 2 options") else: positionX = positionY = position if positionBaseSplit in positionX: baseGlyphX, positionX = positionX.split(positionBaseSplit) if positionBaseSplit in positionY: baseGlyphY, positionY = positionY.split(positionBaseSplit) if flipMarkGlyphSplit in positionX: flipX = True positionX = positionX.replace(flipMarkGlyphSplit, "") if flipMarkGlyphSplit in positionY: flipY = True positionY = positionY.replace(flipMarkGlyphSplit, "") if positionX and positionY: baseX = baseY = 0 markX = markY = 0 if markGlyph not in font: if glyphSuffixSplit in markGlyph: markGlyph = markGlyph.split(glyphSuffixSplit)[0] markPoint1, markAngle1, markFixedX = parsePosition(markGlyph, font, positionX, direction="x", prefix="_") markPoint2, markAngle2, markFixedY = parsePosition(markGlyph, font, positionY, direction="y", prefix="_") intersection = _intersectAngles(markPoint1, markAngle1, markPoint2, markAngle2) if intersection is not None: markX, markY = intersection if baseGlyphX in font and baseGlyphY in font: basePoint1, baseAngle1, _ = parsePosition(baseGlyphX, font, positionX, direction="x", isBase=True) basePoint2, baseAngle2, _ = parsePosition(baseGlyphY, font, positionY, direction="y", isBase=True) intersection = _intersectAngles(basePoint1, baseAngle1, basePoint2, baseAngle2) if intersection is not None: baseX, baseY = intersection # calculate the offset if not markFixedX: x += baseX - markX else: x += markX if not markFixedY: y += baseY - markY else: y += markY if not markFixedX: baseTransform = markTransformMap.get(baseGlyphX) if baseTransform: x += baseTransform[4] - advanceWidth if not markFixedY: baseTransform = markTransformMap.get(baseGlyphY) if baseTransform: y += baseTransform[5] - advanceHeight transformMatrix = (xx, xy, yx, yy, x, y) if flipX: bounds = font[markGlyph].bounds if bounds: minx, miny, maxx, maxy = bounds bt = Transform(*transformMatrix) minx, miny = bt.transformPoint((minx, miny)) maxx, maxy = bt.transformPoint((maxx, maxy)) t = Transform() t = t.translate(0, miny) t = t.scale(1, -1) t = t.translate(0, -maxy) t = t.transform(bt) transformMatrix = t[:] if flipY: bounds = font[markGlyph].bounds if bounds: minx, miny, maxx, maxy = bounds bt = Transform(*transformMatrix) minx, miny = bt.transformPoint((minx, miny)) maxx, maxy = bt.transformPoint((maxx, maxy)) t = Transform() t = t.translate(minx, 0) t = t.scale(-1, 1) t = t.translate(-maxx, 0) t = t.transform(bt) transformMatrix = t[:] return markGlyph, transformMatrix
def test_transform(self): t = Transform(2, 0, 0, 3, 1, 6) assert t.transform((4, 3, 2, 1, 5, 6)) == Transform(8, 9, 4, 3, 11, 24)
def parsePositions(baseGlyph, markGlyph, font, markTransformMap, advanceWidth, advanceHeight): xx, xy, yx, yy, x, y = 1, 0, 0, 1, advanceWidth, advanceHeight baseGlyphX = baseGlyphY = baseGlyph markFixedX = markFixedY = False flipX = flipY = False if positionSplit in markGlyph: markGlyph, position = markGlyph.split(positionSplit) if positionXYSplit in position: positions = position.split(positionXYSplit) if len(positions) == 6: xx, xy, yx, yy, positionX, positionY = positions xx = float(xx) xy = float(xy) yx = float(yx) yy = float(yy) elif len(positions) == 2: positionX, positionY = positions else: raise GlyphBuilderError("mark positions should have 6 or 2 options") else: positionX = positionY = position if positionBaseSplit in positionX: baseGlyphX, positionX = positionX.split(positionBaseSplit) if positionBaseSplit in positionY: baseGlyphY, positionY = positionY.split(positionBaseSplit) if flipMarkGlyphSplit in positionX: flipY = True positionX = positionX.replace(flipMarkGlyphSplit, "") if flipMarkGlyphSplit in positionY: flipX = True positionY = positionY.replace(flipMarkGlyphSplit, "") if positionX and positionY: baseX = baseY = 0 markX = markY = 0 if markGlyph not in font: if glyphSuffixSplit in markGlyph: markGlyph = markGlyph.split(glyphSuffixSplit)[0] markPoint1, markAngle1, markFixedX = parsePosition(markGlyph, font, positionX, direction="x", prefix="_") markPoint2, markAngle2, markFixedY = parsePosition(markGlyph, font, positionY, direction="y", prefix="_") intersection = _intersectAngles(markPoint1, markAngle1, markPoint2, markAngle2) if intersection is not None: markX, markY = intersection if baseGlyphX in font and baseGlyphY in font: basePoint1, baseAngle1, _ = parsePosition(baseGlyphX, font, positionX, direction="x", isBase=True) basePoint2, baseAngle2, _ = parsePosition(baseGlyphY, font, positionY, direction="y", isBase=True) intersection = _intersectAngles(basePoint1, baseAngle1, basePoint2, baseAngle2) if intersection is not None: baseX, baseY = intersection # calculate the offset if not markFixedX: x += baseX - markX else: x += markX if not markFixedY: y += baseY - markY else: y += markY if not markFixedX: baseTransform = markTransformMap.get(baseGlyphX) if baseTransform: x += baseTransform[4] - advanceWidth if not markFixedY: baseTransform = markTransformMap.get(baseGlyphY) if baseTransform: y += baseTransform[5] - advanceHeight transformMatrix = (xx, xy, yx, yy, x, y) if flipX: bounds = font[markGlyph].bounds if bounds: minx, miny, maxx, maxy = bounds bt = Transform(*transformMatrix) minx, miny = bt.transformPoint((minx, miny)) maxx, maxy = bt.transformPoint((maxx, maxy)) t = Transform() t = t.translate(0, miny) t = t.scale(1, -1) t = t.translate(0, -maxy) t = t.transform(bt) transformMatrix = t[:] if flipY: bounds = font[markGlyph].bounds if bounds: minx, miny, maxx, maxy = bounds bt = Transform(*transformMatrix) minx, miny = bt.transformPoint((minx, miny)) maxx, maxy = bt.transformPoint((maxx, maxy)) t = Transform() t = t.translate(minx, 0) t = t.scale(-1, 1) t = t.translate(-maxx, 0) t = t.transform(bt) transformMatrix = t[:] return markGlyph, transformMatrix