diff --git a/exporters/SVGExporter.py b/exporters/SVGExporter.py
index 4052deec..9787de59 100644
--- a/exporters/SVGExporter.py
+++ b/exporters/SVGExporter.py
@@ -4,7 +4,7 @@ from pyqtgraph.Qt import QtGui, QtCore, QtSvg
import pyqtgraph as pg
import re
import xml.dom.minidom as xml
-
+import numpy as np
__all__ = ['SVGExporter']
@@ -121,13 +121,29 @@ def _generateItemSvg(item, nodes=None, root=None):
## 1) Qt SVG does not implement clipping paths. This is absurd.
## The solution is to let Qt generate SVG for each item independently,
## then glue them together manually with clipping.
+ ##
+ ## The format Qt generates for all items looks like this:
+ ##
+ ##
+ ##
+ ## one or more of: or or
+ ##
+ ##
+ ## one or more of: or or
+ ##
+ ## . . .
+ ##
+ ##
## 2) There seems to be wide disagreement over whether path strokes
## should be scaled anisotropically.
## see: http://web.mit.edu/jonas/www/anisotropy/
## Given that both inkscape and illustrator seem to prefer isotropic
## scaling, we will optimize for those cases.
+ ##
## 3) Qt generates paths using non-scaling-stroke from SVG 1.2, but
## inkscape only supports 1.1.
+ ##
+ ## Both 2 and 3 can be addressed by drawing all items in world coordinates.
@@ -155,14 +171,12 @@ def _generateItemSvg(item, nodes=None, root=None):
doc = xml.parseString(xmlStr)
else:
childs = item.childItems()
- tr.translate(item.pos().x(), item.pos().y())
- tr = tr * item.transform()
- #if not item.isVisible() or int(item.flags() & item.ItemHasNoContents) > 0:
- #m = (tr.m11(), tr.m12(), tr.m21(), tr.m22(), tr.m31(), tr.m32())
- ##print item, m
- #xmlStr = '' % m # note: outer group is needed to separate clipping from transform
- #doc = xml.parseString(xmlStr)
- #else:
+ if isinstance(root, QtGui.QGraphicsScene):
+ tr = item.sceneTransform()
+ else:
+ tr = item.itemTransform(root)
+ #tr.translate(item.pos().x(), item.pos().y())
+ #tr = tr * item.transform()
arr = QtCore.QByteArray()
buf = QtCore.QBuffer(arr)
svg = QtSvg.QSvgGenerator()
@@ -177,7 +191,7 @@ def _generateItemSvg(item, nodes=None, root=None):
if hasattr(item, 'setExportMode'):
item.setExportMode(True, {'painter': p})
try:
- #p.setTransform(tr)
+ p.setTransform(tr)
item.paint(p, QtGui.QStyleOptionGraphicsItem(), None)
finally:
p.end()
@@ -197,17 +211,22 @@ def _generateItemSvg(item, nodes=None, root=None):
except:
print doc.toxml()
raise
+
+
+ ## Get rid of group transformation matrices by applying
+ ## transformation to inner coordinates
+ correctCoordinates(g1, item)
## 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)
+ #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)
+ #correctStroke(g1, item, root)
## decide on a name for this item
baseName = item.__class__.__name__
@@ -226,7 +245,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
- path = QtGui.QGraphicsPathItem(item.shape())
+ if isinstance(root, QtGui.QGraphicsScene):
+ path = QtGui.QGraphicsPathItem(item.mapToScene(item.shape()))
+ else:
+ path = QtGui.QGraphicsPathItem(item.mapToItem(root, item.shape()))
pathNode = _generateItemSvg(path, root=root).getElementsByTagName('path')[0]
## and for the clipPath element
clip = name + '_clip'
@@ -248,7 +270,58 @@ def _generateItemSvg(item, nodes=None, root=None):
return g1
-
+def correctCoordinates(node, item):
+ ## Remove transformation matrices from tags by applying matrix to coordinates inside.
+ groups = node.getElementsByTagName('g')
+ for grp in groups:
+ matrix = grp.getAttribute('transform')
+ match = re.match(r'matrix\((.*)\)', matrix)
+ if match is None:
+ vals = [1,0,0,1,0,0]
+ else:
+ vals = map(float, match.groups()[0].split(','))
+ tr = np.array([[vals[0], vals[2], vals[4]], [vals[1], vals[3], vals[5]]])
+
+ removeTransform = False
+ for ch in grp.childNodes:
+ if not isinstance(ch, xml.Element):
+ continue
+ if ch.tagName == 'polyline':
+ removeTransform = True
+ coords = np.array([map(float, c.split(',')) for c in ch.getAttribute('points').strip().split(' ')])
+ coords = pg.transformCoordinates(tr, coords, transpose=True)
+ ch.setAttribute('points', ' '.join([','.join(map(str, c)) for c in coords]))
+ elif ch.tagName == 'path':
+ removeTransform = True
+ newCoords = ''
+ for c in ch.getAttribute('d').strip().split(' '):
+ x,y = c.split(',')
+ if x[0].isalpha():
+ t = x[0]
+ x = x[1:]
+ else:
+ t = ''
+ nc = pg.transformCoordinates(tr, np.array([[float(x),float(y)]]), transpose=True)
+ newCoords += t+str(nc[0,0])+','+str(nc[0,1])+' '
+ ch.setAttribute('d', newCoords)
+ elif ch.tagName == 'text':
+ removeTransform = False
+ #c = np.array([
+ #[float(ch.getAttribute('x')), float(ch.getAttribute('y'))],
+ #[float(ch.getAttribute('font-size')), 0],
+ #[0,0]])
+ #c = pg.transformCoordinates(tr, c, transpose=True)
+ #ch.setAttribute('x', str(c[0,0]))
+ #ch.setAttribute('y', str(c[0,1]))
+ #fs = c[1]-c[2]
+ #fs = (fs**2).sum()**0.5
+ #ch.setAttribute('font-size', str(fs))
+ else:
+ print('warning: export not implemented for SVG tag %s (from item %s)' % (ch.tagName, item))
+
+ if removeTransform:
+ grp.removeAttribute('transform')
+
def correctStroke(node, item, root, width=1):
#print "==============", item, node
diff --git a/graphicsItems/LinearRegionItem.py b/graphicsItems/LinearRegionItem.py
index e1b391aa..0b44c815 100644
--- a/graphicsItems/LinearRegionItem.py
+++ b/graphicsItems/LinearRegionItem.py
@@ -143,6 +143,7 @@ class LinearRegionItem(UIGraphicsItem):
#prof = debug.Profiler('LinearRegionItem.paint')
UIGraphicsItem.paint(self, p, *args)
p.setBrush(self.currentBrush)
+ p.setPen(fn.mkPen(None))
p.drawRect(self.boundingRect())
#prof.finish()