def effect(self): zoom = self.svg.unittouu(str(self.options.zoom) + 'px') if self.options.randomize: imagelist = generate_random_string(self.options.text, zoom) else: tokens = tokenize(self.options.text) imagelist = randomize_input_string(tokens, zoom) image = layoutstring(imagelist, zoom) if image: s = {'stroke': 'none', 'fill': '#000000'} new = inkex.PathElement(style=str(inkex.Style(s)), d=str(inkex.Path(image))) layer = self.svg.get_current_layer() layer.append(new) # compensate preserved transforms of parent layer if layer.getparent() is not None: mat = ( self.svg.get_current_layer().transform * inkex.Transform([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])).matrix new.transform *= -inkex.Transform(mat)
def draw_coluor_bars(self, cx, cy, rotate, name, parent, bbox): group = parent.add(inkex.Group(id=name)) group.transform = inkex.Transform(translate=(cx, cy)) * inkex.Transform(rotate=rotate) loc = 0 if bbox: loc = min(self.mark_size / 3, max(bbox.width, bbox.height) / 45) for bar in [{'c': '*', 'stroke': '#000', 'x': 0, 'y': -(loc + 1)}, {'c': 'r', 'stroke': '#0FF', 'x': 0, 'y': 0}, {'c': 'g', 'stroke': '#F0F', 'x': (loc * 11) + 1, 'y': -(loc + 1)}, {'c': 'b', 'stroke': '#FF0', 'x': (loc * 11) + 1, 'y': 0} ]: i = 0 while i <= 1: color = inkex.Color('white') if bar['c'] == 'r' or bar['c'] == '*': color.red = 255 * i if bar['c'] == 'g' or bar['c'] == '*': color.green = 255 * i if bar['c'] == 'b' or bar['c'] == '*': color.blue = 255 * i r_att = {'fill': str(color), 'stroke': bar['stroke'], 'stroke-width': loc/8, 'x': str((loc * i * 10) + bar['x']), 'y': str(bar['y']), 'width': loc, 'height': loc} rect = Rectangle() for att, value in r_att.items(): rect.set(att, value) group.add(rect) i += 0.1
def composeParents(self, node, m): t = node.get('transform') if t: m = inkex.Transform(inkex.Transform(t).matrix) * inkex.Transform(m) if node.getparent().tag == inkex.addNS( 'g', 'svg') or node.getparent().tag == inkex.addNS('a', 'svg'): m = self.composeParents(node.getparent(), m) return m
def check_text_on_path(svg, element, scale_x, scale_y): """Check whether to skip scaling a text put on a path.""" skip = False path = element.find('textPath').href if not is_in_defs(svg, path): if is_sibling(element, path): # skip common element scaling if both text and path are siblings skip = True # scale offset if 'transform' in element.attrib: element.transform.add_scale(scale_x, scale_y) # scale font size mat = inkex.Transform('scale({},{})'.format(scale_x, scale_y)).matrix det = abs(mat[0][0] * mat[1][1] - mat[0][1] * mat[1][0]) descrim = math.sqrt(abs(det)) prop = 'font-size' # outer text sdict = dict(inkex.Style.parse_str(element.get('style'))) if prop in sdict: sdict[prop] = float(sdict[prop]) * descrim element.set('style', str(inkex.Style(sdict))) # inner tspans for child in element.iterdescendants(): if isinstance(element, inkex.Tspan): sdict = dict(inkex.Style.parse_str(child.get('style'))) if prop in sdict: sdict[prop] = float(sdict[prop]) * descrim child.set('style', str(inkex.Style(sdict))) return skip
def save(self, stream): self._stream = stream self.dxf_insert_code('999', '"DXF R12 Output" (www.mydxf.blogspot.com)') self.dxf_add(r12_header) scale = 25.4 / 90.0 h = self.svg.height path = '//svg:path' for node in self.svg.xpath(path): layer = node.getparent().label if layer is None: layer = 'Layer 1' node.transform *= inkex.Transform([[scale, 0, 0], [0, -scale, h * scale]]) node.apply_transform() path = node.path.to_superpath() if re.search('drill$', layer, re.I) is None: # if layer == 'Brackets Drill': self.dxf_path_to_lines(layer, path) else: self.dxf_path_to_point(layer, path) self.dxf_add(r12_footer)
def effect(self): xforms = [] for i in range(self.NXFORM): if getattr(self.options, 'xform%d' % i): t = [ getattr(self.options, "%s%d" % (p, i)) for p in self.XFORM_PARAMS ] xforms.append(inkex.Transform(t)) if not xforms: inkex.errormsg(_('There are no transforms to apply')) return False if not self.svg.selected: inkex.errormsg(_('There is no selection to duplicate')) return False nodes = self.svg.selected.values() grp = inkex.Group('IFS') layer = self.svg.get_current_layer().add(grp) for i in range(self.options.iter): n = [] for node in nodes: for x in xforms: d = node.copy() d.transform = x * d.transform n.append(d) g = inkex.Group('IFS iter %d' % i) g.add(*n) grp.add(g) nodes = n return True
def set_namedview(self, width, height, unit): width = self.options.width height = self.options.height factor = width / height clip_rect = self.svg.getElementById("clipPathRect") clip_rect.set("width", str(width)) clip_rect.set("height", str(height)) scale = inkex.Transform(scale=(width / 100, height / 100)) self.svg.getElementById("designTop").transform = scale self.svg.getElementById("designBottom").transform = scale scale = (1, factor) if factor <= 1 else (1 / factor, 1) for child in self.svg.getElementById("designTop"): child.transform = inkex.Transform(scale=scale) text_preview = self.svg.getElementById('textPreview') if text_preview is not None: x = width / 100.0 / factor y = height / 1000.0 if factor <= 1: x *= factor y *= factor text_preview.transform = inkex.Transform(translate=(int(width) * 2, 0), scale=(x, y)) info_group = self.svg.getElementById('infoGroup') if info_group is not None: scale = 100 if factor <= 1 else 1000 info_group.transform = inkex.Transform(scale=(width / scale, height / scale * factor)) sides = [(x, y) for y in (-height, 0, height) for x in (-width, 0, width)] for i, (x, y) in enumerate(sides): top = self.svg.getElementById('top{i}'.format(i=i+1)) bottom = self.svg.getElementById('bottom{i}'.format(i=i+1)) if top is not None and bottom is not None: bottom.transform = top.transform = inkex.Transform(translate=(x, y)) clones = [(x, y) for x in (0, width, width * 2) for y in (0, height, height * 2)] for i, (x, y) in enumerate(clones): preview = self.svg.getElementById("clonePreview{i}".format(i=i)) if preview is not None: preview.transform = inkex.Transform(translate=(x, y)) pattern_generator = self.svg.getElementById('fullPatternClone') if pattern_generator is not None: pattern_generator.transform = inkex.Transform(translate=(width * 2, -height)) pattern_generator.set("inkscape:tile-cx", width / 2) pattern_generator.set("inkscape:tile-cy", height / 2) pattern_generator.set("inkscape:tile-w", width) pattern_generator.set("inkscape:tile-h", height) pattern_generator.set("inkscape:tile-x0", width) pattern_generator.set("inkscape:tile-y0", height) pattern_generator.set("width", width) pattern_generator.set("height", height) namedview = self.svg.namedview namedview.set('inkscape:document-units', 'px') namedview.set('inkscape:cx', (width * 5.5) / 2) namedview.set('inkscape:cy', "0") namedview.set('inkscape:zoom', 1 / (width / 100))
def effect(self): if len(self.options.ids) < 2: raise inkex.AbortExtension( _("This extension requires two selected objects. \nThe second must be a path, exactly two nodes long." )) #trafo is selected second obj = self.svg.selected[self.options.ids[0]] trafo = self.svg.selected[self.options.ids[1]] if isinstance(trafo, inkex.PathElement): #distil trafo into two node points trafoPath = trafo.path.transform( trafo.composed_transform()).to_superpath() if len(trafoPath[0]) != 2: raise inkex.AbortExtension( _("The second selected object must be exactly two nodes long." )) # origin of mirror line ox = trafoPath[0][0][1][0] oy = trafoPath[0][0][1][1] # vector along mirror line vx = trafoPath[0][1][1][0] - ox vy = trafoPath[0][1][1][1] - oy # the transformation first translates the origin of the mirror line to [0 0], then rotates the mirror line onto the x-axis, # reflects everything over the x-axis, undoes the rotation, and finally undoes the translation # alpha = atan2(vy, vx); # [1 0 ox] [cos(alpha) -sin(alpha) 0] [1 0 0] [cos(-alpha) -sin(-alpha) 0] [1 0 -ox] # Transformation = [0 1 oy]*[sin(alpha) cos(alpha) 0]*[0 -1 0]*[sin(-alpha) cos(-alpha) 0]*[0 1 -oy] # [0 0 1] [ 0 0 1] [0 0 1] [ 0 0 1] [0 0 1] # after some simplifications (or using your favorite symbolic math software): # [(vx^2-vy^2)/(vx^2+vy^2) (2 vx vy)/(vx^2+vy^2) (2 vy (ox vy-oy vx))/(vx^2+vy^2)] # Transformation = [ (2 vx vy)/(vx^2+vy^2) -(vx^2-vy^2)/(vx^2+vy^2) -(2 vx (ox vy-oy vx))/(vx^2+vy^2)] # [ 0 0 1] denom = vx**2 + vy**2 a00 = (vx**2 - vy**2) / denom a01 = (2 * vx * vy) / denom a02 = 2 * (ox * vy - oy * vx) / denom mat = [[a00, a01, vy * a02], [a01, -a00, -vx * a02]] obj.transform = inkex.Transform(mat) * obj.transform else: if isinstance(trafo, inkex.Group): raise inkex.AbortExtension( _("The second selected object is a group, not a path.\nTry using the procedure Object->Ungroup." )) else: raise inkex.AbortExtension( _("The second selected object is not a path.\nTry using the procedure Path->Object to Path." ))
def effect(self): # make sure we have at least 2 nodes selected if len(self.options.ids) < 2: inkex.utils.debug("You must select at least 2 nodes") return # pick the achor node anchor_nodeId = self.options.ids[0] # first selected if self.options.anchor_node == "LAST_SEL": # last selected #inkex.utils.debug("last sel") #inkex.utils.debug(self.options.ids) anchor_nodeId = self.options.ids[-1] elif self.options.anchor_node == "LARGEST": # largest anchor_nodeId = None largestArea = 0 for nodeId, node in self.svg.selected.items(): nodeArea = BoundingBoxArea(node) if nodeArea > largestArea: anchor_nodeId = nodeId largestArea = nodeArea anchorBBox = self.svg.selected[anchor_nodeId].bounding_box() # calculate the offsets in mm insetH = self.svg.unittouu("{0}mm".format(self.options.inset_x)) insetV = self.svg.unittouu("{0}mm".format(self.options.inset_y)) otherNodes = [ n for i, n in self.svg.selected.items() if i != anchor_nodeId ] # for every other Node for node in otherNodes: bbox = node.bounding_box() # sort out vertical offset deltaV = (anchorBBox.top - bbox.top) + insetV # ALIGN TOP if self.options.relative_to_v in "BOTTOM": deltaV = (anchorBBox.bottom - bbox.bottom) - insetV # ALIGN BOTTOM if self.options.relative_to_v == "CENTRE": deltaV = (anchorBBox.top + anchorBBox.height / 2 - bbox.height / 2) - bbox.top # ALIGN CENTRE # sort out the horizontal offset deltaH = (anchorBBox.left - bbox.left) + insetH # ALIGN LEFT if self.options.relative_to_h == "RIGHT": deltaH = (anchorBBox.right - bbox.right) - insetH # ALIGN RIGHT if self.options.relative_to_h == "MIDDLE": deltaH = (anchorBBox.left + anchorBBox.width / 2 - bbox.width / 2) - bbox.left # ALIGN MIDDLE tform = inkex.Transform("translate({0}, {1})".format( deltaH, deltaV)) node.transform = tform * node.transform
def render_symbol(self): symbol = self.svg.getElementById(self.options.symbolid) if symbol is None: raise inkex.AbortExtension( f"Can't find symbol {self.options.symbolid}") bbox = symbol.path.bounding_box() transform = inkex.Transform(scale=( float(self.boxsize) / bbox.width, float(self.boxsize) / bbox.height, )) for row in range(self.draw.row_count()): for col in range(self.draw.col_count()): if self.draw.isDark(col, row): x, y = self.get_svg_pos(col, row) # Inkscape doesn't support width/height on use tags return Use.new(symbol, x, y, transform=transform)
def longitude_lines(self, number, tilt, radius, rotate): """Add lines of latitude as a group""" # GROUP FOR THE LINES OF LONGITUDE grp_long = inkex.Group() grp_long.set('inkscape:label', 'Lines of Longitude') # angle between neighbouring lines of longitude in degrees #delta_long = 360.0 / number for i in range(0, number // 2): # The longitude of this particular line in radians long_angle = rotate + (i * (360.0 / number)) * (pi / 180.0) if long_angle > pi: long_angle -= 2 * pi # the rise is scaled by the sine of the tilt # length = sqrt(width*width+height*height) #by pythagorean theorem # inverse = sin(acos(length/so.RADIUS)) inverse = abs(sin(long_angle)) * cos(tilt) rads = (radius * inverse + EPSILON, radius) # The rotation of the ellipse to get it to pass through the pole (degs) rotation = atan((radius * sin(long_angle) * sin(tilt)) / (radius * cos(long_angle))) * (180.0 / pi) # remove the hidden side of the ellipses if required # this is always exactly half the ellipse, but we need to find out which half start_end = (0, 2 * pi ) # Default start and end angles -> full ellipse if self.options.HIDE_BACK: if long_angle <= pi / 2: # cut out the half ellispse that is hidden start_end = (pi / 2, 3 * pi / 2) else: start_end = (3 * pi / 2, pi / 2) # finally, draw the line of longitude # the centre is always at the centre of the sphere elem = grp_long.add(self.draw_ellipse(rads, (0, 0), start_end)) # the rotation will be applied about the group centre (the centre of the sphere) elem.transform = inkex.Transform(rotate=(rotation, )) return grp_long
def parsePath(self, node, transforms, names): name = "" for n in names: name = n + "_" + name name = name + node.get("id") m2 = [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]] for t in transforms: m = inkex.Transform(t).matrix m2 = inkex.Transform(m2) * inkex.Transform(m) m = inkex.Transform(node.get("transform")).matrix m2 = inkex.Transform(m2) * inkex.Transform(m) color = self.get_color(node) path = str(inkex.Path(inkex.Path(node.get('d')).to_arrays())) subpaths = path.split('M') for i in range(1, len(subpaths)): subpaths[i] = 'M ' + str.rstrip(subpaths[i]) closed = subpaths[i][-1] in ['Z', 'z'] csp = inkex.Path(subpaths[i]).to_arrays() csp = inkex.Path(csp).transform(m2).to_arrays() if closed: self.closed2curves(csp) else: self.opened2curves(csp) vertices = self.cast2spine(csp, closed) if len(vertices) >= 9 and closed or len( vertices) >= 6 and not closed: self.path2json(name + "_" + str(i), closed, color, vertices) else: inkex.debug("skipping " + name + "_" + str(i) + ": vertex count < 6 (" + str(len(vertices)) + ")")
def recursivelyTraverseSvg(self, nodeList, matCurrent=[[1.0, 0.0, 0.0], [0.0, -1.0, 0.0]], parent_visibility='visible'): """ Recursively traverse the svg file to plot out all of the paths. The function keeps track of the composite transformation that should be applied to each path. This function handles path, group, line, rect, polyline, polygon, circle, ellipse and use (clone) elements. Notable elements not handled include text. Unhandled elements should be converted to paths in Inkscape. TODO: There's a lot of inlined code in the eggbot version of this that would benefit from the Entities method of dealing with things. """ for node in nodeList: # Ignore invisible nodes v = node.get('visibility', parent_visibility) if v == 'inherit': v = parent_visibility if v == 'hidden' or v == 'collapse': pass # first apply the current matrix transform to this node's transform # matNew = composeTransform(matCurrent, parseTransform(node.get("transform"))) matNew = inkex.Transform(matCurrent) * inkex.Transform( node.get("transform")) if node.tag == inkex.addNS('g', 'svg') or node.tag == 'g': if (node.get(inkex.addNS('groupmode', 'inkscape')) == 'layer'): layer_name = node.get(inkex.addNS('label', 'inkscape')) if (self.pause_on_layer_change == 'true'): self.entities.append(SvgLayerChange(layer_name)) self.recursivelyTraverseSvg(node, matNew, parent_visibility=v) elif node.tag == inkex.addNS('use', 'svg') or node.tag == 'use': refid = node.get(inkex.addNS('href', 'xlink')) if refid: # [1:] to ignore leading '#' in reference path = '//*[@id="%s"]' % refid[1:] refnode = node.xpath(path) if refnode: x = float(node.get('x', '0')) y = float(node.get('y', '0')) # Note: the transform has already been applied if (x != 0) or (y != 0): matNew2 = inkex.Transform( matNew) * inkex.Transform.add_translate(x, y) else: matNew2 = matNew v = node.get('visibility', v) self.recursivelyTraverseSvg(refnode, matNew2, parent_visibility=v) else: pass else: pass elif not isinstance(node.tag, str): pass else: entity = self.make_entity(node, matNew) if entity == None: inkex.errormsg( 'Warning: unable to draw object, please convert it to a path first.' )
def effect(self): if not self.svg.selected: inkex.errormsg( "Selection is empty. Please select some objects first!") return export_dir = Path(self.absolute_href(self.options.export_dir)) os.makedirs(export_dir, exist_ok=True) offset = self.options.border_offset bbox = inkex.BoundingBox() for elem in self.svg.selected.values(): transform = inkex.Transform() parent = elem.getparent() if parent is not None and isinstance(parent, inkex.ShapeElement): transform = parent.composed_transform() try: bbox += elem.bounding_box(transform) except Exception: logger.exception("Bounding box not computed") logger.info("Skipping bounding box") transform = elem.composed_transform() x1, y1 = transform.apply_to_point([0, 0]) x2, y2 = transform.apply_to_point([1, 1]) bbox += inkex.BoundingBox((x1, x2), (y1, y2)) template = self.create_document() filename = None group = etree.SubElement(template, '{http://www.w3.org/2000/svg}g') group.attrib['id'] = GROUP_ID group.attrib['transform'] = str( inkex.Transform(((1, 0, -bbox.left), (0, 1, -bbox.top)))) for elem in self.svg.selected.values(): elem_copy = deepcopy(elem) elem_copy.attrib['transform'] = str(elem.composed_transform()) group.append(elem_copy) template.attrib[ 'viewBox'] = f'{-offset} {-offset} {bbox.width + offset * 2} {bbox.height + offset * 2}' template.attrib[ 'width'] = f'{bbox.width + offset * 2}' + self.svg.unit template.attrib[ 'height'] = f'{bbox.height + offset * 2}' + self.svg.unit if filename is None: filename = elem.attrib.get('id', None) if filename: filename = filename.replace(os.sep, '_') + '.svg' if not filename: #should never be the case. Inkscape might crash if the id attribute is empty or not existent due to invalid SVG filename = self.svg.get_unique_id("selection") + '.svg' template.append(group) if not self.options.wrap_transform: self.load( inkscape_command(template.tostring(), select=GROUP_ID, verbs=['SelectionUnGroup'])) template = self.svg for child in template.getchildren(): if child.tag == '{http://www.w3.org/2000/svg}metadata': template.remove(child) self.save_document(template, export_dir / filename) if self.options.opendir is True: self.openExplorer(export_dir) if self.options.newwindow is True: inkscape(os.path.join(export_dir, filename)) if self.options.export_dxf is True: #ensure that python3 command is available #we pass 25.4/96 which stands for unit mm. See inkex.units.UNITS and dxf_outlines.inx cmd = [ 'python3', self.options.dxf_exporter_path, '--output=' + os.path.join(export_dir, filename + '.dxf'), r'--units=25.4/96', os.path.join(export_dir, filename) ] proc = Popen(cmd, shell=False, stdout=PIPE, stderr=PIPE) stdout, stderr = proc.communicate() #inkex.utils.debug("%d %s %s" % (proc.returncode, stdout, stderr)) if self.options.export_pdf is True: cli_output = inkscape( os.path.join(export_dir, filename), actions= 'export-pdf-version:1.5;export-text-to-path;export-filename:{file_name};export-do;FileClose' .format(file_name=os.path.join(export_dir, filename + '.pdf'))) if len(cli_output) > 0: self.msg( "Inkscape returned the following output when trying to run the file export; the file export may still have worked:" ) self.msg(cli_output)
def effect(self): doc_w = self.svg.unittouu(self.document.getroot().get('width')) doc_h = self.svg.unittouu(self.document.getroot().get('height')) box_w = self.svg.unittouu(str(self.options.width) + self.options.unit) box_h = self.svg.unittouu(str(self.options.height) + self.options.unit) box_d = self.svg.unittouu(str(self.options.depth) + self.options.unit) tab_h = box_d * self.options.proportion box_id = self.svg.get_unique_id('box') group = self.svg.get_current_layer().add(inkex.Group(id=box_id)) line_style = { 'stroke': '#000000', 'fill': 'none', 'stroke-width': str(self.svg.unittouu('1px')) } self.guide(doc_h, True) # Inner Close Tab line = group.add(inkex.PathElement(id=box_id + '-inner-close-tab')) line.path = [['M', [box_w - (tab_h * 0.7), 0]], [ 'C', [ box_w - (tab_h * 0.25), 0, box_w, tab_h * 0.3, box_w, tab_h * 0.9 ] ], ['L', [box_w, tab_h]], ['L', [0, tab_h]], ['L', [0, tab_h * 0.9]], ['C', [0, tab_h * 0.3, tab_h * 0.25, 0, tab_h * 0.7, 0]], ['Z', []]] line.style = line_style lower_pos = box_d + tab_h left_pos = 0 self.guide(doc_h - tab_h, True) # Upper Close Tab line = group.add(inkex.PathElement(id=box_id + '-upper-close-tab')) line.path = [['M', [left_pos, tab_h]], ['L', [left_pos + box_w, tab_h]], ['L', [left_pos + box_w, lower_pos]], ['L', [left_pos + 0, lower_pos]], ['Z', []]] line.style = line_style left_pos += box_w # Upper Right Tab side_tab_h = lower_pos - (box_w / 2) if side_tab_h < tab_h: side_tab_h = tab_h line = group.add(inkex.PathElement(id=box_id + '-upper-right-tab')) line.path = [ ['M', [left_pos, side_tab_h]], ['L', [left_pos + (box_d * 0.8), side_tab_h]], ['L', [left_pos + box_d, ((lower_pos * 3) - side_tab_h) / 3]], ['L', [left_pos + box_d, lower_pos]], ['L', [left_pos + 0, lower_pos]], ['Z', []] ] line.style = line_style left_pos += box_w + box_d # Upper Left Tab line = group.add(inkex.PathElement(id=box_id + '-upper-left-tab')) line.path = [['M', [left_pos + box_d, side_tab_h]], ['L', [left_pos + (box_d * 0.2), side_tab_h]], ['L', [left_pos, ((lower_pos * 3) - side_tab_h) / 3]], ['L', [left_pos, lower_pos]], ['L', [left_pos + box_d, lower_pos]], ['Z', []]] line.style = line_style left_pos = 0 self.guide(doc_h - tab_h - box_d, True) # Right Tab line = group.add(inkex.PathElement(id=box_id + '-left-tab')) line.path = [ ['M', [left_pos, lower_pos]], ['L', [left_pos - (box_d / 2), lower_pos + (box_d / 4)]], ['L', [left_pos - (box_d / 2), lower_pos + box_h - (box_d / 4)]], ['L', [left_pos, lower_pos + box_h]], ['Z', []] ] line.style = line_style # Front line = group.add(inkex.PathElement(id=box_id + '-front')) line.path = [['M', [left_pos, lower_pos]], ['L', [left_pos + box_w, lower_pos]], ['L', [left_pos + box_w, lower_pos + box_h]], ['L', [left_pos, lower_pos + box_h]], ['Z', []]] line.style = line_style left_pos += box_w # Right line = group.add(inkex.PathElement(id=box_id + '-right')) line.path = [['M', [left_pos, lower_pos]], ['L', [left_pos + box_d, lower_pos]], ['L', [left_pos + box_d, lower_pos + box_h]], ['L', [left_pos, lower_pos + box_h]], ['Z', []]] line.style = line_style left_pos += box_d # Back line = group.add(inkex.PathElement(id=box_id + '-back')) line.path = [['M', [left_pos, lower_pos]], ['L', [left_pos + box_w, lower_pos]], ['L', [left_pos + box_w, lower_pos + box_h]], ['L', [left_pos, lower_pos + box_h]], ['Z', []]] line.style = line_style left_pos += box_w # Left line = group.add(inkex.PathElement(id=box_id + '-line')) line.path = [['M', [left_pos, lower_pos]], ['L', [left_pos + box_d, lower_pos]], ['L', [left_pos + box_d, lower_pos + box_h]], ['L', [left_pos, lower_pos + box_h]], ['Z', []]] line.style = line_style lower_pos += box_h left_pos = 0 b_tab = lower_pos + box_d if b_tab > box_w / 2.5: b_tab = box_w / 2.5 # Bottom Front Tab line = group.add(inkex.PathElement(id=box_id + '-bottom-front-tab')) line.path = [['M', [left_pos, lower_pos]], ['L', [left_pos, lower_pos + (box_d / 2)]], ['L', [left_pos + box_w, lower_pos + (box_d / 2)]], ['L', [left_pos + box_w, lower_pos]], ['Z', []]] line.style = line_style left_pos += box_w # Bottom Right Tab line = group.add(inkex.PathElement(id=box_id + '-bottom-right-tab')) line.path = [['M', [left_pos, lower_pos]], ['L', [left_pos, lower_pos + b_tab]], ['L', [left_pos + box_d, lower_pos + b_tab]], ['L', [left_pos + box_d, lower_pos]], ['Z', []]] line.style = line_style left_pos += box_d # Bottom Back Tab line = group.add(inkex.PathElement(id=box_id + '-bottom-back-tab')) line.path = [['M', [left_pos, lower_pos]], ['L', [left_pos, lower_pos + (box_d / 2)]], ['L', [left_pos + box_w, lower_pos + (box_d / 2)]], ['L', [left_pos + box_w, lower_pos]], ['Z', []]] line.style = line_style left_pos += box_w # Bottom Left Tab line = group.add(inkex.PathElement(id=box_id + '-bottom-left-tab')) line.path = [['M', [left_pos, lower_pos]], ['L', [left_pos, lower_pos + b_tab]], ['L', [left_pos + box_d, lower_pos + b_tab]], ['L', [left_pos + box_d, lower_pos]], ['Z', []]] line.style = line_style left_pos += box_d lower_pos += b_tab group.transform = inkex.Transform(translate=((doc_w - left_pos) / 2, (doc_h - lower_pos) / 2))
def effect(self): if not self.svg.selected: return export_dir = Path(self.absolute_href(self.options.export_dir)) os.makedirs(export_dir, exist_ok=True) bbox = inkex.BoundingBox() for elem in self.svg.selected.values(): transform = inkex.Transform() parent = elem.getparent() if parent is not None and isinstance(parent, inkex.ShapeElement): transform = parent.composed_transform() try: bbox += elem.bounding_box(transform) except Exception: logger.exception("Bounding box not computed") logger.info("Skipping bounding box") transform = elem.composed_transform() x1, y1 = transform.apply_to_point([0, 0]) x2, y2 = transform.apply_to_point([1, 1]) bbox += inkex.BoundingBox((x1, x2), (y1, y2)) template = self.create_document() filename = None group = etree.SubElement(template, '{http://www.w3.org/2000/svg}g') group.attrib['id'] = GROUP_ID group.attrib['transform'] = str( inkex.Transform(((1, 0, -bbox.left), (0, 1, -bbox.top)))) for elem in self.svg.selected.values(): elem_copy = deepcopy(elem) elem_copy.attrib['transform'] = str(elem.composed_transform()) group.append(elem_copy) width = math.ceil(bbox.width) height = math.ceil(bbox.height) template.attrib['viewBox'] = f'0 0 {width} {height}' template.attrib['width'] = f'{width}' + self.svg.unit template.attrib['height'] = f'{height}' + self.svg.unit if filename is None: filename = elem.attrib.get('id', None) if filename: filename = filename.replace(os.sep, '_') + '.svg' if not filename: filename = 'element.svg' template.append(group) if not self.options.wrap_transform: self.load( inkex.command.inkscape_command(template.tostring(), select=GROUP_ID, verbs=['SelectionUnGroup'])) template = self.svg for child in template.getchildren(): if child.tag == '{http://www.w3.org/2000/svg}metadata': template.remove(child) self.save_document(template, export_dir / filename)
def container_transform(self): transform = super(WireframeSphere, self).container_transform() if self.options.TILT < 0: transform *= inkex.Transform(scale=(1, -1)) return transform