def effect(self): if len(self.svg.selection) != 2: raise inkex.AbortExtension(_("You must select two objects only.")) obj, envelope = self.svg.selection if isinstance(obj, (inkex.PathElement, inkex.Group)): if isinstance(envelope, inkex.PathElement): # Get bounding box plus any extra composed transform of parents. bbox = obj.bounding_box(obj.getparent().composed_transform()) # distill trafo into four node points path = envelope.path.transform(envelope.composed_transform()).to_superpath() tbox = self.envelope_box_from_path(path) else: if isinstance(envelope, inkex.Group): raise inkex.AbortExtension(_("The second selected object is a group, not a" " path.\nTry using Object->Ungroup.")) raise inkex.AbortExtension(_("The second selected object is not a path.\nTry using" " the procedure Path->Object to Path.")) else: raise inkex.AbortExtension(_("The first selected object is neither a path nor a group.\nTry using" " the procedure Path->Object to Path.")) self.process_object(obj, tbox, bbox)
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 envelope_box_from_path(self, envelope_path): if len(envelope_path) < 1 or len(envelope_path[0]) < 4: raise inkex.AbortExtension(_("Second selected path is too short. Must be four or more nodes.")) trafo = [[(csp[1][0], csp[1][1]) for csp in subs] for subs in envelope_path][0][:4] #vectors pointing away from the trafo origin tbox = [ DirectedLineSegment(trafo[0], trafo[1]), DirectedLineSegment(trafo[1], trafo[2]), DirectedLineSegment(trafo[3], trafo[2]), DirectedLineSegment(trafo[0], trafo[3]), ] vects = [segment.vector for segment in tbox] if 0.0 == vects[0].cross(vects[1]) == vects[1].cross(vects[2]) == vects[2].cross(vects[3]): raise inkex.AbortExtension(_("The points for the selected envelope must not all be in a line.")) return tbox
def gen_fce(self, obj, st, poly, transformed_pts): """Generate face""" so = self.options # colour tuple for the face fill fill_col = (so.f_r, so.f_g, so.f_b) # unit light vector lighting = normalise((so.lv_x, -so.lv_y, so.lv_z)) # we have a face list if obj.fce: z_list = [] for i, face in enumerate(obj.fce): # get the normal vector to the face norm = get_unit_normal(transformed_pts, face, so.cw_wound) # get the angle between the normal and the lighting vector angle = acos(numpy.dot(norm, lighting)) z_sort_param = so.z_sort(transformed_pts, face) # include all polygons or just the front-facing ones as needed if so.back or norm[2] > 0: # record the maximum z-value of the face and angle to # light, along with the face ID and normal z_list.append((z_sort_param, angle, norm, i)) z_list.sort(key=lambda x: x[0]) # sort by ascending sort parameter of the face draw_faces(z_list, transformed_pts, obj, so.shade, fill_col, st, poly) else: # we cannot generate a list of faces from the edges without a lot of computation raise inkex.AbortExtension("Face data not found.")
def effect(self): # get number of digits prec = int(self.options.precision) scale = self.svg.unittouu('1px') # convert to document units self.options.offset *= scale factor = 1.0 if self.svg.get('viewBox'): factor = self.svg.scale / self.svg.unittouu('1px') self.options.fontsize /= factor factor *= scale / self.svg.unittouu('1' + self.options.unit) # loop over all selected paths for node in self.svg.selection.filter(inkex.PathElement): csp = node.path.transform(node.composed_transform()).to_superpath() if self.options.mtype == "length": slengths, stotal = csplength(csp) self.group = node.getparent().add(TextElement()) elif self.options.mtype == "area": stotal = abs(csparea(csp) * factor * self.options.scale) self.group = node.getparent().add(TextElement()) else: try: xc, yc = cspcofm(csp) except ValueError as err: raise inkex.AbortExtension(str(err)) self.group = node.getparent().add(inkex.PathElement()) self.group.set('id', 'MassCenter_' + node.get('id')) self.add_cross(self.group, xc, yc, scale) continue # Format the length as string val = round(stotal * factor * self.options.scale, prec) self.options.method(node, str(val))
def effect(self): if debug: # SvgInputMixin __init__: "id:subpath:position of selected nodes, if any" print(self.options.selected_nodes, file=self.tty) self.radius = math.fabs(self.options.radius) self.cut = False if self.options.method in ('line'): self.cut = True if len(self.options.selected_nodes) < 1: # find selected objects and construct a list of selected_nodes for them... for p in self.options.ids: self.options.selected_nodes.extend( self.find_roundable_nodes(p)) if len(self.options.selected_nodes) < 1: raise inkex.AbortExtension( "Could not find nodes inside a path. No path objects selected?" ) if len(self.options.selected_nodes) == 1: # when we only trim one node, we can eat up almost everything, # no need to leave room for rounding neighbour nodes. self.max_trim_factor = max_trim_factor_single for node in sorted(self.options.selected_nodes): ## we walk through the list sorted, so that node indices are processed within a subpath in ascending numeric order. ## that makes adjusting index offsets after node inserts easier. ss = self.round_corner(node)
def process_value(val): """Confirm the values from files or direct""" val = float(val) if val < 0: raise inkex.AbortExtension( "Negative values are currently not supported!") return val
def exportNode(self, node, dpi=None, num='1', size=None): skip, kwargs = self.formatExport(node, dpi, num, size) if skip: raise inkex.AbortExtension("File is available in destination.\nPlease change directory location or make sure overwrite option is checked!") svgFile = self.options.input_file inkscape(svgFile, **kwargs)
def get_font_file(font_name): _win_font_dir = os.path.normpath("C:/windows/fonts/") if os_type == "Windows": _cmd = 'reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Fonts" /s' elif os_type == "Darwin": _cmd = '/usr/local/bin/fc-list' else: _cmd = 'fc-list' result = {} get_list_cmd = subprocess.Popen(_cmd, stdout=subprocess.PIPE) output, err = get_list_cmd.communicate() if err is None: for i in output.splitlines(): detail = re.split(':|,', str(i.decode("utf8"))) if os_type == "Windows": result[detail[0].strip()] = _win_font_dir + detail[1].strip() else: result[detail[1].strip()] = detail[0].strip() else: #inkex.errormsg(str(err)) #sys.exit(1) inkex.AbortExtension(str(err)) return result.get(font_name.replace("'","")) # Remove quotes around font name with space
def effect(self): self.ensureInkWebSupport() if len(self.options.ids) < 2: raise inkex.AbortExtension( _("You must select at least two elements.")) # All set the last else The first set all split = -1 if self.options.from_and_to == "g-to-one" else 1 el_from = list(self.svg.selected.values())[:split] id_to = list(self.svg.selected.ids)[split:] ev_code = "InkWeb.setAtt({{el:['{}'], att:'{}', val:'{}'}})".format( "','".join(id_to), self.options.att, self.options.val) for elem in el_from: prev_ev_code = elem.get(self.options.when) if prev_ev_code is None: prev_ev_code = "" if self.options.compatibility == 'append': el_ev_code = prev_ev_code + ";\n" + ev_code if self.options.compatibility == 'prepend': el_ev_code = ev_code + ";\n" + prev_ev_code if self.options.compatibility == 'replace': el_ev_code = ev_code elem.set(self.options.when, el_ev_code)
def generate(self): if numpy is None: raise inkex.AbortExtension("numpy is required.") so = self.options obj = WavefrontObj(self.get_filename()) scale = self.svg.unittouu('1px') # convert to document units st = Style(so) # initialise style # we will put all the rotations in the object name, so it can be repeated in poly = Group.new(obj.name + ':' + make_rotation_log(so)) (pos_x, pos_y) = self.svg.namedview.center poly.transform.add_translate(pos_x, pos_y) poly.transform.add_scale(scale) # TRANSFORMATION OF THE OBJECT (ROTATION, SCALE, ETC) trans_mat = numpy.identity(3, float) # init. trans matrix as identity matrix for i in range(1, 7): # for each rotation axis = getattr(so, 'r{}_ax'.format(i)) angle = getattr(so, 'r{}_ang'.format(i)) * pi / 180 trans_mat = rotate(trans_mat, angle, axis) # scale by linear factor (do this only after the transforms to reduce round-off) trans_mat = trans_mat * so.scl # the points as projected in the z-axis onto the viewplane transformed_pts = obj.get_transformed_pts(trans_mat) so.show(obj, st, poly, transformed_pts) return poly
def effect(self): self.is_installed() rect = self.svg.selected.first() if rect is None: raise inkex.AbortExtension( _("No object selected. Please select the object you want " "to assign a view to and then press apply.\n")) if not self.options.removeView: view_order = str(self.options.viewOrder) # Remove the view that currently has the requested order number. for node in rect.xpath( "ancestor::svg:g[@inkscape:groupmode='layer']" "/descendant::*[@jessyink:view]"): prop_dict = inkex.Style(node.get("jessyink:view")) if prop_dict["order"] == view_order: node.set("jessyink:view", None) # Set the new view. rect.set( "jessyink:view", inkex.Style( name="view", order=view_order, length=int(self.options.viewDuration * 1000), )) # Remove possible effect arguments. self.attr_remove('effectIn') self.attr_remove('effectOut') else: self.attr_remove('view')
def effect(self): # get hpgl data encoder = hpgl_encoder.hpglEncoder(self) try: self.options.to_port(self.options.to_language(encoder.getHpgl())) except hpgl_encoder.NoPathError: raise inkex.AbortExtension( _("No paths where found. Please convert objects to paths."))
def effect(self): if not self.svg.selected: raise inkex.AbortExtension("Please select an object.") for pathID in self.options.ids: path = self.svg.getElementById(pathID) #assume default namespace for d-attribute path.set("d", path.original_path.to_relative())
def is_installed(self): """Check jessyInk is installed correctly""" scripts = self.svg.getElement("//svg:script[@jessyink:version='1.5.5']") if scripts is None: raise inkex.AbortExtension(_( "The JessyInk script is not installed in this SVG file or has a " "different version than the JessyInk extensions. Please select " "\"install/update...\" from the \"JessyInk\" sub-menu of the \"Extensions\" " "menu to install or update the JessyInk script.\n\n"))
def call(self, input_file, output_file): cp = os.path.dirname( os.path.abspath(__file__)) + "/svg-embed-and-crop/*" java = "javaw -cp \"" command.call('javaw', '-cp', cp, 'edu.emory.cellbio.svg.EmbedAndCropInkscapeEntry', input_file, "-o", output_file) if not os.path.exists(output_file): raise inkex.AbortExtension("Plugin canceled") return output_file
def effect(self): self.is_installed() if not self.svg.selected: raise inkex.AbortExtension( _("No object selected. Please select the object you want to " "assign an effect to and then press apply.\n")) for elem in self.svg.selected.values(): self._process(elem, 'effectIn') self._process(elem, 'effectOut')
def effect(self): scale = self.svg.unittouu('1px') # convert to document units self.options.xoffset *= scale self.options.yoffset *= scale if not self.svg.selected: raise inkex.AbortExtension("Please select an object") if self.options.type == "geometric": bbox = self.svg.selection.bounding_box() else: bbox = self.svg.selection.first().bounding_box() layer = self.svg.get_current_layer() self.add_marker('Arrow1Lstart', False) self.add_marker('Arrow1Lend', True) group = Group() layer.append(group) group.set('fill', 'none') group.set('stroke', 'black') line = self.horz_line(bbox.top, [0, 1], bbox) line.set('marker-start', 'url(#Arrow1Lstart)') line.set('marker-end', 'url(#Arrow1Lend)') line.set('stroke-width', str(scale)) group.append(line) line = self.vert_line(bbox.left, [0, 2], bbox) line.set('stroke-width', str(0.5 * scale)) group.append(line) line = self.vert_line(bbox.right, [0, 2], bbox) line.set('stroke-width', str(0.5 * scale)) group.append(line) line = self.vert_line(bbox.left, [1, 0], bbox) line.set('marker-start', 'url(#Arrow1Lstart)') line.set('marker-end', 'url(#Arrow1Lend)') line.set('stroke-width', str(scale)) group.append(line) line = self.horz_line(bbox.top, [2, 0], bbox) line.set('stroke-width', str(0.5 * scale)) group.append(line) line = self.horz_line(bbox.bottom, [2, 0], bbox) line.set('stroke-width', str(0.5 * scale)) group.append(line) for node in self.svg.selected.values(): group.append(node) layer.append(group) return None
def pixel_snap(self, elem, parent_transform=None): if not isinstance(elem, (Group, Image, Rectangle, PathElement)): return if isinstance(elem, Group): self.snap_transform(elem) transform = elem.transform * Transform(parent_transform) for child in elem: try: self.pixel_snap(child, transform) except TransformError as err: raise inkex.AbortExtension(str(err)) return # If we've been given a parent_transform, we can assume that the # parents have already been snapped, or don't need to be if self.options.snap_ancestors and parent_transform is None: # Loop through ancestors from outermost to innermost, excluding this element. for child in elem.ancestors(): self.snap_transform(child) # If we haven't been given a parent_transform, then we need to calculate it if self.options.ancestor_offset and parent_transform is None: if isinstance(elem.getparent(), ShapeElement): parent_transform = elem.getparent().composed_transform() self.snap_transform(elem) try: self.snap_stroke(elem, parent_transform) except TransformError as err: raise inkex.AbortExtension(str(err)) if isinstance(elem, PathElement): self.snap_path_scale(elem, parent_transform) self.snap_path_pos(elem, parent_transform) self.snap_path( elem, parent_transform ) # would be quite useful to make this an option, as scale/pos alone doesn't mess with the path itself, and works well for sans-serif text elif isinstance(elem, Rectangle): self.snap_rect(elem, parent_transform) elif isinstance(elem, Image): self.snap_image(elem, parent_transform)
def _sekl_call(self, skeletons, p0, dx, bbox): if self.options.vertical: flipxy(p0) newp = [] for skelnode in skeletons.values(): self.curSekeleton = skelnode.path.to_superpath() if self.options.vertical: flipxy(self.curSekeleton) for comp in self.curSekeleton: path = copy.deepcopy(p0) self.skelcomp, self.lengths = linearize(comp) # !!!!>----> TODO: really test if path is closed! end point==start point is not enough! self.skelcompIsClosed = (self.skelcomp[0] == self.skelcomp[-1]) length = sum(self.lengths) xoffset = self.skelcomp[0][ 0] - bbox.x.minimum + self.options.toffset yoffset = self.skelcomp[0][ 1] - bbox.y.center - self.options.noffset if self.options.repeat: NbCopies = max( 1, int(round((length + self.options.space) / dx))) width = dx * NbCopies if not self.skelcompIsClosed: width -= self.options.space bbox.x.maximum = bbox.x.minimum + width new = [] for sub in path: for _ in range(NbCopies): new.append(copy.deepcopy(sub)) offset(sub, dx, 0) path = new for sub in path: offset(sub, xoffset, yoffset) if self.options.stretch: if not bbox.width: raise inkex.AbortExtension( "The 'stretch' option requires that the pattern must have non-zero width :\nPlease edit the pattern width." ) for sub in path: stretch(sub, length / bbox.width, 1, self.skelcomp[0]) for sub in path: for ctlpt in sub: self.apply_diffeomorphism(ctlpt[1], (ctlpt[0], ctlpt[2])) if self.options.vertical: flipxy(path) newp += path return CubicSuperPath(newp)
def generate(self): scale = self.svg.unittouu('1px') # convert to document units opt = self.options if not opt.text: raise inkex.AbortExtension('Please enter an input text') elif opt.drawtype == "symbol" and opt.symbolid == "": raise inkex.AbortExtension('Please enter symbol id') # for Python 3 ugly hack to represent bytes as str for Python2 compatibility text_bytes = bytes(opt.text, opt.encoding).decode("latin_1") text_str = str(opt.text) grp = Group() grp.set('inkscape:label', 'QR Code: ' + text_str) if opt.groupid: grp.set('id', opt.groupid) pos_x, pos_y = self.svg.namedview.center grp.transform.add_translate(pos_x, pos_y) if scale: grp.transform.add_scale(scale) # GENERATE THE QRCODE if opt.typenumber == 0: # Automatic QR code size` code = QRCode.getMinimumQRCode(text_bytes, opt.correctionlevel) else: # Manual QR code size code = QRCode(correction=opt.correctionlevel) code.setTypeNumber(int(opt.typenumber)) code.addData(text_bytes) code.make() self.boxsize = opt.modulesize self.invert_code = opt.invert self.margin = 4 self.draw = GridDrawer(opt.invert, opt.smoothval) self.draw.set_grid(code.modules) self.render_svg(grp, opt.drawtype) return grp
def effect(self): svg = self.document.getroot() self.document_offset = self.svg.unittouu( svg.attrib['height'] ) % 1 # although SVG units are absolute, the elements are positioned relative to the top of the page, rather than zero for id, elem in self.svg.selected.items(): try: self.pixel_snap(elem) except TransformError as err: raise inkex.AbortExtension(str(err))
def effect(self): if not self.svg.selected: raise inkex.AbortExtension( _('You must to select some "Slicer rectangles" ' 'or other "Layout groups".')) base_elements = self.get_slicer_layer().descendants() for key, node in self.svg.selected.id_dict().items(): if node not in base_elements: raise inkex.AbortExtension( _(f'The element "{key}" is not in the Web Slicer layer')) g_parent = node.getparent() group = g_parent.add(inkex.Group()) desc = group.add(inkex.Desc()) desc.text = self.get_conf_text_from_list([ 'html_id', 'html_class', 'width_unity', 'height_unity', 'bg_color' ]) for node in self.svg.selected.values(): group.insert(1, node)
def effect(self): if (len(self.options.ids) == 0): raise inkex.AbortExtension("Please select a path to reverse") # Take a first path segment for pathID in self.options.ids: path = self.svg.getElementById(pathID).original_path inkex.utils.debug(path) reversedPath = path.reverse() inkex.utils.debug(reversedPath) self.svg.getElementById(pathID).set("d", reversedPath)
def effect(self): if not self.svg.selected: raise inkex.AbortExtension("There is no selection to restack.") # process selection to get list of objects to be arranged parentnode = None for node in self.svg.selection.filter(SvgDocumentElement): parentnode = node self.svg.set_selection(*list(node)) if parentnode is None: parentnode = self.svg.get_current_layer() self.options.tab(parentnode)
def get_data(self): """Process the data""" col_key = self.options.col_key col_val = self.options.col_val def process_value(val): """Confirm the values from files or direct""" val = float(val) if val < 0: raise inkex.AbortExtension( "Negative values are currently not supported!") return val if self.options.input_type == "file": if self.options.filename is None: raise inkex.AbortExtension("Filename not specified!") # Future: use encoding when opening the file here (if ever needed) with open(self.options.filename, "r") as fhl: reader = csv.reader(fhl, delimiter=self.options.delimiter) title = col_val if self.options.headings: header = next(reader) title = header[col_val] values = [(line[col_key], process_value(line[col_val])) for line in reader] return (title, ) + tuple(zip(*values)) elif self.options.input_type == "direct_input": (keys, values) = zip( *[l.split(':', 1) for l in self.options.what.split(',')]) return ('Direct Input', keys, [process_value(val) for val in values]) raise inkex.AbortExtension("Unknown input type")
def effect(self): opt = self.options if not opt.placeholderid: raise inkex.AbortExtension('Please enter the placeholder ID') elif not opt.qrcodeid: raise inkex.AbortExtension('Please enter the QRCode ID') placeholder = self.svg.getElementById(opt.placeholderid) qrcode = self.svg.getElementById(opt.qrcodeid) if placeholder is None or qrcode is None: # Delete the generated qrcode qrcode.getparent().remove(qrcode) return # Reset scale before processing qrcode.set('transform', 'scale(1,1)') # Get scaling factors scalex = placeholder.bounding_box().width / qrcode.bounding_box().width scaley = placeholder.bounding_box().height / qrcode.bounding_box().height # Apply scaling and translating tr = Transform() tr.add_translate(placeholder.bounding_box().left, placeholder.bounding_box().top) qrcode.set('transform', tr) tr.add_scale(scalex, scaley) qrcode.set('transform', tr) # Move qrcode inplace of the placeholder placeholder.getparent().append(qrcode) # Delete the placeholder placeholder.getparent().remove(placeholder)
def effect(self): self.ensureInkWebSupport() if len(self.options.ids) < 2: raise inkex.AbortExtension( "You must select at least two elements. The last one is the object you want to go to." ) el_from = list(self.svg.selected.values())[:-1] ev_code = "InkWeb.moveViewbox({from:this, to:'" + self.options.ids[ -1] + "'})" for elem in el_from: prev_ev_code = elem.get(self.options.when) el_ev_code = ev_code + ";" + (prev_ev_code or '') elem.set(self.options.when, el_ev_code)
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 effect(self): if not self.svg.selected: raise inkex.AbortExtension("Please select an object.") self.threshold = float(self.options.threshold) for pathID in self.options.ids: path = self.svg.getElementById(pathID) cleanedPath = inkex.paths.Path() for segment in path.original_path.to_relative(): if self.getCommandLength(segment) > self.threshold: cleanedPath.append(segment) inkex.utils.debug(cleanedPath) path.set("d", cleanedPath)