def test_from_svg_file(self): pen = RecordingPen() with NamedTemporaryFile(delete=False) as tmp: tmp.write(tobytes(SVG_DATA)) try: svg = SVGPath(tmp.name) svg.draw(pen) finally: os.remove(tmp.name) assert pen.value == EXPECTED_PEN_COMMANDS
def svg2glif(svg_file, name, width=0, height=0, unicodes=None, transform=None, version=2): """ Convert an SVG outline to a UFO glyph with given 'name', advance 'width' and 'height' (int), and 'unicodes' (list of int). Return the resulting string in GLIF format (default: version 2). If 'transform' is provided, apply a transformation matrix before the conversion (must be tuple of 6 floats, or a FontTools Transform object). """ glyph = SimpleNamespace(width=width, height=height, unicodes=unicodes) outline = SVGPath(svg_file, transform) # writeGlyphToString takes a callable (usually a glyph's drawPoints # method) that accepts a PointPen, however SVGPath currently only has # a draw method that accepts a segment pen. We need to wrap the call # with a converter pen. def drawPoints(pointPen): pen = SegmentToPointPen(pointPen) outline.draw(pen) return writeGlyphToString(name, glyphObject=glyph, drawPointsFunc=drawPoints, formatVersion=version)
def dropEvent(self, event): mimeData = event.mimeData() if mimeData.hasUrls(): paths = mimeData.urls() # pick just one image path = paths[0].toLocalFile() fileName = os.path.basename(path) with open(path, "rb") as imgFile: data = imgFile.read() ext = os.path.splitext(path)[1][1:] # TODO: make sure we cleanup properly when replacing an image with # another if ext.lower() == "glif": otherGlyph = self._glyph.__class__() try: readGlyphFromString(data, otherGlyph, otherGlyph.getPointPen()) except Exception as e: errorReports.showCriticalException(e) return self._glyph.beginUndoGroup() otherGlyph.drawPoints(self._glyph.getPointPen()) self._glyph.endUndoGroup() return if ext.lower() == "svg": try: svgPath = SVGPath.fromstring(data) except Exception as e: errorReports.showCriticalException(e) return self._glyph.beginUndoGroup() svgPath.draw(self._glyph.getPen()) self._glyph.endUndoGroup() return if ext.lower() != "png": # convert img = QImage(path) data = QByteArray() buffer = QBuffer(data) buffer.open(QIODevice.WriteOnly) img.save(buffer, "PNG") # format data = bytearray(data) fileName = "%s.png" % os.path.splitext(fileName)[0] imageSet = self._glyph.font.images try: imageSet[fileName] = data except Exception as e: errorReports.showCriticalException(e) return image = self._glyph.instantiateImage() image.fileName = fileName self._glyph.image = image event.setAccepted(True) else: super().dropEvent(event)
def test_transform(self): pen = RecordingPen() svg = SVGPath.fromstring(SVG_DATA, transform=(1.0, 0, 0, -1.0, 0, 1000)) svg.draw(pen) assert pen.value == [ ("moveTo", ((100.0, 900.0), )), ("lineTo", ((300.0, 900.0), )), ("lineTo", ((200.0, 700.0), )), ("lineTo", ((100.0, 900.0), )), ("closePath", ()), ("moveTo", ((100.0, 800.0), )), ("curveTo", ((100.0, 900.0), (250.0, 900.0), (250.0, 800.0))), ("curveTo", ((250.0, 700.0), (400.0, 700.0), (400.0, 800.0))), ("endPath", ()) ]
def test_transform(self): pen = RecordingPen() svg = SVGPath.fromstring(SVG_DATA, transform=(1.0, 0, 0, -1.0, 0, 1000)) svg.draw(pen) assert pen.value == [ ("moveTo", ((100.0, 900.0),)), ("lineTo", ((300.0, 900.0),)), ("lineTo", ((200.0, 700.0),)), ("lineTo", ((100.0, 900.0),)), ("closePath", ()), ("moveTo", ((100.0, 800.0),)), ("curveTo", ((100.0, 900.0), (250.0, 900.0), (250.0, 800.0))), ("curveTo", ((250.0, 700.0), (400.0, 700.0), (400.0, 800.0))), ("endPath", ()) ]
def potrace(self, rect, poargs=[], invert=True, pen_class=None, context=None): import skia from PIL import Image from pathlib import Path from subprocess import run from fontTools.svgLib import SVGPath pc, ctx = self._get_renderer_state(pen_class, context) img = pc.Precompose(self, rect, context=ctx) pilimg = Image.fromarray( img.convert(alphaType=skia.kUnpremul_AlphaType)) binpo = Path("bin/potrace") if not binpo.exists(): binpo = Path(__file__).parent.parent.parent / "bin/potrace" with tempfile.NamedTemporaryFile(prefix="coldtype_tmp", suffix=".bmp") as tmp_bmp: pilimg.save(tmp_bmp.name) rargs = [str(binpo), "-s"] if invert: rargs.append("--invert") rargs.extend([str(x) for x in poargs]) rargs.extend(["-o", "-", "--", tmp_bmp.name]) if False: print(">>>", " ".join(rargs)) result = run(rargs, capture_output=True) t = Transform() t = t.scale(0.1, 0.1) svgp = SVGPath.fromstring(result.stdout, transform=t) dp = self.single_pen_class() svgp.draw(dp) return dp.f(0)
def svg2glif(svg, name, width=0, height=0, unicodes=None, transform=None, version=2): """ Convert an SVG outline to a UFO glyph with given 'name', advance 'width' and 'height' (int), and 'unicodes' (list of int). Return the resulting string in GLIF format (default: version 2). If 'transform' is provided, apply a transformation matrix before the conversion (must be tuple of 6 floats, or a FontTools Transform object). """ glyph = SimpleNamespace(width=width, height=height, unicodes=unicodes) outline = SVGPath.fromstring(svg, transform=transform) # writeGlyphToString takes a callable (usually a glyph's drawPoints # method) that accepts a PointPen, however SVGPath currently only has # a draw method that accepts a segment pen. We need to wrap the call # with a converter pen. def drawPoints(pointPen): pen = SegmentToPointPen(pointPen) outline.draw(pen) return writeGlyphToString(name, glyphObject=glyph, drawPointsFunc=drawPoints, formatVersion=version)
def test_fromstring(self): pen = RecordingPen() svg = SVGPath.fromstring(SVG_DATA) svg.draw(pen) assert pen.value == EXPECTED_PEN_COMMANDS
def paste(self): isGlyphTab = self.isGlyphTab() widget = self.stackWidget.currentWidget() if isGlyphTab: glyphs = (widget.activeGlyph(),) else: selection = self.glyphCellView.selection() glyphs = widget.glyphsForIndexes(selection) clipboard = QApplication.clipboard() mimeData = clipboard.mimeData() if mimeData.hasFormat("application/x-trufont-glyph-data"): data = pickle.loads(mimeData.data("application/x-trufont-glyph-data")) if len(data) == len(glyphs): for pickled, glyph in zip(data, glyphs): if isGlyphTab: pasteGlyph = glyph.__class__() pasteGlyph.deserialize(pickled) # TODO: if we serialize selected state, we don't need # to do this pasteGlyph.selected = True if ( len(pasteGlyph) or len(pasteGlyph.components) or len(pasteGlyph.anchors) ): glyph.beginUndoGroup() glyph.holdNotifications() count = len(glyph) pen = glyph.getPointPen() # contours, components pasteGlyph.drawPoints(pen) for contour in glyph[count:]: contour.selected = True # anchors for anchor in pasteGlyph.anchors: glyph.appendAnchor(dict(anchor)) # guidelines for guideline in pasteGlyph.guidelines: glyph.appendGuideline(dict(guideline)) glyph.releaseHeldNotifications() glyph.endUndoGroup() else: glyph.deserialize(pickled) return if mimeData.hasFormat("image/svg+xml"): if len(glyphs) == 1: glyph = glyphs[0] try: svgPath = SVGPath.fromstring(mimeData.data("image/svg+xml")) except Exception: pass else: glyph.beginUndoGroup() if not isGlyphTab: glyph.clear() svgPath.draw(glyph.getPen()) glyph.endUndoGroup() return if mimeData.hasText(): if len(glyphs) == 1: glyph = glyphs[0] otherGlyph = glyph.__class__() text = mimeData.text() try: readGlyphFromString(text, otherGlyph, otherGlyph.getPointPen()) except Exception: try: svgPath = SVGPath.fromstring(text) svgPath.draw(otherGlyph.getPen()) except Exception: return glyph.beginUndoGroup() if not isGlyphTab: glyph.clear() otherGlyph.drawPoints(glyph.getPointPen()) glyph.endUndoGroup()
def doImport(self, f, filePath): layerChoice = self.w.importBox.layerChoice.get() # Format the path names plistPath = os.path.splitext(filePath)[0] + ".plist" svgPath = os.path.splitext(filePath)[0] + ".svg" # Import the SVG outline = SVGPath(svgPath) svgGlyph = RGlyph() pen = svgGlyph.getPen() outline.draw(pen) # Read the Plist with open(plistPath, 'rb') as plistFile: plistData = load(plistFile) thisPageScale = plistData[ "pageScale"] # Use the pageScale and bufferFactor from the file, not from the extension prefs thisBufferFactor = plistData["bufferFactor"] xMax = plistData["xMax"] / thisPageScale yMax = plistData["yMax"] / thisPageScale rowCount = plistData["rowCount"] colCount = plistData["colCount"] pageWidth = plistData["pageWidth"] / thisPageScale pageHeight = plistData["pageHeight"] / thisPageScale glyphLocations = plistData["glyphLocations"] # Scale the glyph back to the normal size svgGlyph.scaleBy((1 / thisPageScale, 1 / thisPageScale)) # Flip and move the SVG Glyph so that it's in the correct orientation svgGlyph.scaleBy((1, -1)) svgGlyph.moveBy((0, pageHeight)) # Find the location that grid location starts (which will be at the center of each glyph drawing) # Same as the export gridLocs = {} for rowNumber in range(rowCount): for colNumber in range(colCount): gridID = "%s-%s" % (colNumber, rowNumber) locX = ((colNumber * xMax * thisBufferFactor) + xMax * thisBufferFactor) locY = (pageHeight - (rowNumber * yMax * thisBufferFactor) - (yMax * thisBufferFactor)) gridLocs[gridID] = (locX, locY) # Find which contours from the SVG belong in which of the grid locations # Hold the contours aside by their index for now sortedContours = {} for cIdx, contour in enumerate(svgGlyph.contours): for gridID, gridLoc in gridLocs.items(): if contour.bounds[0] > gridLoc[0] - xMax and contour.bounds[ 2] < gridLoc[0] + xMax: if contour.bounds[ 1] > gridLoc[1] - yMax and contour.bounds[ 3] < gridLoc[1] + yMax: if not gridID in sortedContours: sortedContours[gridID] = [] sortedContours[gridID].append(cIdx) # Draw the contours into glyphs for gn, gridID in glyphLocations: if not gn in f.keys(): f.newGlyph(gn) g = f[gn] if layerChoice == 0: gf = g.getLayer(f.defaultLayerName) # Import into the foregronud gl = gf.getLayer("backup") gl.appendGlyph(g) gl.width = gf.width gf.clear() destGlyph = gf else: # Import into the "import" layer gl = g.getLayer("import") gl.clear() gl.width = g.width destGlyph = gl # Import if gridID in sortedContours: for cIdx in sortedContours[gridID]: c = svgGlyph.contours[cIdx] destGlyph.appendContour(c) # Move to the center of the grid location destGlyph.moveBy((-gridLocs[gridID][0], -gridLocs[gridID][1])) # ...and offset since the glyph drawing was centered up on the grid location destGlyph.moveBy((g.width * 0.5, yMax * 0.5)) # Small amount of cleanup, remove single-point contours for c in destGlyph.contours: if len(c.points) == 1: destGlyph.removeContour(c) destGlyph.changed()