Merge pull request #671 from campagnola/svg-export-updates
Svg export updates
This commit is contained in:
commit
1d1d7bfe1e
@ -23,7 +23,8 @@ class SVGExporter(Exporter):
|
|||||||
#{'name': 'height', 'type': 'float', 'value': tr.height(), 'limits': (0, None)},
|
#{'name': 'height', 'type': 'float', 'value': tr.height(), 'limits': (0, None)},
|
||||||
#{'name': 'viewbox clipping', 'type': 'bool', 'value': True},
|
#{'name': 'viewbox clipping', 'type': 'bool', 'value': True},
|
||||||
#{'name': 'normalize coordinates', '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('width').sigValueChanged.connect(self.widthChanged)
|
||||||
#self.params.param('height').sigValueChanged.connect(self.heightChanged)
|
#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)
|
## Qt's SVG generator is not complete. (notably, it lacks clipping)
|
||||||
## Instead, we will use Qt to generate SVG for each item independently,
|
## Instead, we will use Qt to generate SVG for each item independently,
|
||||||
## then manually reconstruct the entire document.
|
## 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:
|
if toBytes:
|
||||||
return xml.encode('UTF-8')
|
return xml.encode('UTF-8')
|
||||||
@ -69,10 +71,10 @@ xmlHeader = """\
|
|||||||
<desc>Generated with Qt and pyqtgraph</desc>
|
<desc>Generated with Qt and pyqtgraph</desc>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def generateSvg(item):
|
def generateSvg(item, options={}):
|
||||||
global xmlHeader
|
global xmlHeader
|
||||||
try:
|
try:
|
||||||
node, defs = _generateItemSvg(item)
|
node, defs = _generateItemSvg(item, options=options)
|
||||||
finally:
|
finally:
|
||||||
## reset export mode for all items in the tree
|
## reset export mode for all items in the tree
|
||||||
if isinstance(item, QtGui.QGraphicsScene):
|
if isinstance(item, QtGui.QGraphicsScene):
|
||||||
@ -94,7 +96,7 @@ def generateSvg(item):
|
|||||||
return xmlHeader + defsXml + node.toprettyxml(indent=' ') + "\n</svg>\n"
|
return xmlHeader + defsXml + node.toprettyxml(indent=' ') + "\n</svg>\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
|
## This function is intended to work around some issues with Qt's SVG generator
|
||||||
## and SVG in general.
|
## and SVG in general.
|
||||||
## 1) Qt SVG does not implement clipping paths. This is absurd.
|
## 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
|
## Get rid of group transformation matrices by applying
|
||||||
## transformation to inner coordinates
|
## transformation to inner coordinates
|
||||||
correctCoordinates(g1, defs, item)
|
correctCoordinates(g1, defs, item, options)
|
||||||
profiler('correct')
|
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
|
## decide on a name for this item
|
||||||
baseName = item.__class__.__name__
|
baseName = item.__class__.__name__
|
||||||
@ -239,15 +231,10 @@ def _generateItemSvg(item, nodes=None, root=None):
|
|||||||
## See if this item clips its children
|
## See if this item clips its children
|
||||||
if int(item.flags() & item.ItemClipsChildrenToShape) > 0:
|
if int(item.flags() & item.ItemClipsChildrenToShape) > 0:
|
||||||
## Generate svg for just the path
|
## 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()))
|
path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape()))
|
||||||
item.scene().addItem(path)
|
item.scene().addItem(path)
|
||||||
try:
|
try:
|
||||||
#pathNode = _generateItemSvg(path, root=root).getElementsByTagName('path')[0]
|
pathNode = _generateItemSvg(path, root=root, options=options)[0].getElementsByTagName('path')[0]
|
||||||
pathNode = _generateItemSvg(path, root=root)[0].getElementsByTagName('path')[0]
|
|
||||||
# assume <defs> for this path is empty.. possibly problematic.
|
# assume <defs> for this path is empty.. possibly problematic.
|
||||||
finally:
|
finally:
|
||||||
item.scene().removeItem(path)
|
item.scene().removeItem(path)
|
||||||
@ -267,17 +254,18 @@ def _generateItemSvg(item, nodes=None, root=None):
|
|||||||
## Add all child items as sub-elements.
|
## Add all child items as sub-elements.
|
||||||
childs.sort(key=lambda c: c.zValue())
|
childs.sort(key=lambda c: c.zValue())
|
||||||
for ch in childs:
|
for ch in childs:
|
||||||
csvg = _generateItemSvg(ch, nodes, root)
|
csvg = _generateItemSvg(ch, nodes, root, options=options)
|
||||||
if csvg is None:
|
if csvg is None:
|
||||||
continue
|
continue
|
||||||
cg, cdefs = csvg
|
cg, cdefs = csvg
|
||||||
childGroup.appendChild(cg) ### this isn't quite right--some items draw below their parent (good enough for now)
|
childGroup.appendChild(cg) ### this isn't quite right--some items draw below their parent (good enough for now)
|
||||||
defs.extend(cdefs)
|
defs.extend(cdefs)
|
||||||
|
|
||||||
profiler('children')
|
profiler('children')
|
||||||
return g1, defs
|
return g1, defs
|
||||||
|
|
||||||
def correctCoordinates(node, defs, item):
|
|
||||||
|
def correctCoordinates(node, defs, item, options):
|
||||||
# TODO: correct gradient coordinates inside defs
|
# TODO: correct gradient coordinates inside defs
|
||||||
|
|
||||||
## Remove transformation matrices from <g> tags by applying matrix to coordinates inside.
|
## Remove transformation matrices from <g> tags by applying matrix to coordinates inside.
|
||||||
@ -344,6 +332,10 @@ def correctCoordinates(node, defs, item):
|
|||||||
t = ''
|
t = ''
|
||||||
nc = fn.transformCoordinates(tr, np.array([[float(x),float(y)]]), transpose=True)
|
nc = fn.transformCoordinates(tr, np.array([[float(x),float(y)]]), transpose=True)
|
||||||
newCoords += t+str(nc[0,0])+','+str(nc[0,1])+' '
|
newCoords += t+str(nc[0,0])+','+str(nc[0,1])+' '
|
||||||
|
# If coords start with L instead of M, then the entire path will not be rendered.
|
||||||
|
# (This can happen if the first point had nan values in it--Qt will skip it on export)
|
||||||
|
if newCoords[0] != 'M':
|
||||||
|
newCoords = 'M' + newCoords[1:]
|
||||||
ch.setAttribute('d', newCoords)
|
ch.setAttribute('d', newCoords)
|
||||||
elif ch.tagName == 'text':
|
elif ch.tagName == 'text':
|
||||||
removeTransform = False
|
removeTransform = False
|
||||||
@ -378,6 +370,10 @@ def correctCoordinates(node, defs, item):
|
|||||||
w = ((s[0]-s[1])**2).sum()**0.5
|
w = ((s[0]-s[1])**2).sum()**0.5
|
||||||
ch.setAttribute('stroke-width', str(w))
|
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:
|
if removeTransform:
|
||||||
grp.removeAttribute('transform')
|
grp.removeAttribute('transform')
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user