def generate_histogram(year, key, x, y, x_label): path = 'data/%s/%s.json' % (year, key) print(' - Using this file: %s' % path) # Read the data file for the visualisation data = pandas.read_json(path) original_length = len(data) # Rationalise the data set, like HA currently does. data = data[data.cdf < 0.95] print(' - Reduced the data set from: %s to: %s' % (original_length, len(data))) data[y] *= 100 # Plot the graph fig, ax = plt.subplots() ax.bar(data[x], data[y], width=8, align='edge') ax.set_xlabel(x_label) ax.set_ylabel('Probability density') # Save the figure into a buffer buf = BytesIO() plt.savefig(buf, format='svg') # Read the buffer as an svg string svg = buf.getvalue().decode('UTF-8') # Optimise the svg image svg = scour.scourString(svg) return svg
def optimize(self, allow_svg_errors=False): """ Optimize SVGs, PNGs or Jpegs Unfortunately, this needs to write temporary files by making use of the /tmp directory """ mimetype = magic.Magic(mime=True).from_buffer(self.data) tmp_filename = '/tmp/' + uuid4().get_hex() if mimetype == 'image/svg+xml': try: self.data = str(scourString(self.data)) except: # SVG contains bad data, we can't optimise it pass elif mimetype == 'image/jpeg': self.data = jpegtran("-optimize", _in=self.data).stdout elif mimetype == 'image/png': with open(tmp_filename, 'w') as tmp: tmp.write(self.data) optipng(tmp_filename) with open(tmp_filename) as tmp: self.data = tmp.read() os.remove(tmp_filename)
def scour_xml(doc): in_string = doc.toxml() doc.unlink() options = scour.sanitizeOptions( Values({ "remove_descriptive_elements": True, "enable_viewboxing": True, "strip_ids": True, "protect_ids_list": "ayah_markers,content", })) # scour the string out_string = scour.scourString(in_string, options) # prepare the output xml.dom.minidom object doc = minidom.parseString(out_string.encode("utf-8")) # since minidom does not seem to parse DTDs properly # manually declare all attributes with name "id" to be of type ID # (otherwise things like doc.getElementById() won't work) all_nodes = doc.getElementsByTagName("*") for node in all_nodes: try: node.setIdAttribute("id") except NotFoundErr: pass return doc
def effect(self): # version check if enabled in options if (self.options.scour_version_warn_old): scour_version = scour.__version__ scour_version_min = self.options.scour_version if (StrictVersion(scour_version) < StrictVersion(scour_version_min)): inkex.errormsg("The extension 'Optimized SVG Output' is designed for Scour " + scour_version_min + " and later " "but you're using the older version Scour " + scour_version + ".") inkex.errormsg("This usually works just fine but not all options available in the UI might be supported " "by the version of Scour installed on your system " "(see https://github.com/scour-project/scour/blob/master/HISTORY.md for release notes of Scour).") inkex.errormsg("Note: You can permanently disable this message on the 'About' tab of the extension window.") del self.options.scour_version del self.options.scour_version_warn_old # do the scouring try: input = file(self.args[0], "r") self.options.infilename = self.args[0] sys.stdout.write(scourString(input.read(), self.options).encode("UTF-8")) input.close() sys.stdout.close() except Exception as e: inkex.errormsg("Error during optimization.") inkex.errormsg("\nDetails:\n" + str(e)) inkex.errormsg("\nOS version: " + platform.platform()) inkex.errormsg("Python version: " + sys.version) inkex.errormsg("Scour version: " + scour.__version__) sys.exit()
def html_format(source, language, css_class, options, md): function = reduce(getattr, [lq, *source.split(".")]) fig = plot(function) tmp = StringIO() fig.savefig(tmp, format="svg", bbox_inches="tight", pad_inches=0) return scour.scourString(tmp.getvalue().replace("DejaVu Sans", "sans-serif"))
def html_format(source, language, css_class, options, md): function = reduce(getattr, [lq, *source.split(".")]) if inspect.isclass(function): function = function() fig = plot(function) tmp = StringIO() fig.savefig(tmp, format="svg", bbox_inches="tight", pad_inches=0) return scour.scourString(tmp.getvalue())
def minify_svg(input_file, output_file): # Read SVG file svg_string = pathlib.Path(input_file).read_text() # use scour to remove redundant stuff and then write to file svg_string = scour.scourString(svg_string, ScourOptions()) with open(output_file, "w") as fp: fp.write(svg_string)
def optimize(self, iconizr): f = open(self.path, 'r') input_str = f.read() f.close() svg_opt = scour.scourString(input_str, Values(iconizr.options)) # update XML tree self.root = ET.fromstring(svg_opt) self.xml._setroot(self.root) self.save(iconizr) return True
def effect(self): try: input = file(self.args[0], "r") self.options.infilename = self.args[0] sys.stdout.write(scourString(input.read(), self.options).encode("UTF-8")) input.close() sys.stdout.close() except Exception as e: inkex.errormsg("Error during optimization.") inkex.errormsg("\nDetails:\n" + str(e)) inkex.errormsg("\nOS version: " + platform.platform()) inkex.errormsg("Python version: " + sys.version) inkex.errormsg("Scour version: " + scour.__version__) sys.exit()
def clean_svg(output_path: str = OUTPUT_PATH): """ Clean and fixes icon insert in created SVG files """ svgs = os.listdir(output_path) for svg in svgs: item = f"{output_path}/{svg}" with open(item, "r") as r: src = r.read() data = scour.scourString(src) with open(item, "w") as w: w.write(data)
def save(self, stream): # version check if enabled in options if self.options.scour_version_warn_old: scour_version = scour.__version__ scour_version_min = self.options.scour_version if StrictVersion(scour_version) < StrictVersion(scour_version_min): raise inkex.AbortExtension(f""" The extension 'Optimized SVG Output' is designed for Scour {scour_version_min} or later but you're using the older version Scour {scour_version}. Note: You can permanently disable this message on the 'About' tab of the extension window.""" ) del self.options.scour_version del self.options.scour_version_warn_old # do the scouring stream.write( scourString(self.svg.tostring(), self.options).encode('utf8'))
def svg_tag(svg): t = get_template(svg) if t.template.source: svg_source = t.template.source else: svg_source = open(t.template.origin.name, 'r').read() scour_options = get_scour_options() scour_string = scour.scourString(svg_source, options=scour_options) lxml_object = html.fragment_fromstring(str(scour_string)) for fill_node in lxml_object.cssselect('[fill]'): # new style! https://stackoverflow.com/questions/6126789/selecting-attribute-values-from-lxml#6126846 if fill_node.get('fill', None): del fill_node.attrib['fill'] # old style?! # if getattr(fill_node.attrib, 'fill', None): # del fill_node.attrib.fill svg_out = html.tostring(lxml_object, encoding='unicode') return mark_safe(svg_out)
def clean_svg(svg): ''' Clean up an SVG file. - Remove script tags and the like - Reduce file size @return The SVG data as a string ''' opts = scour.parse_args(args=[ '--disable-group-collapsing', '--enable-viewboxing', '--enable-id-stripping', '--enable-comment-stripping', '--indent=none', '--protect-ids-noninkscape', '--quiet', '--remove-metadata', '--set-precision=5', ]) output = scour.scourString(svg, opts) return output
def save_document(self, document, filename): with open(filename, 'wb') as fp: document = document.tostring() fp.write(scourString(document).encode('utf8'))
def create(self, move_objs, size, hue1, hue2, out): # Import static methods from class if size: size = dup(size) if not hue1: hue1 = "white" if not hue2: hue2 = "silver" if not out: out = self.DEF_OUT self.d = svgwrite.Drawing( filename=out, size=size ) self.d.viewbox(width=self.DIAGRAM_VB, height=self.DIAGRAM_VB) angle_slice = Diagram.FULL_CIRCLE / len(move_objs) # Get an arange like array([0., 120., 240.]) angles = arange(0, Diagram.FULL_CIRCLE, angle_slice) DIAGRAM_VB_RADIUS = round(self.DIAGRAM_VB / 2) CIRCLE_RADIUS = round(DIAGRAM_VB_RADIUS / len(move_objs)) NORTH, EAST, SOUTH, WEST = (0, 90, 180, 270) move_points = list() hypotenuse = DIAGRAM_VB_RADIUS - CIRCLE_RADIUS for angle in angles: # Cardinal points first if angle == NORTH or angle == Diagram.FULL_CIRCLE: p = Point(DIAGRAM_VB_RADIUS, CIRCLE_RADIUS) elif angle == EAST: p = Point(self.DIAGRAM_VB - CIRCLE_RADIUS, DIAGRAM_VB_RADIUS) elif angle == SOUTH: p = Point(DIAGRAM_VB_RADIUS, self.DIAGRAM_VB - CIRCLE_RADIUS) elif angle == WEST: p = Point(CIRCLE_RADIUS, DIAGRAM_VB_RADIUS) else: # Otherwise, other angles angle_rad = radians(angle % EAST) opposite = hypotenuse * sin(angle_rad) adjacent = hypotenuse * cos(angle_rad) if angle < EAST: p = Point(DIAGRAM_VB_RADIUS + opposite, DIAGRAM_VB_RADIUS - adjacent) elif angle < SOUTH: p = Point(DIAGRAM_VB_RADIUS + adjacent, DIAGRAM_VB_RADIUS + opposite) elif angle < WEST: p = Point(DIAGRAM_VB_RADIUS - opposite, DIAGRAM_VB_RADIUS + adjacent) else: p = Point(DIAGRAM_VB_RADIUS - adjacent, DIAGRAM_VB_RADIUS - opposite) move_points.append(p) self.move_points = move_points # if len(move_points) > len(move_objs): # print("Slight error: I have data for too many circles in the diagram.") text_size = CIRCLE_RADIUS / 3 max_text_len = 12 style = pkg_resources.resource_stream(__name__, "diagram.css") style = style.read().decode("utf-8") style = style.replace("$TEXTSIZE", str(round(text_size))) self.d.defs.add( self.d.style(style) ) gradient = self.d.defs.add( self.d.radialGradient(id="radgrad") ) gradient.add_stop_color('0%', hue1).add_stop_color('100%', hue2) arrowhead = self.d.marker( insert=(1.5, 2), size=(4, 4), id="head", orient="auto", ) arrowhead.add( self.d.polygon( points=[(0, 0), (4, 2), (0, 4), (1, 2)], ) ) self.d.defs.add(arrowhead) # / defs decorative = self.d.g(id="decorative") decorative.add( self.d.rect( insert=(0, 0), size=(dup(self.DIAGRAM_VB)), ) ) decorative.add( self.d.circle( center=(dup(DIAGRAM_VB_RADIUS)), r=DIAGRAM_VB_RADIUS, ) ) self.d.add(decorative) all_moves_g = self.d.g(id="moves") # A circle for each move, with legend. for i, point in enumerate(move_points): the_move_obj = move_objs[i] #print(the_move_obj) name = "%d-%s" % (i, the_move_obj.move) move_group = self.d.g( id=name.replace(" ", "-"), ) move_group.add( self.d.circle( center=(point.tuple), r=CIRCLE_RADIUS, ) ) # Calculate some stuff for the text. text = move_objs[i].move text_length = max_text_len if (len(text) > max_text_len) else len(text) # Turn into coefficient text_length /= max_text_len # A bit less than double the radius, to allow padding. text_length_px = rounded(1.9 * CIRCLE_RADIUS * text_length) downshifted = ( rounded(point.x), rounded(point.y + text_size / 4) ) move_group.add( self.d.text( text, insert=(downshifted), text_anchor="middle", textLength=text_length_px, lengthAdjust="spacingAndGlyphs", ) ) # All the various beat lines. beats_lines = self.d.g(class_="beats") for target in move_objs[i].beats_num: line = ResizableLine( point, move_points[target] ).resize(CIRCLE_RADIUS * 2.2, proportional=False) # Take a bit more off the end for the arrow. line.resize(CIRCLE_RADIUS / 5, proportional=False, from_start=False) # Give it an id and add it to the drawing. l_path = self.d.path( d=line.path, id="{}-to-{}".format( i, move_objs[target].move ) ) beats_lines.add(l_path) # Flow text along that path. text = self.d.text("", dy=[-10]) textpath = self.d.textPath( l_path, move_objs[i].beats[move_objs[target].move], spacing='auto', startOffset=10 ) anim = self.d.animate( attributeName="startOffset", values=(-50, line.length), dur="7s", repeatCount="indefinite" ) textpath.add(anim) text.add(textpath) beats_lines.add(text) move_group.add(beats_lines) all_moves_g.add(move_group) self.d.add(all_moves_g) self.d.save() # Now optimise. try: from scour import scour with open(self.FILE_NAME, "r") as file: svg_string = file.read() # Remove indentation in stylesheet. svg_string = svg_string.replace("\t", "") # Optimise with scour. options = scour.parse_args([ "--no-line-breaks", "--create-groups", "--set-precision=2" ]) svg_string = scour.scourString(svg_string, options) with open(self.FILE_NAME, "w") as file: file.write(svg_string) except: print("Unable to optimise your diagram with scour.")