def test_ignore_single_points(self): pen = DummyPen() try: logging.captureWarnings(True) with CapturingLogHandler("py.warnings", level="WARNING") as log: quadpen = Cu2QuPen(pen, MAX_ERR, ignore_single_points=True) finally: logging.captureWarnings(False) quadpen.moveTo((0, 0)) quadpen.endPath() quadpen.moveTo((1, 1)) quadpen.closePath() self.assertGreaterEqual(len(log.records), 1) self.assertIn("ignore_single_points is deprecated", log.records[0].args[0]) # single-point contours were ignored, so the pen commands are empty self.assertFalse(pen.commands) # redraw without ignoring single points quadpen.ignore_single_points = False quadpen.moveTo((0, 0)) quadpen.endPath() quadpen.moveTo((1, 1)) quadpen.closePath() self.assertTrue(pen.commands) self.assertEqual(str(pen).splitlines(), [ "pen.moveTo((0, 0))", "pen.endPath()", "pen.moveTo((1, 1))", "pen.closePath()" ])
def setupTable_glyf(self): """Make the glyf table.""" allGlyphs = self.allGlyphs if self.convertCubics: from cu2qu.pens import Cu2QuPen allGlyphs = {} for name, glyph in self.allGlyphs.items(): if isinstance(glyph, StubGlyph): allGlyphs[name] = glyph continue newGlyph = glyph.__class__() glyph.draw( Cu2QuPen(newGlyph.getPen(), self.cubicConversionError, reverse_direction=True)) # the width is needed for autoUseMyMetrics method below newGlyph.width = glyph.width allGlyphs[name] = newGlyph self.otf["loca"] = newTable("loca") self.otf["glyf"] = glyf = newTable("glyf") glyf.glyphs = {} glyf.glyphOrder = self.glyphOrder for name in self.glyphOrder: glyph = allGlyphs[name] pen = TTGlyphPen(allGlyphs) glyph.draw(pen) ttGlyph = pen.glyph() if ttGlyph.isComposite() and self.autoUseMyMetrics: self.autoUseMyMetrics(ttGlyph, glyph.width, allGlyphs) glyf[name] = ttGlyph
def test_curveTo_no_points(self): quadpen = Cu2QuPen(DummyPen(), MAX_ERR) quadpen.moveTo((0, 0)) with self.assertRaisesRegex( AssertionError, "illegal curve segment point count: 0"): quadpen.curveTo()
def setupTable_glyf(self): """Make the glyf table.""" allGlyphs = self.allGlyphs if self.convertCubics: from cu2qu.pens import Cu2QuPen allGlyphs = {} for name, glyph in self.allGlyphs.items(): if isinstance(glyph, StubGlyph): allGlyphs[name] = glyph continue newGlyph = glyph.__class__() glyph.draw( Cu2QuPen(newGlyph.getPen(), self.cubicConversionError, reverse_direction=True)) allGlyphs[name] = newGlyph self.otf["loca"] = newTable("loca") self.otf["glyf"] = glyf = newTable("glyf") glyf.glyphs = {} glyf.glyphOrder = self.glyphOrder for name in self.glyphOrder: pen = TTGlyphPen(allGlyphs) allGlyphs[name].draw(pen) glyf[name] = pen.glyph()
def test_addComponent(self): pen = DummyPen() quadpen = Cu2QuPen(pen, MAX_ERR) quadpen.addComponent("a", (1, 2, 3, 4, 5.0, 6.0)) # components are passed through without changes self.assertEqual(str(pen).splitlines(), [ "pen.addComponent('a', (1, 2, 3, 4, 5.0, 6.0))", ])
def glyphs_to_quadratic(glyphs, max_err, **kwargs): quadGlyphs = {} for gname in glyphs.keys(): glyph = glyphs[gname] ttPen = TTGlyphPen(glyphs) cu2quPen = Cu2QuPen(ttPen, max_err, **kwargs) glyph.draw(cu2quPen) quadGlyphs[gname] = ttPen.glyph() return quadGlyphs
def test_curveTo_3_points(self): pen = DummyPen() quadpen = Cu2QuPen(pen, MAX_ERR) quadpen.moveTo((0, 0)) quadpen.curveTo((1, 1), (2, 2), (3, 3)) self.assertEqual(str(pen).splitlines(), [ "pen.moveTo((0, 0))", "pen.qCurveTo((0.75, 0.75), (2.25, 2.25), (3, 3))", ])
def test_curveTo_1_point(self): pen = DummyPen() quadpen = Cu2QuPen(pen, MAX_ERR) quadpen.moveTo((0, 0)) quadpen.curveTo((1, 1)) self.assertEqual(str(pen).splitlines(), [ "pen.moveTo((0, 0))", "pen.lineTo((1, 1))", ])
def test_qCurveTo_more_than_1_point(self): pen = DummyPen() quadpen = Cu2QuPen(pen, MAX_ERR) quadpen.moveTo((0, 0)) quadpen.qCurveTo((1, 1), (2, 2)) self.assertEqual(str(pen).splitlines(), [ "pen.moveTo((0, 0))", "pen.qCurveTo((1, 1), (2, 2))", ])
def glyphs_to_quadratic( glyphs, max_err=MAX_ERR, reverse_direction=REVERSE_DIRECTION): quadGlyphs = {} for gname in glyphs.keys(): glyph = glyphs[gname] ttPen = TTGlyphPen(glyphs) cu2quPen = Cu2QuPen(ttPen, max_err, reverse_direction=reverse_direction) glyph.draw(cu2quPen) quadGlyphs[gname] = ttPen.glyph() return quadGlyphs
def test_curveTo_more_than_3_points(self): # a 'SuperBezier' as described in fontTools.basePen.AbstractPen pen = DummyPen() quadpen = Cu2QuPen(pen, MAX_ERR) quadpen.moveTo((0, 0)) quadpen.curveTo((1, 1), (2, 2), (3, 3), (4, 4)) self.assertEqual(str(pen).splitlines(), [ "pen.moveTo((0, 0))", "pen.qCurveTo((0.75, 0.75), (1.625, 1.625), (2, 2))", "pen.qCurveTo((2.375, 2.375), (3.25, 3.25), (4, 4))", ])
def test__check_contour_closed(self): msg = "closePath or endPath is required" quadpen = Cu2QuPen(DummyPen(), MAX_ERR) quadpen.moveTo((0, 0)) with self.assertRaisesRegex(AssertionError, msg): quadpen.moveTo((1, 1)) with self.assertRaisesRegex(AssertionError, msg): quadpen.addComponent("a", (1, 0, 0, 1, 0, 0)) # it works if contour is closed quadpen.closePath() quadpen.moveTo((1, 1)) quadpen.endPath() quadpen.addComponent("a", (1, 0, 0, 1, 0, 0))
def glyphs_to_quadratic(glyphs, max_err=MAX_ERR, reverse_direction=REVERSE_DIRECTION): try: from cu2qu.pens import Cu2QuPen from fontTools.pens.ttGlyphPen import TTGlyphPen except ImportError: raise ValueError("cannot convert glyphs due to missing libs") quadGlyphs = {} for gname in glyphs.keys(): glyph = glyphs[gname] ttPen = TTGlyphPen(glyphs) cu2quPen = Cu2QuPen(ttPen, max_err, reverse_direction=reverse_direction) glyph.draw(cu2quPen) quadGlyphs[gname] = ttPen.glyph() return quadGlyphs
def test__check_contour_is_open(self): msg = "moveTo is required" quadpen = Cu2QuPen(DummyPen(), MAX_ERR) with self.assertRaisesRegex(AssertionError, msg): quadpen.lineTo((0, 0)) with self.assertRaisesRegex(AssertionError, msg): quadpen.qCurveTo((0, 0), (1, 1)) with self.assertRaisesRegex(AssertionError, msg): quadpen.curveTo((0, 0), (1, 1), (2, 2)) with self.assertRaisesRegex(AssertionError, msg): quadpen.closePath() with self.assertRaisesRegex(AssertionError, msg): quadpen.endPath() quadpen.moveTo((0, 0)) # now it works quadpen.lineTo((1, 1)) quadpen.qCurveTo((2, 2), (3, 3)) quadpen.curveTo((4, 4), (5, 5), (6, 6)) quadpen.closePath()
def test_ignore_single_points(self): pen = DummyPen() quadpen = Cu2QuPen(pen, MAX_ERR, ignore_single_points=True) quadpen.moveTo((0, 0)) quadpen.endPath() quadpen.moveTo((1, 1)) quadpen.closePath() # single-point contours were ignored, so the pen commands are empty self.assertFalse(pen.commands) # redraw without ignoring single points quadpen.ignore_single_points = False quadpen.moveTo((0, 0)) quadpen.endPath() quadpen.moveTo((1, 1)) quadpen.closePath() self.assertTrue(pen.commands) self.assertEqual( str(pen).splitlines(), [ "pen.moveTo((0, 0))", "pen.endPath()", "pen.moveTo((1, 1))", "pen.closePath()" ])
def otf2ttf(self, maxErr=1.0, postFormat=2.0, reverseDirection=True): # maxErr = 1.0, approximation error, measured in units per em (UPM). # postFormat = 2.0, default `post` table format. # reverseDirection = True, assuming the input contours' direction is correctly set (counter-clockwise), we just flip it to clockwise. if self.font.sfntVersion != "OTTO" or not self.font.has_key( "CFF ") or not self.font.has_key("post"): print("WARNING: Invalid CFF-based font. --otf2ttf is now ignored.", file=sys.stderr) self.jobs.convert_otf2ttf = False return # Convert cubic to quadratic quadGlyphs = {} glyphOrder = self.font.getGlyphOrder() glyphSet = self.font.getGlyphSet() for glyphName in glyphSet.keys(): glyph = glyphSet[glyphName] ttPen = TTGlyphPen(glyphSet) cu2quPen = Cu2QuPen(ttPen, maxErr, reverseDirection) glyph.draw(cu2quPen) quadGlyphs[glyphName] = ttPen.glyph() # Create quadratic `glyf` table glyf = newTable("glyf") glyf.glyphOrder = glyphOrder glyf.glyphs = quadGlyphs self.font["glyf"] = glyf # Create global instruction table `prep` with basic rendering settings hintProg = Program() hintProg.fromBytecode([184, 1, 255, 133, 184, 0, 4, 141]) prep = newTable("prep") prep.program = hintProg self.font["prep"] = prep # Create `gasp` table gasp = newTable("gasp") gasp.version = 1 gasp.gaspRange = {65535: 10} self.font["gasp"] = gasp # Create partial TrueType `maxp` table (v1.0) maxp = newTable("maxp") maxp.tableVersion = 0x00010000 maxp.maxZones = 1 maxp.maxTwilightPoints = 0 maxp.maxStorage = 0 maxp.maxFunctionDefs = 0 maxp.maxInstructionDefs = 0 maxp.maxStackElements = 0 maxp.maxSizeOfInstructions = 0 maxp.maxComponentElements = max( len(g.components if hasattr(g, "components") else []) for g in glyf.glyphs.values()) self.font["maxp"] = maxp # Create an empty `loca` table, which will be automatically generated upon compile self.font["loca"] = newTable("loca") # Modify `post` table post = self.font["post"] post.formatType = postFormat post.extraNames = [] post.mapping = {} post.glyphOrder = glyphOrder # Change sfntVersion from CFF to TrueType self.font.sfntVersion = "\x00\x01\x00\x00" # Recalculate missing properties in `head`, `glyf`, `maxp` upon compile self.font.recalcBBoxes = True # Clean-ups del self.font["CFF "] if self.font.has_key("VORG"): del self.font["VORG"] return