#!/usr/bin/python """ Author: dysbulic Date: June 2008 Graphs generated by R contain paths with tens of thousands of elements which kills firefox This is a program to split those pathes into component parts. Additionally it collapses collinear lines into a single line. """ import sys, re import xml.dom.minidom from xml.dom.minidom import Node from cStringIO import StringIO if len(sys.argv) <= 1: print __doc__ sys.exit(1) file = sys.argv[1] doc = xml.dom.minidom.parse(file) allLinesRegex = re.compile("^( *([LM]) *(\\d+\.?\\d*)[, ](\\d+\.?\\d*) *)*") lineRegex = re.compile("([LM]) (\\d+\.?\\d*) (\\d+\.?\\d*)") def addPath(node, path, style): pathNode = doc.createElement("path") pathNode.setAttribute("d", path) pathNode.setAttribute("style", style) node.appendChild(pathNode) node.appendChild(doc.createTextNode("\n")) def compressPathes(node): """Process a SVG document and merge certain sequential pathes that start and end at the same point""" index = 0 while index < node.childNodes.length: child = node.childNodes[index] index += 1 if child.nodeType == Node.ELEMENT_NODE: if child.nodeName == "g": compressPathes(child) elif child.nodeName == "path": path = child.getAttribute("d") if len(path) > 5000 and allLinesRegex.match(path): style = child.getAttribute("style") parent = child.parentNode parent.removeChild(child) points = [None, None] outPath = "" for match in lineRegex.findall(path): point = [float(match[1]), float(match[2])] # There is only one M at the beginning if match[0] == "M": outPath += "M%.3f,%.3f" % (point[0], point[1]) continue # Fill the points array before continuing for i in range(0, len(points)): if points[i] is None: points[i] = point point = None if point is None: continue # Skip intermediate collinear points if ((points[1][1] - points[0][1]) * (point[0] - points[0][0]) - (points[1][0] - points[0][0]) * (point[1] - points[0][1])) == 0: points[1] = point continue outPath += " L%.3f,%.3f" % (points[0][0], points[0][1]) if len(outPath) > 4000: addPath(parent, outPath, style) outPath = "M %.3f,%.3f" % (points[0][0], points[0][1]) # Shift points back points[0] = points[1] points[1] = None # Print any remaining path elements outPath += " L%.3f,%.3f" % (points[0][0], points[0][1]) if points[1] is not None: outPath += " L%.3f,%.3f" % (points[1][0], points[1][1]) addPath(parent, outPath, style) compressPathes(doc.documentElement) print doc.documentElement.toxml()