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 draw_rect(x, y, w, h, stroke_width, fill, name): elem = Rectangle(x=str(x), y=str(y), width=str(w), height=str(h)) elem.style = { 'stroke': '#000000', 'stroke-width': str(stroke_width), 'fill': fill } elem.set('inkscape:label', name) return elem
def generateRectangle(self, x, y, w, h, strokeWidth, stroke, fill, name): rect = Rectangle(x=str(x), y=str(y), width=str(w), height=str(h)) rect.style = { 'stroke': stroke, 'stroke-width': strokeWidth, 'fill': fill } rect.label = name return rect
def test_regular_rectangle_with_stroke(self): x, y = 10, 20 w, h = 7, 20 stroke_half_width = 1 rect = Rectangle(width=str(w), height=str(h), x=str(x), y=str(y)) rect.style = Style("stroke-width:{};stroke:red".format(stroke_half_width * 2)) self.assert_bounding_box_is_equal(rect, (x - stroke_half_width, x + w + stroke_half_width), (y - stroke_half_width, y + h + stroke_half_width))
def test_group_with_number_of_rects_translated(self): group = Group() dx, dy = 5, 10 xmin, ymin = 1000, 1000 xmax, ymax = -1000, -1000 rects = [] for x, y, w, h in [ (10, 20, 5, 7), (30, 40, 5, 7), ]: rect = Rectangle(width=str(w), height=str(h), x=str(x), y=str(y)) rects.append(rect) xmin = min(xmin, x) xmax = max(xmax, x + w) ymin = min(ymin, y) ymax = max(ymax, y + h) group.add(rect) group.transform = Transform(translate=(dx, dy)) self.assert_bounding_box_is_equal(group, (dx + xmin, dx + xmax), (dy + ymin, dy + ymax))
def test_rectangle_without_coordinates(self): x, y = 0, 0 w, h = 7, 20 rect = Rectangle(width=str(w), height=str(h)) self.assert_bounding_box_is_equal(rect, (x, x + w), (y, y + h))
def test_regular_rectangle(self): x, y = 10, 20 w, h = 7, 20 rect = Rectangle(width=str(w), height=str(h), x=str(x), y=str(y)) self.assert_bounding_box_is_equal(rect, (x, x + w), (y, y + h))
def test_regular_rectangle_scaled(self): x, y = 10, 20 w, h = 7, 20 scale_x = 2 scale_y = 3 rect = Rectangle(width=str(w), height=str(h), x=str(x), y=str(y)) rect.transform = Transform(scale=(scale_x, scale_y)) self.assert_bounding_box_is_equal(rect, (scale_x * x, scale_x * (x + w)), (scale_y * y, scale_y * (y + h)))
def test_rectangle_without_dimensions(self): x, y = 10, 15 w, h = 0, 0 rect = Rectangle(x=str(x), y=str(y)) self.assert_bounding_box_is_equal(rect, (x, x + w), (y, y + h))
def test_regular_rectangle_with_stroke_scaled(self): x, y = 10, 20 w, h = 7, 20 stroke_half_width = 1 scale_x = 2 scale_y = 3 rect = Rectangle(width=str(w), height=str(h), x=str(x), y=str(y)) rect.style = Style("stroke-width:{};stroke:red".format(stroke_half_width * 2)) rect.transform = Transform(scale=(scale_x, scale_y)) self.assert_bounding_box_is_equal(rect, (scale_x * (x - stroke_half_width), scale_x * (x + w + stroke_half_width)), (scale_y * (y - stroke_half_width), scale_y * (y + h + stroke_half_width)))
def draw_rect(x, y, w, h, width, fill, name, parent): """Draw an SVG Rectangle""" rect = parent.add( Rectangle(x=str(x), y=str(y), width=str(w), height=str(h))) rect.style = { 'stroke': '#000000', 'stroke-width': str(width), 'fill': fill } rect.label = name
def test_group_with_regular_rect(self): group = Group() x, y = 10, 20 w, h = 7, 20 rect = Rectangle(width=str(w), height=str(h), x=str(x), y=str(y)) group.add(rect) self.assert_bounding_box_is_equal(group, (x, x + w), (y, y + h))
def test_group_nested_transform(self): group = Group() x, y = 10, 20 w, h = 7, 20 scale = 2 rect = Rectangle(width=str(w), height=str(h), x=str(x), y=str(y)) rect.transform = Transform(rotate=45, scale=scale) group.add(rect) group.transform = Transform(rotate=-45) # rotation is compensated, but scale is not a = rect.composed_transform() self.assert_bounding_box_is_equal(group, (scale * x, scale * (x + w)), (scale * y, scale * (y + h)))
def test_parse(self): """Test Rectangle parsed from XML""" rect = Rectangle(attrib={ "x": "10px", "y": "20px", "width": "100px", "height": "200px", "rx": "15px", "ry": "30px" }) self.assertEqual(rect.left, 10) self.assertEqual(rect.top, 20) self.assertEqual(rect.right, 10+100) self.assertEqual(rect.bottom, 20+200) self.assertEqual(rect.width, 100) self.assertEqual(rect.height, 200) self.assertEqual(rect.rx, 15) self.assertEqual(rect.ry, 30)
def generate(self): size = str(self.options.size) style = inkex.Style({ 'stroke': 'none', 'stroke-width': '1', 'fill': '#000000' }) attribs = {'style': str(style), 'height': size, 'width': size} if not self.options.text: raise inkex.AbortExtension("Please enter an input string.") # create a 2d list corresponding to the 1's and 0s of the DataMatrix encoded = self.encode(self.options.text, *self.options.symbol) for x, y in self.render_data_matrix(encoded): attribs.update({'x': str(x), 'y': str(y)}) yield Rectangle(**attribs)
def draw_rectangle(self, x, y, width, height): """Draw a rectangle bar with optional shadow""" if self.blur: shadow = Rectangle(x=str(x + 1), y=str(y + 1), width=str(width), height=str(height)) shadow.style = self.blur yield shadow rect = Rectangle(x=str(x), y=str(y), width=str(width), height=str(height)) rect.set("style", "fill:" + self.get_color()) yield rect
def render_svg(self, grp, drawtype): """Render to svg""" drawer = getattr(self, f"render_{drawtype}", self.render_obsolete) if drawer is None: raise Exception("Unknown draw type: " + drawtype) canvas_width = (self.draw.col_count() + 2 * self.margin) * self.boxsize canvas_height = (self.draw.row_count() + 2 * self.margin) * self.boxsize # white background providing margin: rect = grp.add(Rectangle.new(0, 0, canvas_width, canvas_height)) rect.style['stroke'] = 'none' rect.style['fill'] = "black" if self.invert_code else "white" qrg = grp.add(Group()) qrg.style['stroke'] = 'none' qrg.style['fill'] = "white" if self.invert_code else "black" qrg.add(drawer())
def effect(self): if not self.svg.selected: for node in self.svg.xpath('//svg:text | //svg:flowRoot'): self.svg.selected[node.get('id')] = node if not self.svg.selected: return parentnode = self.svg.get_current_layer() if self.options.flowtext: text_element = FlowRoot text_span = FlowPara else: text_element = TextElement text_span = Tspan text_root = parentnode.add(text_element()) text_root.set('xml:space', 'preserve') text_root.style = { 'font-size': '20px', 'font-style': 'normal', 'font-weight': 'normal', 'line-height': '125%', 'letter-spacing': '0px', 'word-spacing': '0px', 'fill': '#000000', 'fill-opacity': 1, 'stroke': 'none' } for node in sorted(self.svg.selected.values(), key=self._sort): self.recurse(text_span, node, text_root) if self.options.flowtext: region = text_root.add(FlowRegion()) region.set('xml:space', 'preserve') rect = region.add(Rectangle()) rect.set('xml:space', 'preserve') rect.set('height', 200) rect.set('width', 200)
def test_group_with_number_of_rects(self): group = Group() xmin, ymin = 1000, 1000 xmax, ymax = -1000, -1000 rects = [] for x, y, w, h in [ (10, 20, 5, 7), (30, 40, 5, 7), ]: rect = Rectangle(width=str(w), height=str(h), x=str(x), y=str(y)) rects.append(rect) xmin = min(xmin, x) xmax = max(xmax, x + w) ymin = min(ymin, y) ymax = max(ymax, y + h) group.append(rect) self.assert_bounding_box_is_equal(group, (xmin, xmax), (ymin, ymax))
def test_new_group(self): """Test creating groups""" svg = Layer.new('layerA', Group.new('groupA', Rectangle())) self.assertElement(svg,\ b'<g inkscape:groupmode="layer" inkscape:label="layerA">'\ b'<g inkscape:label="groupA"><rect/></g></g>')
def generate_nup(self, unit="px", pgSize=("8.5*96", "11*96"), pgMargin=(0, 0), pgPadding=(0, 0), num=(2, 2), calculateSize=True, size=None, margin=(0, 0), padding=(20, 20), show=['default'], ): """Generate the SVG. Inputs are run through 'eval(str(x))' so you can use '8.5*72' instead of 612. Margin / padding dimension tuples can be (top & bottom, left & right) or (top, right, bottom, left). Keyword arguments: pgSize -- page size, width x height pgMargin -- extra space around each page pgPadding -- added to pgMargin n -- rows x cols size -- override calculated size, width x height margin -- white space around each piece padding -- inner padding for each piece show -- list of keywords indicating what to show - 'crosses' - cutting guides - 'inner' - inner boundary - 'outer' - outer boundary """ if 'default' in show: show = set(show).union(['inner', 'innerbox', 'holder', 'crosses']) pgMargin = self.expandTuple(unit, pgMargin) pgPadding = self.expandTuple(unit, pgPadding) margin = self.expandTuple(unit, margin) padding = self.expandTuple(unit, padding) pgSize = self.expandTuple(unit, pgSize, length=2) # num = tuple(map(lambda ev: eval(str(ev)), num)) if not pgMargin or not pgPadding: return inkex.errormsg("No padding or margin available.") page_edge = list(map(sum, zip(pgMargin, pgPadding))) top, right, bottom, left = 0, 1, 2, 3 width, height = 0, 1 rows, cols = 0, 1 size = self.expandTuple(unit, size, length=2) if size is None or calculateSize or len(size) < 2 or size[0] == 0 or size[1] == 0: size = ((pgSize[width] - page_edge[left] - page_edge[right] - num[cols] * (margin[left] + margin[right])) / num[cols], (pgSize[height] - page_edge[top] - page_edge[bottom] - num[rows] * (margin[top] + margin[bottom])) / num[rows] ) else: size = self.expandTuple(unit, size, length=2) # sep is separation between same points on pieces sep = (size[width] + margin[right] + margin[left], size[height] + margin[top] + margin[bottom]) style = 'stroke:#000000;stroke-opacity:1;fill:none;fill-opacity:1;' padbox = Rectangle( x=str(page_edge[left] + margin[left] + padding[left]), y=str(page_edge[top] + margin[top] + padding[top]), width=str(size[width] - padding[left] - padding[right]), height=str(size[height] - padding[top] - padding[bottom]), style=style, ) margbox = Rectangle( x=str(page_edge[left] + margin[left]), y=str(page_edge[top] + margin[top]), width=str(size[width]), height=str(size[height]), style=style, ) doc = self.get_template(width=pgSize[width], height=pgSize[height]) svg = doc.getroot() def make_clones(under, to): for row in range(0, num[rows]): for col in range(0, num[cols]): if row == 0 and col == 0: continue use = under.add(Use()) use.set('xlink:href', '#' + to) use.transform.add_translate(col * sep[width], row * sep[height]) # guidelayer ##################################################### if {'inner', 'outer'}.intersection(show): layer = svg.add(inkex.Layer.new('Guide Layer')) if 'inner' in show: ibox = layer.add(padbox.copy()) ibox.style['stroke'] = '#8080ff' ibox.set('id', 'innerguide') make_clones(layer, 'innerguide') if 'outer' in show: obox = layer.add(margbox.copy()) obox.style['stroke'] = '#8080ff' obox.set('id', 'outerguide') make_clones(layer, 'outerguide') # crosslayer ##################################################### if {'crosses'}.intersection(show): layer = svg.add(inkex.Layer.new('Cut Layer')) if 'crosses' in show: crosslen = 12 group = layer.add(inkex.Group(id='cross')) x, y = 0, 0 path = 'M%f %f' % (x + page_edge[left] + margin[left], y + page_edge[top] + margin[top] - crosslen) path += ' L%f %f' % (x + page_edge[left] + margin[left], y + page_edge[top] + margin[top] + crosslen) path += ' M%f %f' % (x + page_edge[left] + margin[left] - crosslen, y + page_edge[top] + margin[top]) path += ' L%f %f' % (x + page_edge[left] + margin[left] + crosslen, y + page_edge[top] + margin[top]) group.add(inkex.PathElement(style=style + 'stroke-width:0.05', d=path, id='crossmarker')) for row in 0, 1: for col in 0, 1: if row or col: cln = group.add(Use()) cln.set('xlink:href', '#crossmarker') cln.transform.add_translate(col * size[width], row * size[height]) make_clones(layer, 'cross') # clonelayer ##################################################### layer = svg.add(inkex.Layer.new('Clone Layer')) make_clones(layer, 'main') # mainlayer ###################################################### layer = svg.add(inkex.Layer.new('Main Layer')) group = layer.add(inkex.Group(id='main')) if 'innerbox' in show: group.add(padbox) if 'outerbox' in show: group.add(margbox) if 'holder' in show: x, y = (page_edge[left] + margin[left] + padding[left], page_edge[top] + margin[top] + padding[top]) w, h = (size[width] - padding[left] - padding[right], size[height] - padding[top] - padding[bottom]) path = 'M{:f} {:f}'.format(x + w / 2., y) path += ' L{:f} {:f}'.format(x + w, y + h / 2.) path += ' L{:f} {:f}'.format(x + w / 2., y + h) path += ' L{:f} {:f}'.format(x, y + h / 2.) path += ' Z' group.add(inkex.PathElement(style=style, d=path)) return svg.tostring()
def render_stbar(self, keys, values): """Draw stacked bar chart""" # Iterate over all values to draw the different slices color = 0 # create value sum in order to divide the bars try: valuesum = sum(values) except ValueError: valuesum = 0.0 # Init offset offset = 0 width = self.options.bar_width height = self.options.bar_height x = self.width / 2 y = self.height / 2 if self.blur: if self.options.rotate: width, height = height, width shy = y else: shy = str(y - self.options.bar_height) # Create rectangle element shadow = Rectangle( x=str(x), y=str(shy), width=str(width), height=str(height), ) # Set shadow blur (connect to filter object in xml path) shadow.style = self.blur yield shadow # Draw Single bars for cnt, value in enumerate(values): # Calculate the individual heights normalized on 100units normedvalue = (self.options.bar_height / valuesum) * float(value) # Create rectangle element rect = Rectangle() # Set chart position to center of document. if not self.options.rotate: rect.set('x', str(self.width / 2)) rect.set('y', str(self.height / 2 - offset - normedvalue)) rect.set("width", str(self.options.bar_width)) rect.set("height", str(normedvalue)) else: rect.set('x', str(self.width / 2 + offset)) rect.set('y', str(self.height / 2)) rect.set("height", str(self.options.bar_width)) rect.set("width", str(normedvalue)) rect.set("style", "fill:" + self.get_color()) # If text is given, draw short paths and add the text # TODO: apply overlap workaround for visible gaps in between if keys: if not self.options.rotate: x1 = (self.width + self.options.bar_width) / 2 y1 = y - offset - (normedvalue / 2) x2 = self.options.bar_width / 2 + self.options.text_offset y2 = 0 txt = self.width / 2 + self.options.bar_width + self.options.text_offset + 1 tyt = y - offset + self.fontoff - (normedvalue / 2) else: x1 = x + offset + normedvalue / 2 y1 = y + self.options.bar_width / 2 x2 = 0 y2 = self.options.bar_width / 2 + (self.options.font_size \ * cnt) + self.options.text_offset txt = x + offset + normedvalue / 2 - self.fontoff tyt = (y) + self.options.bar_width + (self.options.font_size \ * (cnt + 1)) + self.options.text_offset elem = inkex.PathElement() elem.path = [Move(x1, y1), line(x2, y2)] elem.style = { 'fill': 'none', 'stroke': self.options.font_color, 'stroke-width': self.options.stroke_width, 'stroke-linecap': 'butt', } yield elem yield self.draw_text(keys[cnt], x=str(txt), y=str(tyt)) # Increase Offset and Color offset = offset + normedvalue color = (color + 1) % 8 # Draw rectangle yield rect yield self.draw_header(self.width / 2 + offset + normedvalue)
def effect(self): # Check that elements have been selected if not self.svg.selected: inkex.errormsg(_("Please select objects!")) return linestyle = { 'stroke': '#000000', 'stroke-width': str(self.svg.unittouu('1px')), 'fill': 'none', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' } facestyle = { 'stroke': '#000000', 'stroke-width': str(self.svg.unittouu('1px')), 'fill': 'none', 'stroke-linecap': 'round', 'stroke-linejoin': 'round' } parent_group = self.svg.selection.first().getparent() trans = parent_group.composed_transform() invtrans = None if trans: invtrans = -trans # Recovery of the selected objects pts = [] nodes = [] seeds = [] fills = [] for node in self.svg.selected.values(): nodes.append(node) bbox = node.bounding_box() if bbox: center_x, center_y = bbox.center point = [center_x, center_y] if trans: point = trans.apply_to_point(point) pts.append(Point(*point)) if self.options.delaunayFillOptions != "delaunay-no-fill": fills.append(node.style.get('fill', 'none')) seeds.append(Point(center_x, center_y)) # Creation of groups to store the result if self.options.diagramType != 'Delaunay': # Voronoi group_voronoi = parent_group.add(Group()) group_voronoi.set('inkscape:label', 'Voronoi') if invtrans: group_voronoi.transform *= invtrans if self.options.diagramType != 'Voronoi': # Delaunay group_delaunay = parent_group.add(Group()) group_delaunay.set('inkscape:label', 'Delaunay') # Clipping box handling if self.options.diagramType != 'Delaunay': # Clipping bounding box creation group_bbox = sum([node.bounding_box() for node in nodes], None) # Clipbox is the box to which the Voronoi diagram is restricted if self.options.clip_box == 'Page': svg = self.document.getroot() width = self.svg.unittouu(svg.get('width')) height = self.svg.unittouu(svg.get('height')) clip_box = (0, width, 0, height) else: clip_box = (group_bbox.left, group_bbox.right, group_bbox.top, group_bbox.bottom) # Safebox adds points so that no Voronoi edge in clip_box is infinite safe_box = (2 * clip_box[0] - clip_box[1], 2 * clip_box[1] - clip_box[0], 2 * clip_box[2] - clip_box[3], 2 * clip_box[3] - clip_box[2]) pts.append(Point(safe_box[0], safe_box[2])) pts.append(Point(safe_box[1], safe_box[2])) pts.append(Point(safe_box[1], safe_box[3])) pts.append(Point(safe_box[0], safe_box[3])) if self.options.showClipBox: # Add the clip box to the drawing rect = group_voronoi.add(Rectangle()) rect.set('x', str(clip_box[0])) rect.set('y', str(clip_box[2])) rect.set('width', str(clip_box[1] - clip_box[0])) rect.set('height', str(clip_box[3] - clip_box[2])) rect.style = linestyle # Voronoi diagram generation if self.options.diagramType != 'Delaunay': vertices, lines, edges = voronoi.computeVoronoiDiagram(pts) for edge in edges: vindex1, vindex2 = edge[1:] if (vindex1 < 0) or (vindex2 < 0): continue # infinite lines have no need to be handled in the clipped box else: segment = self.clip_edge(vertices, lines, edge, clip_box) # segment = [vertices[vindex1],vertices[vindex2]] # deactivate clipping if len(segment) > 1: x1, y1 = segment[0] x2, y2 = segment[1] cmds = [['M', [x1, y1]], ['L', [x2, y2]]] path = group_voronoi.add(PathElement()) path.set('d', str(inkex.Path(cmds))) path.style = linestyle if self.options.diagramType != 'Voronoi': triangles = voronoi.computeDelaunayTriangulation(seeds) i = 0 if self.options.delaunayFillOptions == "delaunay-fill": random.seed("inkscape") for triangle in triangles: pt1 = seeds[triangle[0]] pt2 = seeds[triangle[1]] pt3 = seeds[triangle[2]] cmds = [['M', [pt1.x, pt1.y]], ['L', [pt2.x, pt2.y]], ['L', [pt3.x, pt3.y]], ['Z', []]] if self.options.delaunayFillOptions == "delaunay-fill" \ or self.options.delaunayFillOptions == "delaunay-fill-random": facestyle = { 'stroke': fills[triangle[random.randrange(0, 2)]], 'stroke-width': str(self.svg.unittouu('0.005px')), 'fill': fills[triangle[random.randrange(0, 2)]], 'stroke-linecap': 'round', 'stroke-linejoin': 'round' } path = group_delaunay.add(PathElement()) path.set('d', str(inkex.Path(cmds))) path.style = facestyle i += 1
def render_obsolete(self): 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) return Rectangle.new(x, y, self.boxsize, self.boxsize)
def generate(self): """Generate the actual svg from the coding""" string = self.encode(self.text) if string == 'ERROR': return name = self.get_id('barcode') # use an svg group element to contain the barcode barcode = Group() barcode.set('id', name) barcode.set('style', 'fill: black;') barcode.transform.add_translate(self.pos_x, self.pos_y) if self.scale: barcode.transform.add_scale(self.scale) bar_id = 1 bar_offset = 0 tops = set() for datum in self.graphical_array(string): # Datum 0 tells us what style of bar is to come next style = self.get_style(int(datum[0])) # Datum 1 tells us what width in units, # style tells us how wide a unit is width = int(datum[1]) * int(style['width']) if style['write']: tops.add(style['top']) rect = Rectangle() rect.set('x', str(bar_offset)) rect.set('y', str(style['top'])) if self.pos_text == TEXT_POS_TOP: rect.set('y', str(style['top'] + self.font_size)) rect.set('id', "{}_bar{:d}".format(name, bar_id)) rect.set('width', str(width)) rect.set('height', str(style['height'])) barcode.append(rect) bar_offset += width bar_id += 1 for extra in self._extra: if extra is not None: barcode.append(extra) bar_width = bar_offset # Add text at the bottom of the barcode text = TextElement() text.set('x', str(int(bar_width / 2))) text.set('y', str(min(tops) + self.font_size - 1)) if self.pos_text == TEXT_POS_BOTTOM: text.set('y', str(self.height + max(tops) + self.font_size)) text.set('style', TEXT_TEMPLATE % self.font_size) text.set('xml:space', 'preserve') text.set('id', '{}_text'.format(name)) text.text = str(self.text) barcode.append(text) return barcode
def test_rectangle_without_attributes(self): rect = Rectangle() self.assert_bounding_box_is_equal(rect, (0, 0), (0, 0))
def test_new(self): """Anchor tag creation""" link = Anchor.new('https://inkscape.org', Rectangle()) self.assertElement(link, b'<a xlink:href="https://inkscape.org"><rect/></a>')