def test_clamp_to_almost_2_component_transform(self): componentName = "a" glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.addComponent(componentName, (1.99999, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 2, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 2, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, 2, 0, 0)) pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0)) compositeGlyph = pen.glyph() almost2 = MAX_F2DOT14 # 0b1.11111111111111 pen.addComponent(componentName, (almost2, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, almost2, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, almost2, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, almost2, 0, 0)) pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0)) expectedGlyph = pen.glyph() assert expectedGlyph == compositeGlyph
def test_build_var(tmpdir): outPath = os.path.join(str(tmpdir), "test_var.ttf") fb, advanceWidths, nameStrings = _setupFontBuilder(True) pen = TTGlyphPen(None) pen.moveTo((100, 0)) pen.lineTo((100, 400)) pen.lineTo((500, 400)) pen.lineTo((500, 000)) pen.closePath() glyph = pen.glyph() pen = TTGlyphPen(None) emptyGlyph = pen.glyph() glyphs = {".notdef": emptyGlyph, "A": glyph, "a": glyph, ".null": emptyGlyph} fb.setupGlyf(glyphs) metrics = {} glyphTable = fb.font["glyf"] for gn, advanceWidth in advanceWidths.items(): metrics[gn] = (advanceWidth, glyphTable[gn].xMin) fb.setupHorizontalMetrics(metrics) fb.setupHorizontalHeader(ascent=824, descent=200) fb.setupNameTable(nameStrings) axes = [ ('LEFT', 0, 0, 100, "Left"), ('RGHT', 0, 0, 100, "Right"), ('UPPP', 0, 0, 100, "Up"), ('DOWN', 0, 0, 100, "Down"), ] instances = [ dict(location=dict(LEFT=0, RGHT=0, UPPP=0, DOWN=0), stylename="TotallyNormal"), dict(location=dict(LEFT=0, RGHT=100, UPPP=100, DOWN=0), stylename="Right Up"), ] fb.setupFvar(axes, instances) variations = {} # Four (x, y) pairs and four phantom points: leftDeltas = [(-200, 0), (-200, 0), (0, 0), (0, 0), None, None, None, None] rightDeltas = [(0, 0), (0, 0), (200, 0), (200, 0), None, None, None, None] upDeltas = [(0, 0), (0, 200), (0, 200), (0, 0), None, None, None, None] downDeltas = [(0, -200), (0, 0), (0, 0), (0, -200), None, None, None, None] variations['a'] = [ TupleVariation(dict(RGHT=(0, 1, 1)), rightDeltas), TupleVariation(dict(LEFT=(0, 1, 1)), leftDeltas), TupleVariation(dict(UPPP=(0, 1, 1)), upDeltas), TupleVariation(dict(DOWN=(0, 1, 1)), downDeltas), ] fb.setupGvar(variations) fb.setupOS2() fb.setupPost() fb.setupDummyDSIG() fb.save(outPath) _verifyOutput(outPath)
def test_clamp_to_almost_2_component_transform(self): componentName = 'a' glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.addComponent(componentName, (1.99999, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 2, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 2, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, 2, 0, 0)) pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0)) compositeGlyph = pen.glyph() almost2 = MAX_F2DOT14 # 0b1.11111111111111 pen.addComponent(componentName, (almost2, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, almost2, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, almost2, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, almost2, 0, 0)) pen.addComponent(componentName, (-2, 0, 0, -2, 0, 0)) expectedGlyph = pen.glyph() self.assertEqual(expectedGlyph, compositeGlyph)
def test_remove_extra_move_points(self): pen = TTGlyphPen(None) pen.moveTo((0, 0)) pen.lineTo((100, 0)) pen.qCurveTo((100, 50), (50, 100), (0, 0)) pen.closePath() self.assertEqual(len(pen.points), 4) self.assertEqual(pen.points[0], (0, 0))
def test_remove_extra_move_points(self): pen = TTGlyphPen(None) pen.moveTo((0, 0)) pen.lineTo((100, 0)) pen.qCurveTo((100, 50), (50, 100), (0, 0)) pen.closePath() assert len(pen.points) == 4 assert pen.points[0] == (0, 0)
def test_keep_move_point(self): pen = TTGlyphPen(None) pen.moveTo((0, 0)) pen.lineTo((100, 0)) pen.qCurveTo((100, 50), (50, 100), (30, 30)) # when last and move pts are different, closePath() implies a lineTo pen.closePath() self.assertEqual(len(pen.points), 5) self.assertEqual(pen.points[0], (0, 0))
def test_keep_duplicate_end_point(self): pen = TTGlyphPen(None) pen.moveTo((0, 0)) pen.lineTo((100, 0)) pen.qCurveTo((100, 50), (50, 100), (0, 0)) pen.lineTo((0, 0)) # the duplicate point is not removed pen.closePath() self.assertEqual(len(pen.points), 5) self.assertEqual(pen.points[0], (0, 0))
def test_keep_duplicate_end_point(self): pen = TTGlyphPen(None) pen.moveTo((0, 0)) pen.lineTo((100, 0)) pen.qCurveTo((100, 50), (50, 100), (0, 0)) pen.lineTo((0, 0)) # the duplicate point is not removed pen.closePath() assert len(pen.points) == 5 assert pen.points[0] == (0, 0)
def test_keep_move_point(self): pen = TTGlyphPen(None) pen.moveTo((0, 0)) pen.lineTo((100, 0)) pen.qCurveTo((100, 50), (50, 100), (30, 30)) # when last and move pts are different, closePath() implies a lineTo pen.closePath() assert len(pen.points) == 5 assert pen.points[0] == (0, 0)
def test_glyph_decomposes(self): componentName = 'a' glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() pen.addComponent(componentName, (1, 0, 0, 1, 2, 0)) compositeGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() pen.moveTo((2, 0)) pen.lineTo((2, 1)) pen.lineTo((3, 0)) pen.closePath() plainGlyph = pen.glyph() plainGlyph.program = compositeGlyph.program self.assertEqual(plainGlyph, compositeGlyph)
def test_glyph_decomposes(self): componentName = 'a' glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() pen.addComponent(componentName, (1, 0, 0, 1, 2, 0)) compositeGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() pen.moveTo((2, 0)) pen.lineTo((2, 1)) pen.lineTo((3, 0)) pen.closePath() plainGlyph = pen.glyph() self.assertEqual(plainGlyph, compositeGlyph)
def test_scaled_component_bounds(self): glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((-231, 939)) pen.lineTo((-55, 939)) pen.lineTo((-55, 745)) pen.lineTo((-231, 745)) pen.closePath() glyphSet["gravecomb"] = pen.glyph() pen = TTGlyphPen(glyphSet) pen.moveTo((-278, 939)) pen.lineTo((8, 939)) pen.lineTo((8, 745)) pen.lineTo((-278, 745)) pen.closePath() glyphSet["circumflexcomb"] = pen.glyph() pen = TTGlyphPen(glyphSet) pen.addComponent("circumflexcomb", (1, 0, 0, 1, 0, 0)) pen.addComponent("gravecomb", (0.9, 0, 0, 0.9, 198, 180)) glyphSet["uni0302_uni0300"] = uni0302_uni0300 = pen.glyph() uni0302_uni0300.recalcBounds(glyphSet) self.assertGlyphBoundsEqual(uni0302_uni0300, (-278, 745, 148, 1025))
def test_glyph_decomposes(self): componentName = "a" glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() pen.addComponent(componentName, (1, 0, 0, 1, 2, 0)) pen.addComponent("missing", (1, 0, 0, 1, 0, 0)) # skipped compositeGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() pen.moveTo((2, 0)) pen.lineTo((2, 1)) pen.lineTo((3, 0)) pen.closePath() plainGlyph = pen.glyph() assert plainGlyph == compositeGlyph
def test_endPath_sameAsClosePath(self): pen = TTGlyphPen(None) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() closePathGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.endPath() endPathGlyph = pen.glyph() self.assertEqual(closePathGlyph, endPathGlyph)
def test_endPath_sameAsClosePath(self): pen = TTGlyphPen(None) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() closePathGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.endPath() endPathGlyph = pen.glyph() assert closePathGlyph == endPathGlyph
def test_endPath_sameAsClosePath(self): pen = TTGlyphPen(None) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() closePathGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.endPath() endPathGlyph = pen.glyph() endPathGlyph.program = closePathGlyph.program self.assertEqual(closePathGlyph, endPathGlyph)
def makeCollectionHmtxTransform2(): fonts = [getTTFont(sfntTTFSourcePath), getTTFont(sfntTTFSourcePath)] for i, font in enumerate(fonts): glyf = font["glyf"] hmtx = font["hmtx"] maxp = font["maxp"] for name in glyf.glyphs: glyph = glyf.glyphs[name] glyph.expand(glyf) if hasattr(glyph, "xMin"): metrics = hmtx.metrics[name] if i == 0: # Move the glyph so that xMin is 0 pen = TTGlyphPen(None) glyph.draw(pen, glyf, -glyph.xMin) glyph = pen.glyph() glyph.recalcBounds(glyf) assert glyph.xMin == 0 glyf.glyphs[name] = glyph hmtx.metrics[name] = (metrics[0], 0) # Build a unique glyph for each font, but with the same advance and LSB name = "box" pen = TTGlyphPen(None) pen.moveTo([0, 0]) pen.lineTo([0, 1000]) if i > 0: pen.lineTo([0, 2000]) pen.lineTo([1000, 2000]) pen.lineTo([1000, 1000]) pen.lineTo([1000, 0]) pen.closePath() glyph = pen.glyph() glyph.recalcBounds(glyf) glyf.glyphs[name] = glyph hmtx.metrics[name] = (glyph.xMax, glyph.xMin) glyf.glyphOrder.append(name) maxp.recalc(font) data = hmtx.compile(font) hmtx.decompile(data, font) data = getSFNTCollectionData(fonts, shared=["hmtx"]) return data
def test_no_handle_overflowing_transform(self): componentName = "a" glyphSet = {} pen = TTGlyphPen(glyphSet, handleOverflowingTransforms=False) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() baseGlyph = pen.glyph() glyphSet[componentName] = _TestGlyph(baseGlyph) pen.addComponent(componentName, (3, 0, 0, 1, 0, 0)) compositeGlyph = pen.glyph() assert compositeGlyph.components[0].transform == ((3, 0), (0, 1)) with pytest.raises(struct.error): compositeGlyph.compile({"a": baseGlyph})
def test_within_range_component_transform(self): componentName = "a" glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0)) compositeGlyph = pen.glyph() pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0)) expectedGlyph = pen.glyph() assert expectedGlyph == compositeGlyph
def test_no_handle_overflowing_transform(self): componentName = 'a' glyphSet = {} pen = TTGlyphPen(glyphSet, handleOverflowingTransforms=False) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() baseGlyph = pen.glyph() glyphSet[componentName] = _TestGlyph(baseGlyph) pen.addComponent(componentName, (3, 0, 0, 1, 0, 0)) compositeGlyph = pen.glyph() self.assertEqual(compositeGlyph.components[0].transform, ((3, 0), (0, 1))) with self.assertRaises(struct.error): compositeGlyph.compile({'a': baseGlyph})
def test_within_range_component_transform(self): componentName = 'a' glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0)) compositeGlyph = pen.glyph() pen.addComponent(componentName, (1.5, 0, 0, 1, 0, 0)) pen.addComponent(componentName, (1, 0, 0, -1.5, 0, 0)) expectedGlyph = pen.glyph() self.assertEqual(expectedGlyph, compositeGlyph)
def test_round_float_coordinates_and_component_offsets(self): glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((367.6, 0)) pen.closePath() simpleGlyph = pen.glyph() simpleGlyph.recalcBounds(glyphSet) self.assertGlyphBoundsEqual(simpleGlyph, (0, 0, 368, 1)) componentName = "a" glyphSet[componentName] = simpleGlyph pen.addComponent(componentName, (1, 0, 0, 1, -86.4, 0)) compositeGlyph = pen.glyph() compositeGlyph.recalcBounds(glyphSet) self.assertGlyphBoundsEqual(compositeGlyph, (-86, 0, 282, 1))
def empty_svg_font(): glyph_order = [".notdef"] + list(ascii_letters) pen = TTGlyphPen(glyphSet=None) pen.moveTo((0, 0)) pen.lineTo((0, 500)) pen.lineTo((500, 500)) pen.lineTo((500, 0)) pen.closePath() glyph = pen.glyph() glyphs = {g: glyph for g in glyph_order} fb = FontBuilder(unitsPerEm=1024, isTTF=True) fb.setupGlyphOrder(glyph_order) fb.setupCharacterMap({ord(c): c for c in ascii_letters}) fb.setupGlyf(glyphs) fb.setupHorizontalMetrics({g: (500, 0) for g in glyph_order}) fb.setupHorizontalHeader() fb.setupOS2() fb.setupPost() fb.setupNameTable({"familyName": "TestSVG", "styleName": "Regular"}) svg_table = newTable("SVG ") svg_table.docList = [] fb.font["SVG "] = svg_table return fb.font
def draw(self, ppp: float): pen = TTGlyphPen(None) for y, row in enumerate(reversed(self.bits)): for x, col in enumerate(row): if col: pen.moveTo( ((x + self.bbx.x) * ppp, (y + self.bbx.y) * ppp)) pen.lineTo( ((x + self.bbx.x + 1) * ppp, (y + self.bbx.y) * ppp)) pen.lineTo(( (x + self.bbx.x + 1) * ppp, (y + self.bbx.y + 1) * ppp, )) pen.lineTo( ((x + self.bbx.x) * ppp, (y + self.bbx.y + 1) * ppp)) pen.lineTo( ((x + self.bbx.x) * ppp, (y + self.bbx.y) * ppp)) pen.closePath() return pen.glyph()
def makeGlyfBBox1(calcBBoxes=True, composite=False): font = getTTFont(sfntTTFSourcePath, recalcBBoxes=calcBBoxes) glyf = font["glyf"] hmtx = font["hmtx"] for name in ("bbox1", "bbox2"): pen = TTGlyphPen(None) if name == "bbox1": pen.moveTo((0, 0)) pen.lineTo((0, 1000)) pen.lineTo((1000, 1000)) pen.lineTo((1000, 0)) pen.closePath() else: pen.moveTo((0, 0)) pen.qCurveTo((500, 750), (600, 500), (500, 250), (0, 0)) pen.closePath() glyph = pen.glyph() if not calcBBoxes: glyph.recalcBounds(glyf) glyph.xMax -= 100 glyf.glyphs[name] = glyph hmtx.metrics[name] = (0, 0) glyf.glyphOrder.append(name) if composite: name = "bbox3" pen = TTGlyphPen(glyf.glyphOrder) pen.addComponent("bbox1", [1, 0, 0, 1, 0, 0]) pen.addComponent("bbox2", [1, 0, 0, 1, 1000, 0]) glyph = pen.glyph() glyph.recalcBounds(glyf) glyf.glyphs[name] = glyph hmtx.metrics[name] = (0, 0) glyf.glyphOrder.append(name) tableData = getSFNTData(font)[0] font.close() del font header, directory, tableData = defaultSFNTTestData(tableData=tableData, flavor="TTF") data = packSFNT(header, directory, tableData, flavor="TTF") return data
def test_build_var(tmpdir): outPath = os.path.join(str(tmpdir), "test_var.ttf") fb = FontBuilder(1024, isTTF=True) fb.setupGlyphOrder([".notdef", ".null", "A", "a"]) fb.setupCharacterMap({65: "A", 97: "a"}) advanceWidths = {".notdef": 600, "A": 600, "a": 600, ".null": 600} familyName = "HelloTestFont" styleName = "TotallyNormal" nameStrings = dict(familyName=dict(en="HelloTestFont", nl="HalloTestFont"), styleName=dict(en="TotallyNormal", nl="TotaalNormaal")) nameStrings['psName'] = familyName + "-" + styleName pen = TTGlyphPen(None) pen.moveTo((100, 0)) pen.lineTo((100, 400)) pen.lineTo((500, 400)) pen.lineTo((500, 000)) pen.closePath() glyph = pen.glyph() pen = TTGlyphPen(None) emptyGlyph = pen.glyph() glyphs = { ".notdef": emptyGlyph, "A": glyph, "a": glyph, ".null": emptyGlyph } fb.setupGlyf(glyphs) metrics = {} glyphTable = fb.font["glyf"] for gn, advanceWidth in advanceWidths.items(): metrics[gn] = (advanceWidth, glyphTable[gn].xMin) fb.setupHorizontalMetrics(metrics) fb.setupHorizontalHeader(ascent=824, descent=200) fb.setupNameTable(nameStrings) axes = [ ('LEFT', 0, 0, 100, "Left"), ('RGHT', 0, 0, 100, "Right"), ('UPPP', 0, 0, 100, "Up"), ('DOWN', 0, 0, 100, "Down"), ] instances = [ dict(location=dict(LEFT=0, RGHT=0, UPPP=0, DOWN=0), stylename="TotallyNormal"), dict(location=dict(LEFT=0, RGHT=100, UPPP=100, DOWN=0), stylename="Right Up"), ] fb.setupFvar(axes, instances) variations = {} # Four (x, y) pairs and four phantom points: leftDeltas = [(-200, 0), (-200, 0), (0, 0), (0, 0), None, None, None, None] rightDeltas = [(0, 0), (0, 0), (200, 0), (200, 0), None, None, None, None] upDeltas = [(0, 0), (0, 200), (0, 200), (0, 0), None, None, None, None] downDeltas = [(0, -200), (0, 0), (0, 0), (0, -200), None, None, None, None] variations['a'] = [ TupleVariation(dict(RGHT=(0, 1, 1)), rightDeltas), TupleVariation(dict(LEFT=(0, 1, 1)), leftDeltas), TupleVariation(dict(UPPP=(0, 1, 1)), upDeltas), TupleVariation(dict(DOWN=(0, 1, 1)), downDeltas), ] fb.setupGvar(variations) fb.setupOS2() fb.setupPost() fb.setupDummyDSIG() fb.save(outPath) _verifyOutput(outPath)
def test_out_of_range_transform_decomposed(self): componentName = 'a' glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.addComponent(componentName, (3, 0, 0, 2, 0, 0)) pen.addComponent(componentName, (1, 0, 0, 1, -1, 2)) pen.addComponent(componentName, (2, 0, 0, -3, 0, 0)) compositeGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 2)) pen.lineTo((3, 0)) pen.closePath() pen.moveTo((-1, 2)) pen.lineTo((-1, 3)) pen.lineTo((0, 2)) pen.closePath() pen.moveTo((0, 0)) pen.lineTo((0, -3)) pen.lineTo((2, 0)) pen.closePath() expectedGlyph = pen.glyph() self.assertEqual(expectedGlyph, compositeGlyph)
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, ])
def test_build_var(tmpdir): outPath = os.path.join(str(tmpdir), "test_var.ttf") fb = FontBuilder(1024, isTTF=True) fb.setupGlyphOrder([".notdef", ".null", "A", "a"]) fb.setupCharacterMap({65: "A", 97: "a"}) advanceWidths = {".notdef": 600, "A": 600, "a": 600, ".null": 600} familyName = "HelloTestFont" styleName = "TotallyNormal" nameStrings = dict(familyName=dict(en="HelloTestFont", nl="HalloTestFont"), styleName=dict(en="TotallyNormal", nl="TotaalNormaal")) nameStrings['psName'] = familyName + "-" + styleName pen = TTGlyphPen(None) pen.moveTo((100, 0)) pen.lineTo((100, 400)) pen.lineTo((500, 400)) pen.lineTo((500, 000)) pen.closePath() glyph = pen.glyph() pen = TTGlyphPen(None) emptyGlyph = pen.glyph() glyphs = {".notdef": emptyGlyph, "A": glyph, "a": glyph, ".null": emptyGlyph} fb.setupGlyf(glyphs) metrics = {} glyphTable = fb.font["glyf"] for gn, advanceWidth in advanceWidths.items(): metrics[gn] = (advanceWidth, glyphTable[gn].xMin) fb.setupHorizontalMetrics(metrics) fb.setupHorizontalHeader(ascent=824, descent=200) fb.setupNameTable(nameStrings) axes = [ ('LEFT', 0, 0, 100, "Left"), ('RGHT', 0, 0, 100, "Right"), ('UPPP', 0, 0, 100, "Up"), ('DOWN', 0, 0, 100, "Down"), ] instances = [ dict(location=dict(LEFT=0, RGHT=0, UPPP=0, DOWN=0), stylename="TotallyNormal"), dict(location=dict(LEFT=0, RGHT=100, UPPP=100, DOWN=0), stylename="Right Up"), ] fb.setupFvar(axes, instances) variations = {} # Four (x, y) pairs and four phantom points: leftDeltas = [(-200, 0), (-200, 0), (0, 0), (0, 0), None, None, None, None] rightDeltas = [(0, 0), (0, 0), (200, 0), (200, 0), None, None, None, None] upDeltas = [(0, 0), (0, 200), (0, 200), (0, 0), None, None, None, None] downDeltas = [(0, -200), (0, 0), (0, 0), (0, -200), None, None, None, None] variations['a'] = [ TupleVariation(dict(RGHT=(0, 1, 1)), rightDeltas), TupleVariation(dict(LEFT=(0, 1, 1)), leftDeltas), TupleVariation(dict(UPPP=(0, 1, 1)), upDeltas), TupleVariation(dict(DOWN=(0, 1, 1)), downDeltas), ] fb.setupGvar(variations) fb.setupOS2() fb.setupPost() fb.setupDummyDSIG() fb.save(outPath) f = TTFont(outPath) f.saveXML(outPath + ".ttx") with open(outPath + ".ttx") as f: testData = strip_VariableItems(f.read()) refData = strip_VariableItems(getTestData("test_var.ttf.ttx")) assert refData == testData
def test_build_var(tmpdir): outPath = os.path.join(str(tmpdir), "test_var.ttf") fb, advanceWidths, nameStrings = _setupFontBuilder(True) pen = TTGlyphPen(None) pen.moveTo((100, 0)) pen.lineTo((100, 400)) pen.lineTo((500, 400)) pen.lineTo((500, 000)) pen.closePath() glyph1 = pen.glyph() pen = TTGlyphPen(None) pen.moveTo((50, 0)) pen.lineTo((50, 200)) pen.lineTo((250, 200)) pen.lineTo((250, 0)) pen.closePath() glyph2 = pen.glyph() pen = TTGlyphPen(None) emptyGlyph = pen.glyph() glyphs = { ".notdef": emptyGlyph, "A": glyph1, "a": glyph2, ".null": emptyGlyph } fb.setupGlyf(glyphs) metrics = {} glyphTable = fb.font["glyf"] for gn, advanceWidth in advanceWidths.items(): metrics[gn] = (advanceWidth, glyphTable[gn].xMin) fb.setupHorizontalMetrics(metrics) fb.setupHorizontalHeader(ascent=824, descent=200) fb.setupNameTable(nameStrings) axes = [ ('LEFT', 0, 0, 100, "Left"), ('RGHT', 0, 0, 100, "Right"), ('UPPP', 0, 0, 100, "Up"), ('DOWN', 0, 0, 100, "Down"), ] instances = [ dict(location=dict(LEFT=0, RGHT=0, UPPP=0, DOWN=0), stylename="TotallyNormal"), dict(location=dict(LEFT=0, RGHT=100, UPPP=100, DOWN=0), stylename="Right Up"), ] fb.setupFvar(axes, instances) variations = {} # Four (x, y) pairs and four phantom points: leftDeltas = [(-200, 0), (-200, 0), (0, 0), (0, 0), None, None, None, None] rightDeltas = [(0, 0), (0, 0), (200, 0), (200, 0), None, None, None, None] upDeltas = [(0, 0), (0, 200), (0, 200), (0, 0), None, None, None, None] downDeltas = [(0, -200), (0, 0), (0, 0), (0, -200), None, None, None, None] variations['a'] = [ TupleVariation(dict(RGHT=(0, 1, 1)), rightDeltas), TupleVariation(dict(LEFT=(0, 1, 1)), leftDeltas), TupleVariation(dict(UPPP=(0, 1, 1)), upDeltas), TupleVariation(dict(DOWN=(0, 1, 1)), downDeltas), ] fb.setupGvar(variations) fb.addFeatureVariations( [([ { "LEFT": (0.8, 1), "DOWN": (0.8, 1) }, { "RGHT": (0.8, 1), "UPPP": (0.8, 1) }, ], { "A": "a" })], featureTag="rclt", ) statAxes = [] for tag, minVal, defaultVal, maxVal, name in axes: values = [ dict(name="Neutral", value=defaultVal, flags=0x2), dict(name=name, value=maxVal) ] statAxes.append(dict(tag=tag, name=name, values=values)) fb.setupStat(statAxes) fb.setupOS2() fb.setupPost() fb.setupDummyDSIG() fb.save(outPath) _verifyOutput(outPath)
def colrv1_path(tmp_path): base_glyph_names = ["uni%04X" % i for i in range(0xE000, 0xE000 + 10)] layer_glyph_names = ["glyph%05d" % i for i in range(10, 20)] glyph_order = [".notdef"] + base_glyph_names + layer_glyph_names pen = TTGlyphPen(glyphSet=None) pen.moveTo((0, 0)) pen.lineTo((0, 500)) pen.lineTo((500, 500)) pen.lineTo((500, 0)) pen.closePath() glyph = pen.glyph() glyphs = {g: glyph for g in glyph_order} fb = FontBuilder(unitsPerEm=1024, isTTF=True) fb.setupGlyphOrder(glyph_order) fb.setupCharacterMap( {int(name[3:], 16): name for name in base_glyph_names}) fb.setupGlyf(glyphs) fb.setupHorizontalMetrics({g: (500, 0) for g in glyph_order}) fb.setupHorizontalHeader() fb.setupOS2() fb.setupPost() fb.setupNameTable({"familyName": "TestCOLRv1", "styleName": "Regular"}) fb.setupCOLR( { "uniE000": ( ot.PaintFormat.PaintColrLayers, [ { "Format": ot.PaintFormat.PaintGlyph, "Paint": (ot.PaintFormat.PaintSolid, 0), "Glyph": "glyph00010", }, { "Format": ot.PaintFormat.PaintGlyph, "Paint": (ot.PaintFormat.PaintSolid, (2, 0.3)), "Glyph": "glyph00011", }, ], ), "uniE001": ( ot.PaintFormat.PaintColrLayers, [ { "Format": ot.PaintFormat.PaintTransform, "Paint": { "Format": ot.PaintFormat.PaintGlyph, "Paint": { "Format": ot.PaintFormat.PaintRadialGradient, "x0": 250, "y0": 250, "r0": 250, "x1": 200, "y1": 200, "r1": 0, "ColorLine": { "ColorStop": [(0.0, 1), (1.0, 2)], "Extend": "repeat", }, }, "Glyph": "glyph00012", }, "Transform": (0.7071, 0.7071, -0.7071, 0.7071, 0, 0), }, { "Format": ot.PaintFormat.PaintGlyph, "Paint": (ot.PaintFormat.PaintSolid, (1, 0.5)), "Glyph": "glyph00013", }, ], ), "uniE002": ( ot.PaintFormat.PaintColrLayers, [ { "Format": ot.PaintFormat.PaintGlyph, "Paint": { "Format": ot.PaintFormat.PaintLinearGradient, "x0": 0, "y0": 0, "x1": 500, "y1": 500, "x2": -500, "y2": 500, "ColorLine": { "ColorStop": [(0.0, 1), (1.0, 2)] }, }, "Glyph": "glyph00014", }, { "Format": ot.PaintFormat.PaintTransform, "Paint": { "Format": ot.PaintFormat.PaintGlyph, "Paint": (ot.PaintFormat.PaintSolid, 1), "Glyph": "glyph00015", }, "Transform": (1, 0, 0, 1, 400, 400), }, ], ), "uniE003": { "Format": ot.PaintFormat.PaintRotate, "Paint": { "Format": ot.PaintFormat.PaintColrGlyph, "Glyph": "uniE001", }, "angle": 45, "centerX": 250, "centerY": 250, }, "uniE004": [ ("glyph00016", 1), ("glyph00017", 2), ], }, ) fb.setupCPAL( [ [ (1.0, 0.0, 0.0, 1.0), # red (0.0, 1.0, 0.0, 1.0), # green (0.0, 0.0, 1.0, 1.0), # blue ], ], ) output_path = tmp_path / "TestCOLRv1.ttf" fb.save(output_path) return output_path
def test_out_of_range_transform_decomposed(self): componentName = "a" glyphSet = {} pen = TTGlyphPen(glyphSet) pen.moveTo((0, 0)) pen.lineTo((0, 1)) pen.lineTo((1, 0)) pen.closePath() glyphSet[componentName] = _TestGlyph(pen.glyph()) pen.addComponent(componentName, (3, 0, 0, 2, 0, 0)) pen.addComponent(componentName, (1, 0, 0, 1, -1, 2)) pen.addComponent(componentName, (2, 0, 0, -3, 0, 0)) compositeGlyph = pen.glyph() pen.moveTo((0, 0)) pen.lineTo((0, 2)) pen.lineTo((3, 0)) pen.closePath() pen.moveTo((-1, 2)) pen.lineTo((-1, 3)) pen.lineTo((0, 2)) pen.closePath() pen.moveTo((0, 0)) pen.lineTo((0, -3)) pen.lineTo((2, 0)) pen.closePath() expectedGlyph = pen.glyph() assert expectedGlyph == compositeGlyph