def effect(self): self.is_installed() if not self.options.layerName: raise inkex.AbortExtension(_("Please enter a layer name.")) node = self.svg.getElement( f"//*[@inkscape:groupmode='layer' " f"and @inkscape:label='{self.options.layerName}']") if node is None: raise inkex.AbortExtension( _(f"Layer '{self.options.layerName}' not found.")) if self.options.effectIn == "default": node.set("jessyink:transitionIn", None) else: length = int(self.options.effectInDuration * 1000) node.set("jessyink:transitionIn", Style(name=self.options.effectIn, length=length)) if self.options.effectOut == "default": node.set("jessyink:transitionOut", None) else: length = int(self.options.effectOutDuration * 1000) node.set("jessyink:transitionOut", Style(name=self.options.effectOut, length=length))
def read_stop_gradient(gradient): stop_data = {"id": gradient.attrib.get("id"), "stops": []} for stop in gradient: offset = stop.attrib.get("offset") style = Style(Style.parse_str(stop.attrib['style'])) color = Color.parse_str(style.get("stop-color"))[1] opacity = style.get("stop-opacity") stop_data.get("stops").append( tuple([float(offset)] + [x / 256.0 for x in color] + [float(opacity)])) return stop_data
def splitPath(inkex, node): dashes = [] try: # inkscape 1.0 style = dict(Style(node.get('style'))) except: # inkscape 0.9x style = simplestyle.parseStyle(node.get('style')) if 'stroke-dasharray' in style: if style['stroke-dasharray'].find(',') > 0: dashes = [ float(dash) for dash in style['stroke-dasharray'].split(',') if dash ] if dashes: try: # inkscape 1.0 p = CubicSuperPath(node.get('d')) except: # inkscape 0.9x p = cubicsuperpath.parsePath(node.get('d')) new = [] for sub in p: idash = 0 dash = dashes[0] length = 0 new.append([sub[0][:]]) i = 1 while i < len(sub): dash = dash - length length = cspseglength(new[-1][-1], sub[i]) while dash < length: new[-1][-1], next, sub[i] = cspbezsplitatlength( new[-1][-1], sub[i], dash / length) if idash % 2: # create a gap new.append([next[:]]) else: # splice the curve new[-1].append(next[:]) length = length - dash idash = (idash + 1) % len(dashes) dash = dashes[idash] if idash % 2: new.append([sub[i]]) else: new[-1].append(sub[i]) i += 1 try: # inkscape 1.0 node.set('d', CubicSuperPath(new)) except: # inkscape 0.9x node.set('d', cubicsuperpath.formatPath(new)) del style['stroke-dasharray'] try: # inkscape 1.0 node.set('style', Style(style)) except: # inkscape 0.9x node.set('style', simplestyle.formatStyle(style)) if node.get(inkex.addNS('type', 'sodipodi')): del node.attrib[inkex.addNS('type', 'sodipodi')]
def test_interpolate(self): """Test interpolation method.""" stl1 = Style({ 'stroke-width': '0px', 'fill-opacity': 1.0, 'fill': Color((200, 0, 0)) }) stl2 = Style({ 'stroke-width': '1pc', 'fill-opacity': 0.0, 'fill': Color((100, 0, 100)) }) stl3 = stl1.interpolate(stl2, 0.5) assert stl3['fill-opacity'] == pytest.approx(0.5, 1e-3) assert stl3['fill'] == [150, 0, 50] assert stl3['stroke-width'] == '8px'
def test_composite(self): """Test chaining styles together""" stl = Style("border-color: blue;") stl += "border-color: red; border-issues: true;" self.assertEqual(str(stl), 'border-color:red;border-issues:true') st2 = stl + "border-issues: false;" self.assertEqual(str(st2), 'border-color:red;border-issues:false')
def scalePxAttribute(self, node, transf, attr): if 'style' in node.attrib: style = node.attrib.get('style') style = dict(Style.parse_str(style)) update = False if attr in style: try: stroke_width = float(style.get(attr).strip().replace("px", "")) stroke_width *= math.sqrt(abs(transf.a * transf.d)) style[attr] = str(max(stroke_width, 0.1)) + "px" update = True except AttributeError: pass if update: node.attrib['style'] = Style(style).to_str()
def scaleStrokeWidth(self, node, transf): if 'style' in node.attrib: style = node.attrib.get('style') style = dict(Style.parse_str(style)) update = False if 'stroke-width' in style: try: stroke_width = float(style.get('stroke-width').strip().replace("px", "")) stroke_width *= math.sqrt(abs(transf.a * transf.d)) style['stroke-width'] = str(stroke_width) update = True except AttributeError: pass if update: node.attrib['style'] = Style(style).to_str()
def scaleStrokeWidth(self, node, transf): if 'style' in node.attrib: style = node.attrib.get('style') style = dict(Style.parse_str(style)) update = False if 'stroke-width' in style: try: stroke_width = self.svg.unittouu( style.get('stroke-width')) / self.svg.unittouu("1px") stroke_width *= math.sqrt( abs(transf.a * transf.d - transf.b * transf.c)) style['stroke-width'] = str(stroke_width) update = True except AttributeError as e: pass if update: node.attrib['style'] = Style(style).to_str()
def test_in_place_style(self): """Do styles update when we set them""" elem = self.svg.getElementById('D') elem.style['fill'] = 'purpleberry' self.assertEqual(elem.get('style'), 'fill:purpleberry') elem.style = {'marker': 'flag'} self.assertEqual(elem.get('style'), 'marker:flag') elem.style = Style(stroke='gammon') self.assertEqual(elem.get('style'), 'stroke:gammon') elem.style.update('grape:2;strawberry:nice;') self.assertEqual(elem.get('style'), 'stroke:gammon;grape:2;strawberry:nice')
def scaleStrokeWidth(self, node, transf): if 'style' in node.attrib: style = node.attrib.get('style') style = dict(Style.parse_str(style)) update = False if 'stroke-width' in style: try: stroke_width = float( style.get('stroke-width').strip().replace("px", "")) # Modification by David Burghoff: corrected to use determinant # stroke_width *= math.sqrt(abs(transf.a * transf.d)) stroke_width *= math.sqrt( abs(transf.a * transf.d - transf.b * transf.c)) style['stroke-width'] = str(stroke_width) update = True except AttributeError: pass if update: node.attrib['style'] = Style(style).to_str()
def test_color_property(self): """Color special handling""" stl = Style("fill-opacity:0.7;fill:red;") self.assertEqual(stl.get_color('fill').alpha, 0.7) self.assertEqual(str(stl.get_color('fill')), 'rgba(255, 0, 0, 0.7)') stl.set_color('rgba(0, 127, 0, 0.5)', 'stroke') self.assertEqual( str(stl), 'fill-opacity:0.7;fill:red;stroke-opacity:0.5;stroke:#007f00')
def test_inbuilts(self): """Test inbuild style functions""" stadd = Style("a: 1") + Style("b: 2") self.assertTrue(stadd == Style("b: 2; a: 1")) self.assertFalse(stadd == Style("b: 2")) self.assertFalse(stadd != Style("b: 2; a: 1")) self.assertEqual(stadd - "a: 4", "b: 2") stadd -= "b: 3; c: 4" self.assertEqual(stadd, Style("a: 1"))
def get_compiled_gradient(self, new_id): # compiling gradient stops root = etree.Element("linearGradient", id=new_id) for idx, stop in enumerate(self.gradient_data["stops"]): stop_id = self.get_name() + str(idx) offset = stop[0] color = Color([stop[1], stop[2], stop[3]], "rgb") opacity = stop[4] tmp_stops = { "id": stop_id, "offset": str(offset), "style": Style({ "stop-color": str(color), "stop-opacity": str(opacity) }).to_str() } current_stop = etree.SubElement(root, "stop", attrib=tmp_stops) return root
def get_selected_gradients_data(self): selected_objects = self.svg.selected gradient_list = [] if len(selected_objects) > 0: for item in selected_objects: style = Style( Style.parse_str(selected_objects.get(item).get('style'))) fill = stroke = "None" if style.get("fill"): fill = style.get("fill")[5:-1] if "url" in style.get( "fill") else "None" if style.get("stroke"): stroke = style("stroke")[5:-1] if "url" in style.get( "stroke") else "None" if fill == "None" and stroke == "None": continue # read fill data if "radialGradient" in fill or "linearGradient" in fill: real_fill = self.svg.getElementById(fill).attrib[ "{" + NSS["xlink"] + "}href"][1:] real_fill_node = self.svg.getElementById(real_fill) if real_fill_node not in gradient_list: gradient_list.append(real_fill_node) # read stroke data if "radialGradient" in stroke or "linearGradient" in stroke: real_stroke = self.svg.getElementById(stroke).attrib[ "{" + NSS["xlink"] + "}href"][1:] real_stroke_node = self.svg.getElementById(real_stroke) if real_stroke_node not in gradient_list: gradient_list.append(real_stroke_node) data = [] # read gradients data for gradient in gradient_list: # parse gradient stops stop_data = read_stop_gradient(gradient) data.append(stop_data) return data
def effect(self): exponent = self.options.exponent if exponent >= 0: exponent += 1.0 else: exponent = 1.0 / (1.0 - exponent) steps = [1.0 / (self.options.steps + 1.0)] for i in range(self.options.steps - 1): steps.append(steps[0] + steps[-1]) steps = [step**exponent for step in steps] if self.options.zsort: # work around selection order swapping with Live Preview objects = self.svg.get_z_selected() else: # use selection order (default) objects = self.svg.selected objects = [ node for node in objects.values() if isinstance(node, inkex.PathElement) ] # prevents modification of original objects objects = copy.deepcopy(objects) for node in objects: node.apply_transform() objectpairs = pairwise(objects, start=False) for (elem1, elem2) in objectpairs: start = elem1.path.to_superpath() end = elem2.path.to_superpath() sst = copy.deepcopy(elem1.style) est = copy.deepcopy(elem2.style) basestyle = copy.deepcopy(sst) if 'stroke-width' in basestyle: basestyle['stroke-width'] = sst.interpolate_prop( est, 0, 'stroke-width') # prepare for experimental style tweening if self.options.style: styledefaults = Style({ 'opacity': 1.0, 'stroke-opacity': 1.0, 'fill-opacity': 1.0, 'stroke-width': 1.0, 'stroke': 'none', 'fill': 'none' }) for key in styledefaults: sst.setdefault(key, styledefaults[key]) est.setdefault(key, styledefaults[key]) isnotplain = lambda x: not (x == 'none' or x[:1] == '#') isgradient = lambda x: x.startswith('url(#') if isgradient(sst['stroke']) and isgradient(est['stroke']): strokestyle = 'gradient' elif isnotplain(sst['stroke']) or isnotplain( est['stroke']) or (sst['stroke'] == 'none' and est['stroke'] == 'none'): strokestyle = 'notplain' else: strokestyle = 'color' if isgradient(sst['fill']) and isgradient(est['fill']): fillstyle = 'gradient' elif isnotplain(sst['fill']) or isnotplain( est['fill']) or (sst['fill'] == 'none' and est['fill'] == 'none'): fillstyle = 'notplain' else: fillstyle = 'color' if strokestyle == 'color': if sst['stroke'] == 'none': sst['stroke-width'] = '0.0' sst['stroke-opacity'] = '0.0' sst['stroke'] = est['stroke'] elif est['stroke'] == 'none': est['stroke-width'] = '0.0' est['stroke-opacity'] = '0.0' est['stroke'] = sst['stroke'] if fillstyle == 'color': if sst['fill'] == 'none': sst['fill-opacity'] = '0.0' sst['fill'] = est['fill'] elif est['fill'] == 'none': est['fill-opacity'] = '0.0' est['fill'] = sst['fill'] if self.options.method == 2: # subdivide both paths into segments of relatively equal lengths slengths, stotal = csplength(start) elengths, etotal = csplength(end) lengths = {} t = 0 for sp in slengths: for l in sp: t += l / stotal lengths.setdefault(t, 0) lengths[t] += 1 t = 0 for sp in elengths: for l in sp: t += l / etotal lengths.setdefault(t, 0) lengths[t] += -1 sadd = [k for (k, v) in lengths.items() if v < 0] sadd.sort() eadd = [k for (k, v) in lengths.items() if v > 0] eadd.sort() t = 0 s = [[]] for sp in slengths: if not start[0]: s.append(start.pop(0)) s[-1].append(start[0].pop(0)) for l in sp: pt = t t += l / stotal if sadd and t > sadd[0]: while sadd and sadd[0] < t: nt = (sadd[0] - pt) / (t - pt) bezes = cspbezsplitatlength( s[-1][-1][:], start[0][0][:], nt) s[-1][-1:] = bezes[:2] start[0][0] = bezes[2] pt = sadd.pop(0) s[-1].append(start[0].pop(0)) t = 0 e = [[]] for sp in elengths: if not end[0]: e.append(end.pop(0)) e[-1].append(end[0].pop(0)) for l in sp: pt = t t += l / etotal if eadd and t > eadd[0]: while eadd and eadd[0] < t: nt = (eadd[0] - pt) / (t - pt) bezes = cspbezsplitatlength( e[-1][-1][:], end[0][0][:], nt) e[-1][-1:] = bezes[:2] end[0][0] = bezes[2] pt = eadd.pop(0) e[-1].append(end[0].pop(0)) start = s[:] end = e[:] else: # which path has fewer segments? lengthdiff = len(start) - len(end) # swap shortest first if lengthdiff > 0: start, end = end, start # subdivide the shorter path for x in range(abs(lengthdiff)): maxlen = 0 subpath = 0 segment = 0 for y in range(len(start)): for z in range(1, len(start[y])): leng = bezlenapprx(start[y][z - 1], start[y][z]) if leng > maxlen: maxlen = leng subpath = y segment = z sp1, sp2 = start[subpath][segment - 1:segment + 1] start[subpath][segment - 1:segment + 1] = cspbezsplit( sp1, sp2) # if swapped, swap them back if lengthdiff > 0: start, end = end, start # break paths so that corresponding subpaths have an equal number of segments s = [[]] e = [[]] while start and end: if start[0] and end[0]: s[-1].append(start[0].pop(0)) e[-1].append(end[0].pop(0)) elif end[0]: s.append(start.pop(0)) e[-1].append(end[0][0]) e.append([end[0].pop(0)]) elif start[0]: e.append(end.pop(0)) s[-1].append(start[0][0]) s.append([start[0].pop(0)]) else: s.append(start.pop(0)) e.append(end.pop(0)) if self.options.dup: steps = [0] + steps + [1] # create an interpolated path for each interval group = self.svg.get_current_layer().add(inkex.Group()) for time in steps: interp = [] # process subpaths for ssp, esp in zip(s, e): if not (ssp or esp): break interp.append([]) # process superpoints for sp, ep in zip(ssp, esp): if not (sp or ep): break interp[-1].append([]) # process points for p1, p2 in zip(sp, ep): if not (sp or ep): break interp[-1][-1].append(interppoints(p1, p2, time)) # remove final subpath if empty. if not interp[-1]: del interp[-1] # basic style interpolation if self.options.style: basestyle.update(sst.interpolate(est, time)) for prop in ['stroke', 'fill']: if isgradient(sst[prop]) and isgradient(est[prop]): gradid1 = sst[prop][4:-1] gradid2 = est[prop][4:-1] grad1 = self.svg.getElementById(gradid1) grad2 = self.svg.getElementById(gradid2) newgrad = grad1.interpolate(grad2, time) stops, orientation = newgrad.stops_and_orientation( ) self.svg.defs.add(orientation) basestyle[prop] = orientation.get_id(as_url=2) if len(stops): self.svg.defs.add(stops, orientation) orientation.set('xlink:href', stops.get_id(as_url=1)) new = group.add(inkex.PathElement()) new.style = basestyle new.path = CubicSuperPath(interp)
def test_set_property(self): """Set the style attribute directly""" stl = Style() stl['border-pain'] = 'green' self.assertEqual(str(stl), 'border-pain:green')
def _create_styles(self, n): 'Return a style to use for the generated objects.' # Use either the first or the last element's stroke for line caps, # stroke widths, etc. fstyle = self.svg.selection.first().style lstyle = self.svg.selection[-1].style if self.options.stroke_type == 'last_sel': style = Style(lstyle) else: style = Style(fstyle) # Apply the specified fill color. if self.options.fill_type == 'first_sel': fcolor = fstyle.get_color('fill') style.set_color(fcolor, 'fill') elif self.options.fill_type == 'last_sel': fcolor = lstyle.get_color('fill') style.set_color(fcolor, 'fill') elif self.options.fill_type == 'specified': style.set_color(self.options.fill_color, 'fill') elif self.options.fill_type == 'random': pass # Handled below else: sys.exit( inkex.utils.errormsg( _('Internal error: Unrecognized fill type "%s".')) % self.options.fill_type) # Apply the specified stroke color. if self.options.stroke_type == 'first_sel': scolor = fstyle.get_color('stroke') style.set_color(scolor, 'stroke') elif self.options.stroke_type == 'last_sel': scolor = lstyle.get_color('stroke') style.set_color(scolor, 'stroke') elif self.options.stroke_type == 'specified': style.set_color(self.options.stroke_color, 'stroke') elif self.options.stroke_type == 'random': pass # Handled below else: sys.exit( inkex.utils.errormsg( _('Internal error: Unrecognized stroke type "%s".')) % self.options.stroke_type) # Produce n copies of the style. styles = [Style(style) for i in range(n)] if self.options.fill_type == 'random': for s in styles: r = random.randint(0, 255) g = random.randint(0, 255) b = random.randint(0, 255) s.set_color('#%02x%02x%02x' % (r, g, b), 'fill') s['fill-opacity'] = 255 if self.options.stroke_type == 'random': for s in styles: r = random.randint(0, 255) g = random.randint(0, 255) b = random.randint(0, 255) s.set_color('#%02x%02x%02x' % (r, g, b), 'stroke') s['stroke-opacity'] = 255 # Return the list of styles. return [str(s) for s in styles]
def test_new_style(self): """Create a style from a path string""" stl = Style("border-color: blue; border-width: 4px;") self.assertEqual(str(stl), 'border-color:blue;border-width:4px')