def fixUses(self, node, index): ''' Fix <use> objects after transformation the objects their refer to ''' # Go deep first for i in node: self.fixUses(i, index) # Is it a <use> tag? if node.tag == inkex.addNS("use", "svg"): # Get reference href = node.get(inkex.addNS('href', 'xlink')) if href[0] == "#": # Did we apply any transforms to the referred node? if href[1:] in index: # Yes! thatTr = index[href[1:]] thisTr = SpeleoTransform.getTransform(node) invThatTr = SpeleoTransform.invertTransform(thatTr) # So, first transform of this <use> should be *reverting* that transform! node.set( "transform", simpletransform.formatTransform( simpletransform.composeTransform( thisTr, invThatTr)))
def effect(self): # Initialize symbol knowledge self.initSymbols() if self.options.replace: self.ensureSymbol(self.options.replace) symbol_ids = [] # Go through all selected ... for id, obj in self.selected.iteritems(): # Get unique symbol IDs symbol_ids.append(obj.get(inkex.addNS("href", "xlink"))) # For all found distinct symbol IDs ... for link in set(symbol_ids): # Perhaps not a symbol? if link == None: continue if self.options.group: # Create a group group = inkex.etree.SubElement( self.currentLayer(), "g", {"style": "stroke:black; stroke-width: 0.28mm;"}) # Compute corrective transform correctiveTransform = SpeleoTransform.invertTransform( SpeleoTransform.getTotalTransform(group)) else: # No group, no transform! group = None correctiveTransform = None # Look for our symbols! self.scanTree(self.document.getroot(), link, group, correctiveTransform)
def processTree(self, node): ''' Recursively walk the SVG tree and randomize all rock/pebble symbols on the way ''' # Is this a symbol reference? if node.tag == inkex.addNS('use', 'svg'): # Is this one of our rock/pebble symbols? m = re.match('#(gr|un|)?([ra])b([0-9]+)[a-z]', str(node.get(inkex.addNS("href", "xlink")))) if m: # Add translation, if present x = float(node.get("x")) y = float(node.get("y")) if x <> 0 or y <> 0: node.set("x", "0") node.set("y", "0") currentTr = node.get("transform") if currentTr == None: currentTr = "" node.set("transform", currentTr + (" translate(%.6f, %.6f) " % (x, y))) # Get current symbol transform tr = SpeleoTransform.getTotalTransform(node) # Move it back to 0,0 for rotation simpletransform.applyTransformToNode(SpeleoTransform.invertTransform(tr), node) # Select a new, random symbol ID (matching the symbol family) new_id = m.group(1) + m.group(2) + "b" + m.group(3) + random.choice("abcdefghij") # Make sure the new symbol type is with us if self.ensureSymbol(new_id): node.set(inkex.addNS("href", "xlink"), "#" + new_id) # If not, we just leave the type as it is. # Apply random rotation simpletransform.applyTransformToNode(simpletransform.parseTransform("rotate(" + str(random.randint(0, 359)) + ")"), node) # Return the symbol to where it was simpletransform.applyTransformToNode(tr, node) # For compatibility with old maps, using speleoUIS3 if (node.tag == inkex.addNS('tspan', 'svg') or node.tag == inkex.addNS('text', 'svg')) and node.text: if len(node.text) == 1: if node.text in string.ascii_lowercase: node.text = random.choice(string.ascii_lowercase) elif node.text in string.ascii_uppercase: node.text = random.choice(string.ascii_uppercase) if (node.text in string.ascii_letters): node.set("rotate", str(random.randint(0, 359))) # Recurse! for child in node: self.processTree(child);
def makeLine(self, node, grp=None): if node.tag != inkex.addNS("path", "svg"): return False t = self.options.linetype if self.options.method == "patheffect": return self.makeLinePE(node) tr = SpeleoTransform.getTotalTransform(node.getparent()) parent_scale = (tr[0][0] + tr[1][1]) / 2.0 tr = SpeleoTransform.getTotalTransform(node) this_scale = (tr[0][0] + tr[1][1]) / 2.0 docscale = self.getDocScale() fontsize = self.options.fontsize / parent_scale * 0.352778 * docscale linewidth = self.options.linewidth / this_scale * docscale fontstroke = self.options.fontstroke / parent_scale * docscale if t == 1: line_style = 'stroke:#000000;stroke-width:%.3fpx;fill:none' % linewidth text_style = 'font-size:%.3fpt' % fontsize text = self.char * 100 elif t == 2: line_style = 'stroke:#000000;fill:none;stroke-width:0.5px;stroke-opacity:0.005' text_style = 'font-size:%.3fpx;stroke:#000000;stroke-width:%.3fpx' % ( fontsize, fontstroke) text = self.char * 100 node.set('style', line_style) tx = inkex.etree.Element('text', {"class": "speleoLine"}) tp = inkex.etree.Element('textPath') tspan = inkex.etree.Element('tspan') tp.set(inkex.addNS('href', 'xlink'), '#' + node.get("id")) tspan.set( 'style', 'font-family:Speleo3;-inkscape-font-specification:Speleo3;' + text_style) tspan.text = text tp.append(tspan) tx.append(tp) if grp == None: grp = inkex.etree.SubElement(node.getparent(), "g") tx.set(inkex.addNS('insensitive', 'sodipodi'), 'true') grp.append(node) grp.append(tx) return True
def effect(self): if self.options.mode == "unpack": # Unpack layers from a group # Get current layer root = self.currentLayer() # Are we in the root layer? invertedTransform = SpeleoTransform.invertTransform( SpeleoTransform.getTotalTransform(root)) # Go through all selected layer containers for id, node in self.selected.iteritems(): if node.tag != inkex.addNS('g', 'svg'): continue # Index of all applied transforms idIndex = {} # Transform the group self.processLayerContainer(node, root, invertedTransform, idIndex) # Fix references self.fixUses(root, idIndex) else: # Pack layers into group # What to pack? if self.options.pack == "root": root = self.getRoot() else: root = self.currentLayer() # Create a containter to pack the layers into self.box = inkex.etree.SubElement(self.document.getroot(), "g") # Pack all the layers! for child in root: self.packLayers(child, True) # Unmark any current layer setting view = self.xpathSingle('//sodipodi:namedview') if view <> None: current_layer = inkex.addNS('current-layer', 'inkscape') if current_layer in view.attrib: view.attrib.pop(current_layer)
def getBox(self, obj): x = y = w = h = 0 try: x = float(obj.get("x")) y = float(obj.get("y")) except: pass tr = SpeleoTransform.getTotalTransform(obj) (x, y) = SpeleoTransform.transformCoords((x, y), tr) if obj.tag == inkex.addNS("rect", "svg"): try: x2 = float(obj.get("x")) + float(obj.get("width")) y2 = float(obj.get("y")) + float(obj.get("height")) (x2, y2) = SpeleoTransform.transformCoords((x2, y2), tr) w = abs(x2 - x) h = abs(y2 - y) except: pass return (x, y, w, h)
def getTransformed(self, x, y, src, dst, t, node=None): ''' Transform local coordinates into new local coordinates ''' # Compute absolute coordinates (gx, gy) = SpeleoTransform.transformCoords((x, y), t) # Compute new coordinates using transformation method of choice (nx, ny, angle) = self.transformCoordinates(gx, gy, src, dst) # If nothing changed, return old local coordinates if nx == gx and ny == gy and angle == 0: return (x, y) # ... so that we do not waste time on inverting transforms # See if we need to rotate a symbol... if angle <> 0 and node <> None: # Maybe it needs rotation! if node.tag == inkex.addNS("use", "svg") and self.options.rotate: name = node.get(inkex.addNS("href", "xlink"))[1:] if name in [ "gradient", "mini-gradient", "source", "sink", "water", "draft", "paleoflow", "scallops", "gradarr", "mini-gradarr" "grgradient", "grmini-gradient", "grsource", "grsink", "grwater", "grdraft", "grpaleoflow", "grscallops", "gradarr", "mini-gradarr" ]: # simpletransform.applyTransformToNode(simpletransform.parseTransform("rotate(" + str(angle) + ")"), node) node.set( "transform", node.get("transform") + " rotate(" + str(angle) + ")") t = SpeleoTransform.getTotalTransform(node) # Invert transform it = SpeleoTransform.invertTransform(t) # Go back to local coordinate system return SpeleoTransform.transformCoords((nx, ny), it)
def saveStation(self, marker, caption): ''' Retrieve station data from SVG elements and store it ''' # Get station name from the caption object name = self.getText(caption) # Perhaps it's not a station, but rather a prefix indication if name[:3] == "***": self.prefix = name[3:] return 1 # Get absolute marker coordinates coords = SpeleoTransform.transformCoords( (float(marker.get("x")), float(marker.get("y"))), SpeleoTransform.getTotalTransform(marker)) self.log("Station %s has coordinates %.2f, %.2f" % (name, coords[0], coords[1])) # Save the station self.dict[name] = coords return True
def scanTree(self, node, sel, group, correctiveTransform): ''' Recursively look for <use>s referring to symbol sel Adds all these symbols to group 'group', applying transforms accordingly ''' # Avoid too much recursion if node == group: return # Do not go into invisible layers if self.isLayer(node): if self.hasDisplayNone(node): return # Recurse first for i in node: self.scanTree(i, sel, group, correctiveTransform) # See if it's the symbol we need href = node.get(inkex.addNS('href', 'xlink')) # Perhaps not a reference at all... if href == None: return # Is this the right symbol? if href == sel: # Move to our group if group <> None: # Get total transform of this symbol transform = SpeleoTransform.getTotalTransform(node) # Group it together with others group.append(node) # Reset this node transform node.set( "transform", simpletransform.formatTransform( simpletransform.composeTransform( correctiveTransform, transform))) # Did anyone order a replace? if self.options.replace: node.set(inkex.addNS('href', 'xlink'), '#' + self.options.replace)
def makeLinePE(self, node): if node.tag != inkex.addNS("path", "svg"): return False docscale = self.getDocScale() if len(node.get(inkex.addNS("original-d", "inkscape"), "")) == 0: node.set(inkex.addNS("original-d", "inkscape"), node.get("d")) self.removeAttrib(node, inkex.addNS("d", "svg")) node.set( inkex.addNS("path-effect", "inkscape"), "#caveink-step-effect" if self.options.char < 4 else "#caveink-drip-effect") tr = SpeleoTransform.getTotalTransform(node) this_scale = (tr[0][0] + tr[1][1]) / 2.0 linewidth = self.options.linewidth / this_scale * docscale line_style = 'stroke:#000000;stroke-width:%.3fpx;fill:none' % linewidth node.set('style', line_style)
def processLayerContainer(self, node, root, invertedRootTransform, idIndex): ''' Unpack layers contained in a layer container. Does some preparations, and then recursively calls unpackLayers() ''' # The group might have been transformed thisTransform = SpeleoTransform.getTotalTransform(node) # Account for any transforms our target layer can possibly have thisTransform = simpletransform.composeTransform( invertedRootTransform, thisTransform) # Process children - should all be layers in fact! for child in node: # Apply transforms and change groupping mode self.unpackLayers(child, thisTransform, idIndex) # Move to our root root.append(child) # Delete the container node.getparent().remove(node)
def drawGrid(self, x_center, y_center, doc_w, doc_h, pos_x=0, pos_y=0): if self.options.coords == "rotate": rot = " rotate(270)" else: rot = "" x_center -= pos_x y_center -= pos_y uufactor = self.getDocScale() uu_spacing = self.options.spacing / self.options.scale * 1000.0 * uufactor x_start = x_center - math.floor(x_center / uu_spacing) * uu_spacing y_start = y_center - math.floor(y_center / uu_spacing) * uu_spacing num_x = int(math.ceil(doc_w / uu_spacing)) num_y = int(math.ceil(doc_h / uu_spacing)) layer = self.currentLayer() g = inkex.etree.SubElement(layer, 'g') # Invert any sub-transforms of layer simpletransform.applyTransformToNode( SpeleoTransform.invertTransform( SpeleoTransform.getTotalTransform(g)), g) # Add translation simpletransform.applyTransformToNode( simpletransform.parseTransform("translate(%.6f, %.6f)" % (pos_x, pos_y)), g) if self.options.coords != "none": g.set( 'style', 'stroke:#888;fill:#888;stroke-width:' + str(uufactor * 0.2) + 'px;font-size:' + str(self.options.fontsize * uufactor * 0.352778) + 'px;text-anchor:end;text-align:end') cg = inkex.etree.SubElement(g, "g") g = inkex.etree.SubElement(g, "g") g.set('style', 'fill:none;') cg.set('style', 'stroke:none;stroke-width:0') else: g.set('style', 'stroke:#888;fill:none;stroke-width:' + str(uufactor * 0.2)) # Vertical lines and coordinates for x in range(0, num_x): x = x_start + x * uu_spacing if x > doc_w: break if self.options.type == "line": l = inkex.etree.SubElement(g, 'path') l.set('d', 'M ' + str(x) + ",0 L " + str(x) + "," + str(doc_h)) if self.options.coords != "none": l = inkex.etree.SubElement(cg, 'text') l.text = str( int( round((x - x_center) / uu_spacing * self.options.spacing) + self.options.origin_x)) + self.options.units l.set("style", "text-anchor:end;text-align:end") l.set("transform", ("translate(%.2f,%.2f)" + rot) % (x - 1.4 * uufactor, 4.2 * uufactor)) # Horizontal lines and coordinates for y in range(0, num_y): y = y_start + y * uu_spacing if y > doc_h: break if self.options.type == "line": l = inkex.etree.SubElement(g, 'path') l.set('d', 'M 0,' + str(y) + " L " + str(doc_w) + "," + str(y)) if self.options.coords != "none": l = inkex.etree.SubElement(cg, 'text') l.text = str( int(-round( (y - y_center) / uu_spacing * self.options.spacing) + self.options.origin_y)) + self.options.units l.set("style", "text-anchor:end;text-align:end") l.set( "transform", "translate(%.2f,%.2f)" % (12.7 * uufactor, y - 1.4 * uufactor)) # Crosses, if requested if self.options.type == "cross": l = inkex.etree.SubElement(g, 'path') path_id = self.uniqueId("gridCross", True) l.set('d', "M -10,0 10,0 M 0,-10 0,10") l.set("transform", "translate(%.2f, %.2f)" % (x_center, y_center)) l.set("id", path_id) for x in range(0, num_x): x_doc = x_start + x * uu_spacing - x_center if x_doc > doc_w: break for y in range(0, num_y): if x == 0 and y == 0: continue y_doc = y_start + y * uu_spacing - y_center if y_doc > doc_h: break l = inkex.etree.SubElement(g, 'use') l.set(inkex.addNS('href', 'xlink'), '#' + str(path_id)) l.set("transform", "translate(%.2f,%.2f)" % (x_doc, y_doc))
def rectify(self, src, dst, node, centerline): ''' Recursively transform objects from old to new centerline ''' # Skip invisible layers if self.hasDisplayNone(node): return # Skip layers with centerlines and the defs object if node == centerline: return if node == self.dst: return if node.tag == inkex.addNS('defs', 'svg'): return # If it's a path ... if node.tag == inkex.addNS('path', 'svg'): # Parse path data path = simplepath.parsePath(node.get("d")) # Get absolute transform tr = SpeleoTransform.getTotalTransform(node) # Rectify the path self.rectifyPath(path, src, dst, tr) # Re-set path data node.set("d", simplepath.formatPath(path)) # Remove attributes that break our efforts # TODO not really compatible now with our modern dripline / step generating code self.removeAttrib(node, inkex.addNS("path-effect", "inkscape")) self.removeAttrib(node, inkex.addNS("original-d", "inkscape")) self.removeAttrib(node, inkex.addNS("type", "sodipodi")) return elif node.get("x") <> None and node.tag == inkex.addNS("use", "svg"): # Convert use coordinates to a transform # node.set("transform", node.get("transform") + " translate(" + node.get('x') + "," + node.get('y') + ")") #simpletransform.applyTransformToNode(simpletransform.parseTransform("translate(" + node.get('x') + "," + node.get('y') + ")"), node) # Handle symbols tr = SpeleoTransform.getTotalTransform(node) # Convert and set new coordinates (x, y) = self.getTransformed(float(node.get("x")), float(node.get("y")), src, dst, tr, node) node.set("x", str(x)) node.set("y", str(y)) elif node.get("x") <> None: # Generic handler for nodes having x & y coordinates tr = SpeleoTransform.getTotalTransform(node) # Convert and set new coordinates (x, y) = self.getTransformed(float(node.get("x")), float(node.get("y")), src, dst, tr, node) node.set("x", str(x)) node.set("y", str(y)) elif node.get("cx") <> None: # Generic handler for nodes having x & y coordinates as cx/cy tr = SpeleoTransform.getTotalTransform(node) # Convert and set new coordinates (x, y) = self.getTransformed(float(node.get("cx")), float(node.get("cy")), src, dst, tr) node.set("cx", str(x)) node.set("cy", str(y)) # Go deeper for child in node.getchildren(): self.rectify(src, dst, child, centerline)