def addComponent(self, glyphName, transformation): component = GlyphComponent() component.glyphName = glyphName component.transform = (transformation[:2], transformation[2:4]) component.x, component.y = [int(n) for n in transformation[4:]] component.flags = 0 self.components.append(component)
def test_toXML_no_transform(self): comp = GlyphComponent() comp.glyphName = "a" comp.flags = ARGS_ARE_XY_VALUES comp.x, comp.y = 1, 2 assert getXML(comp.toXML) == [ '<component glyphName="a" x="1" y="2" flags="0x2"/>' ]
def test_fromXML_no_transform(self): comp = GlyphComponent() for name, attrs, content in parseXML( ['<component glyphName="a" x="1" y="2" flags="0x2"/>']): comp.fromXML(name, attrs, content, ttFont=None) assert comp.glyphName == "a" assert comp.flags & ARGS_ARE_XY_VALUES != 0 assert (comp.x, comp.y) == (1, 2) assert not hasattr(comp, "transform")
def test_fromXML_reference_points(self): comp = GlyphComponent() for name, attrs, content in parseXML([ '<component glyphName="a" firstPt="1" secondPt="2" flags="0x0"/>' ]): comp.fromXML(name, attrs, content, ttFont=None) assert comp.glyphName == "a" assert comp.flags == 0 assert (comp.firstPt, comp.secondPt) == (1, 2) assert not hasattr(comp, "transform")
def test_fromXML_transform_scale(self): comp = GlyphComponent() for name, attrs, content in parseXML( ['<component glyphName="a" x="1" y="2" scale="0.3" flags="0xa"/>' ]): comp.fromXML(name, attrs, content, ttFont=None) assert comp.glyphName == "a" assert comp.flags & ARGS_ARE_XY_VALUES != 0 assert comp.flags & WE_HAVE_A_SCALE != 0 assert (comp.x, comp.y) == (1, 2) assert hasattr(comp, "transform") for value, expected in zip(itertools.chain(*comp.transform), [0.2999878, 0, 0, 0.2999878]): assert value == pytest.approx(expected)
def test_fromXML_transform_2x2_scale(self): comp = GlyphComponent() for name, attrs, content in parseXML([ '<component glyphName="a" x="1" y="2" scalex="0.6" scale01="-0.2" ' 'scale10="0.2" scaley="0.3" flags="0x82"/>' ]): comp.fromXML(name, attrs, content, ttFont=None) assert comp.glyphName == "a" assert comp.flags & ARGS_ARE_XY_VALUES != 0 assert comp.flags & WE_HAVE_A_TWO_BY_TWO != 0 assert (comp.x, comp.y) == (1, 2) assert hasattr(comp, "transform") for value, expected in zip( itertools.chain(*comp.transform), [0.5999756, -0.2000122, 0.2000122, 0.2999878]): assert value == pytest.approx(expected)
def _buildComponents(self, componentFlags): if self.handleOverflowingTransforms: # we can't encode transform values > 2 or < -2 in F2Dot14, # so we must decompose the glyph if any transform exceeds these overflowing = any(s > 2 or s < -2 for (glyphName, transformation) in self.components for s in transformation[:4]) components = [] for glyphName, transformation in self.components: if glyphName not in self.glyphSet: self.log.warning( "skipped non-existing component '%s'", glyphName ) continue if (self.points or (self.handleOverflowingTransforms and overflowing)): # can't have both coordinates and components, so decompose tpen = TransformPen(self, transformation) self.glyphSet[glyphName].draw(tpen) continue component = GlyphComponent() component.glyphName = glyphName component.x, component.y = transformation[4:] transformation = transformation[:4] if transformation != (1, 0, 0, 1): if (self.handleOverflowingTransforms and any(MAX_F2DOT14 < s <= 2 for s in transformation)): # clamp values ~= +2.0 so we can keep the component transformation = tuple(MAX_F2DOT14 if MAX_F2DOT14 < s <= 2 else s for s in transformation) component.transform = (transformation[:2], transformation[2:]) component.flags = componentFlags components.append(component) return components
def glyph(self, componentFlags=0x4): assert self._isClosed(), "Didn't close last contour." components = [] for glyphName, transformation in self.components: if self.points: # can't have both, so decompose the glyph tpen = TransformPen(self, transformation) self.glyphSet[glyphName].draw(tpen) continue component = GlyphComponent() component.glyphName = glyphName if transformation[:4] != (1, 0, 0, 1): component.transform = (transformation[:2], transformation[2:4]) component.x, component.y = transformation[4:] component.flags = componentFlags components.append(component) glyph = Glyph() glyph.coordinates = GlyphCoordinates(self.points) glyph.endPtsOfContours = self.endPts glyph.flags = array("B", self.types) self.init() if components: glyph.components = components glyph.numberOfContours = -1 else: glyph.numberOfContours = len(glyph.endPtsOfContours) glyph.program = ttProgram.Program() glyph.program.fromBytecode(b"") return glyph
def glyph(self, componentFlags=0x4): assert self._isClosed(), "Didn't close last contour." if self.handleOverflowingTransforms: # we can't encode transform values > 2 or < -2 in F2Dot14, # so we must decompose the glyph if any transform exceeds these overflowing = any(s > 2 or s < -2 for (glyphName, transformation) in self.components for s in transformation[:4]) components = [] for glyphName, transformation in self.components: if (self.points or (self.handleOverflowingTransforms and overflowing)): # can't have both coordinates and components, so decompose try: baseGlyph = self.glyphSet[glyphName] except KeyError: self.log.debug( "can't decompose non-existing component '%s'; skipped", glyphName) continue else: tpen = TransformPen(self, transformation) baseGlyph.draw(tpen) continue component = GlyphComponent() component.glyphName = glyphName component.x, component.y = transformation[4:] transformation = transformation[:4] if transformation != (1, 0, 0, 1): if (self.handleOverflowingTransforms and any(MAX_F2DOT14 < s <= 2 for s in transformation)): # clamp values ~= +2.0 so we can keep the component transformation = tuple( MAX_F2DOT14 if MAX_F2DOT14 < s <= 2 else s for s in transformation) component.transform = (transformation[:2], transformation[2:]) component.flags = componentFlags components.append(component) glyph = Glyph() glyph.coordinates = GlyphCoordinates(self.points) glyph.endPtsOfContours = self.endPts glyph.flags = array("B", self.types) self.init() if components: glyph.components = components glyph.numberOfContours = -1 else: glyph.numberOfContours = len(glyph.endPtsOfContours) glyph.program = ttProgram.Program() glyph.program.fromBytecode(b"") return glyph
def _appendComponent(self, baseGlyph, transformation=None, identifier=None, **kwargs): c = GlyphComponent() c.transformation = transformation c.glyphName = baseGlyph c.x = 0 c.y = 0 c.flags = 0 # XXX glyph = self.naked()._glyph if hasattr(self.naked()._glyph,"components"): glyph.components.append(c) else: glyph.components = [c]
def test_toXML_reference_points(self): comp = GlyphComponent() comp.glyphName = "a" comp.flags = 0 comp.firstPt = 1 comp.secondPt = 2 assert getXML(comp.toXML) == [ '<component glyphName="a" firstPt="1" secondPt="2" flags="0x0"/>' ]
def test_toXML_transform_scale(self): comp = GlyphComponent() comp.glyphName = "a" comp.flags = ARGS_ARE_XY_VALUES | WE_HAVE_A_SCALE comp.x, comp.y = 1, 2 comp.transform = [[0.2999878, 0], [0, 0.2999878]] assert getXML(comp.toXML) == [ '<component glyphName="a" x="1" y="2" scale="0.3" flags="0xa"/>' ]
def test_toXML_transform_2x2_scale(self): comp = GlyphComponent() comp.glyphName = "a" comp.flags = ARGS_ARE_XY_VALUES | WE_HAVE_A_TWO_BY_TWO comp.x, comp.y = 1, 2 comp.transform = [[0.5999756, -0.2000122], [0.2000122, 0.2999878]] assert getXML(comp.toXML) == [ '<component glyphName="a" x="1" y="2" scalex="0.6" scale01="-0.2" ' 'scale10="0.2" scaley="0.3" flags="0x82"/>' ]
def _buildComponents(self, componentFlags): if self.handleOverflowingTransforms: # we can't encode transform values > 2 or < -2 in F2Dot14, # so we must decompose the glyph if any transform exceeds these overflowing = any(s > 2 or s < -2 for (glyphName, transformation) in self.components for s in transformation[:4]) components = [] for glyphName, transformation in self.components: if glyphName not in self.glyphSet: self.log.warning( f"skipped non-existing component '{glyphName}'") continue if self.points or (self.handleOverflowingTransforms and overflowing): # can't have both coordinates and components, so decompose self._decompose(glyphName, transformation) continue component = GlyphComponent() component.glyphName = glyphName component.x, component.y = (otRound(v) for v in transformation[4:]) # quantize floats to F2Dot14 so we get same values as when decompiled # from a binary glyf table transformation = tuple( floatToFixedToFloat(v, 14) for v in transformation[:4]) if transformation != (1, 0, 0, 1): if self.handleOverflowingTransforms and any( MAX_F2DOT14 < s <= 2 for s in transformation): # clamp values ~= +2.0 so we can keep the component transformation = tuple( MAX_F2DOT14 if MAX_F2DOT14 < s <= 2 else s for s in transformation) component.transform = (transformation[:2], transformation[2:]) component.flags = componentFlags components.append(component) return components
def skPathFromGlyphComponent(component: _g_l_y_f.GlyphComponent, glyphSet: _TTGlyphMapping): baseGlyphName, transformation = component.getComponentInfo() path = skPathFromGlyph(baseGlyphName, glyphSet) return path.transform(*transformation)
def test_getCoordinates(self): glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((100, 0)) pen.lineTo((100, 100)) pen.lineTo((0, 100)) pen.closePath() # simple contour glyph glyphSet["a"] = a = pen.glyph() assert a.getCoordinates(glyphSet) == ( GlyphCoordinates([(0, 0), (100, 0), (100, 100), (0, 100)]), [3], array.array("B", [1, 1, 1, 1]), ) # composite glyph with only XY offset pen = TTGlyphPen(glyphSet) pen.addComponent("a", (1, 0, 0, 1, 10, 20)) glyphSet["b"] = b = pen.glyph() assert b.getCoordinates(glyphSet) == ( GlyphCoordinates([(10, 20), (110, 20), (110, 120), (10, 120)]), [3], array.array("B", [1, 1, 1, 1]), ) # composite glyph with a scale (and referencing another composite glyph) pen = TTGlyphPen(glyphSet) pen.addComponent("b", (0.5, 0, 0, 0.5, 0, 0)) glyphSet["c"] = c = pen.glyph() assert c.getCoordinates(glyphSet) == ( GlyphCoordinates([(5, 10), (55, 10), (55, 60), (5, 60)]), [3], array.array("B", [1, 1, 1, 1]), ) # composite glyph with unscaled offset (MS-style) pen = TTGlyphPen(glyphSet) pen.addComponent("a", (0.5, 0, 0, 0.5, 10, 20)) glyphSet["d"] = d = pen.glyph() d.components[0].flags |= UNSCALED_COMPONENT_OFFSET assert d.getCoordinates(glyphSet) == ( GlyphCoordinates([(10, 20), (60, 20), (60, 70), (10, 70)]), [3], array.array("B", [1, 1, 1, 1]), ) # composite glyph with a scaled offset (Apple-style) pen = TTGlyphPen(glyphSet) pen.addComponent("a", (0.5, 0, 0, 0.5, 10, 20)) glyphSet["e"] = e = pen.glyph() e.components[0].flags |= SCALED_COMPONENT_OFFSET assert e.getCoordinates(glyphSet) == ( GlyphCoordinates([(5, 10), (55, 10), (55, 60), (5, 60)]), [3], array.array("B", [1, 1, 1, 1]), ) # composite glyph where the 2nd and 3rd components use anchor points pen = TTGlyphPen(glyphSet) pen.addComponent("a", (1, 0, 0, 1, 0, 0)) glyphSet["f"] = f = pen.glyph() comp1 = GlyphComponent() comp1.glyphName = "a" # aling the new component's pt 0 to pt 2 of contour points added so far comp1.firstPt = 2 comp1.secondPt = 0 comp1.flags = 0 f.components.append(comp1) comp2 = GlyphComponent() comp2.glyphName = "a" # aling the new component's pt 0 to pt 6 of contour points added so far comp2.firstPt = 6 comp2.secondPt = 0 comp2.transform = [[0.707107, 0.707107], [-0.707107, 0.707107]] # rotate 45 deg comp2.flags = WE_HAVE_A_TWO_BY_TWO f.components.append(comp2) coords, end_pts, flags = f.getCoordinates(glyphSet) assert end_pts == [3, 7, 11] assert flags == array.array("B", [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) assert list(sum(coords, ())) == pytest.approx([ 0, 0, 100, 0, 100, 100, 0, 100, 100, 100, 200, 100, 200, 200, 100, 200, 200, 200, 270.7107, 270.7107, 200.0, 341.4214, 129.2893, 270.7107, ])