diff --git a/pyqtgraph/exporters/SVGExporter.py b/pyqtgraph/exporters/SVGExporter.py index ae1ec91c..ce2a90c0 100644 --- a/pyqtgraph/exporters/SVGExporter.py +++ b/pyqtgraph/exporters/SVGExporter.py @@ -23,7 +23,8 @@ class SVGExporter(Exporter): #{'name': 'height', 'type': 'float', 'value': tr.height(), 'limits': (0, None)}, #{'name': 'viewbox clipping', 'type': 'bool', 'value': True}, #{'name': 'normalize coordinates', 'type': 'bool', 'value': True}, - #{'name': 'normalize line width', 'type': 'bool', 'value': True}, + {'name': 'scaling stroke', 'type': 'bool', 'value': False, 'tip': "If False, strokes are non-scaling, " + "which means that they appear the same width on screen regardless of how they are scaled or how the view is zoomed."}, ]) #self.params.param('width').sigValueChanged.connect(self.widthChanged) #self.params.param('height').sigValueChanged.connect(self.heightChanged) @@ -49,7 +50,8 @@ class SVGExporter(Exporter): ## Qt's SVG generator is not complete. (notably, it lacks clipping) ## Instead, we will use Qt to generate SVG for each item independently, ## then manually reconstruct the entire document. - xml = generateSvg(self.item) + options = {ch.name():ch.value() for ch in self.params.children()} + xml = generateSvg(self.item, options) if toBytes: return xml.encode('UTF-8') @@ -69,10 +71,10 @@ xmlHeader = """\ Generated with Qt and pyqtgraph """ -def generateSvg(item): +def generateSvg(item, options={}): global xmlHeader try: - node, defs = _generateItemSvg(item) + node, defs = _generateItemSvg(item, options=options) finally: ## reset export mode for all items in the tree if isinstance(item, QtGui.QGraphicsScene): @@ -94,7 +96,7 @@ def generateSvg(item): return xmlHeader + defsXml + node.toprettyxml(indent=' ') + "\n\n" -def _generateItemSvg(item, nodes=None, root=None): +def _generateItemSvg(item, nodes=None, root=None, options={}): ## This function is intended to work around some issues with Qt's SVG generator ## and SVG in general. ## 1) Qt SVG does not implement clipping paths. This is absurd. @@ -209,18 +211,8 @@ def _generateItemSvg(item, nodes=None, root=None): ## Get rid of group transformation matrices by applying ## transformation to inner coordinates - correctCoordinates(g1, defs, item) + correctCoordinates(g1, defs, item, options) profiler('correct') - ## make sure g1 has the transformation matrix - #m = (tr.m11(), tr.m12(), tr.m21(), tr.m22(), tr.m31(), tr.m32()) - #g1.setAttribute('transform', "matrix(%f,%f,%f,%f,%f,%f)" % m) - - #print "=================",item,"=====================" - #print g1.toprettyxml(indent=" ", newl='') - - ## Inkscape does not support non-scaling-stroke (this is SVG 1.2, inkscape supports 1.1) - ## So we need to correct anything attempting to use this. - #correctStroke(g1, item, root) ## decide on a name for this item baseName = item.__class__.__name__ @@ -239,15 +231,10 @@ def _generateItemSvg(item, nodes=None, root=None): ## See if this item clips its children if int(item.flags() & item.ItemClipsChildrenToShape) > 0: ## Generate svg for just the path - #if isinstance(root, QtGui.QGraphicsScene): - #path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape())) - #else: - #path = QtGui.QGraphicsPathItem(root.mapToParent(item.mapToItem(root, item.shape()))) path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape())) item.scene().addItem(path) try: - #pathNode = _generateItemSvg(path, root=root).getElementsByTagName('path')[0] - pathNode = _generateItemSvg(path, root=root)[0].getElementsByTagName('path')[0] + pathNode = _generateItemSvg(path, root=root, options=options)[0].getElementsByTagName('path')[0] # assume for this path is empty.. possibly problematic. finally: item.scene().removeItem(path) @@ -267,17 +254,18 @@ def _generateItemSvg(item, nodes=None, root=None): ## Add all child items as sub-elements. childs.sort(key=lambda c: c.zValue()) for ch in childs: - csvg = _generateItemSvg(ch, nodes, root) + csvg = _generateItemSvg(ch, nodes, root, options=options) if csvg is None: continue cg, cdefs = csvg childGroup.appendChild(cg) ### this isn't quite right--some items draw below their parent (good enough for now) defs.extend(cdefs) - + profiler('children') return g1, defs -def correctCoordinates(node, defs, item): + +def correctCoordinates(node, defs, item, options): # TODO: correct gradient coordinates inside defs ## Remove transformation matrices from tags by applying matrix to coordinates inside. @@ -382,6 +370,10 @@ def correctCoordinates(node, defs, item): w = ((s[0]-s[1])**2).sum()**0.5 ch.setAttribute('stroke-width', str(w)) + # Remove non-scaling-stroke if requested + if options.get('scaling stroke') is True and ch.getAttribute('vector-effect') == 'non-scaling-stroke': + ch.removeAttribute('vector-effect') + if removeTransform: grp.removeAttribute('transform')