class Gauge(object): config = { 'id' : None, 'title' : 'Title', 'titleFontColor' : '#999999', 'value' : 0, 'valueFontColor' : '#010101', 'min' : 0, 'max' : 100, 'showMinMax' : True, 'gaugeWidthScale' : 1.0, 'gaugeColor' : '#edebeb', 'label' : "", 'showInnerShadow' : True, 'shadowOpacity' : 0.2, 'shadowSize' : 5, 'shadowVerticalOffset' : 3, 'levelColors' : ["#a9d70b", "#f9c802", "#ff0000"], 'levelColorsGradient' : True, 'labelFontColor' : "#b3b3b3", 'showNeedle' : False, 'needleColor' : "#b3b3b3", 'canvasWidth' : 400, 'canvasHeight' : 300, } def __init__(self, *args, **kwargs): for param_name, param_value in kwargs.items(): if self.config.has_key(param_name): self.config[param_name] = param_value # Overflow values if self.config['value'] > self.config['max']: self.config['value'] = self.config['max'] if self.config['value'] < self.config['min']: self.config['value'] = self.config['min'] self.originalValue = self.config['value'] self.canvas = Drawing(size=(self.config['canvasWidth'], self.config['canvasHeight'])) canvasW = self.config['canvasWidth'] canvasH = self.config['canvasHeight'] self.canvas.add(self.canvas.rect(insert=(0, 0), size=(canvasW, canvasH), stroke="none", fill="#ffffff")) # widget dimensions widgetW, widgetH = None, None if ((canvasW / canvasH) > 1.25): widgetW = 1.25 * canvasH widgetH = canvasH else: widgetW = canvasW widgetH = canvasW / 1.25 # delta dx = (canvasW - widgetW)/2 dy = (canvasH - widgetH)/2 # title titleFontSize = ((widgetH / 8) > 10) and (widgetH / 10) or 10 titleX = dx + widgetW / 2 titleY = dy + widgetH / 6.5 # value valueFontSize = ((widgetH / 6.4) > 16) and (widgetH / 6.4) or 16 valueX = dx + widgetW / 2 valueY = dy + widgetH / 1.4 # label labelFontSize = ((widgetH / 16) > 10) and (widgetH / 16) or 10 labelX = dx + widgetW / 2 labelY = valueY + valueFontSize / 2 + 6 # min minFontSize = ((widgetH / 16) > 10) and (widgetH / 16) or 10 minX = dx + (widgetW / 10) + (widgetW / 6.666666666666667 * self.config['gaugeWidthScale']) / 2 minY = dy + widgetH / 1.126760563380282 # max maxFontSize = ((widgetH / 16) > 10) and (widgetH / 16) or 10 maxX = dx + widgetW - (widgetW / 10) - (widgetW / 6.666666666666667 * self.config['gaugeWidthScale']) / 2 maxY = dy + widgetH / 1.126760563380282 # parameters self.params = { 'canvasW' : canvasW, 'canvasH' : canvasH, 'widgetW' : widgetW, 'widgetH' : widgetH, 'dx' : dx, 'dy' : dy, 'titleFontSize' : titleFontSize, 'titleX' : titleX, 'titleY' : titleY, 'valueFontSize' : valueFontSize, 'valueX' : valueX, 'valueY' : valueY, 'labelFontSize' : labelFontSize, 'labelX' : labelX, 'labelY' : labelY, 'minFontSize' : minFontSize, 'minX' : minX, 'minY' : minY, 'maxFontSize' : maxFontSize, 'maxX' : maxX, 'maxY' : maxY } # gauge self.gauge = self.gauge_path(self.config['max'], self.config['min'], self.config['max'], self.params['widgetW'], self.params['widgetH'], self.params['dx'], self.params['dy'], self.config['gaugeWidthScale'], stroke='none', fill=self.config['gaugeColor']) self.canvas.add(self.gauge) # level percent_value = (self.config['value'] - self.config['min']) / (self.config['max'] - self.config['min']) self.level = self.gauge_path(self.config['value'], self.config['min'], self.config['max'], self.params['widgetW'], self.params['widgetH'], self.params['dx'], self.params['dy'], self.config['gaugeWidthScale'], stroke='none', fill=self.get_color_for_value(percent_value, self.config['levelColors'], self.config['levelColorsGradient'])) self.canvas.add(self.level) # needle if self.config['showNeedle']: self.needle = self.needle_path(self.config['value'], self.config['min'], self.config['max'], self.params['widgetW'], self.params['widgetH'], self.params['dx'], self.params['dy'], self.config['gaugeWidthScale'], stroke='none', fill=self.config['needleColor']) self.canvas.add(self.needle) # Value else: text_config = { "font-size" : "%d" % self.params['valueFontSize'], "font-weight" : "bold", "font-family" : "Arial", "fill" : self.config['valueFontColor'], "fill-opacity" : "1", "text-anchor" : 'middle' } value_text = self.canvas.text('', insert=('%d' % self.params['valueX'], '%d' % self.params['valueY']), **text_config) value_tspan = self.canvas.tspan(self.originalValue, dy=[8]) value_text.add(value_tspan) self.canvas.add(value_text) # Add min & max value self.show_minmax() def save(self, path): svg = self.canvas.tostring() svg2png = getattr(cairosvg, 'svg2png') png_byte = svg2png(bytestring=svg) f = open(path,'w') f.write(png_byte) f.close() def gauge_path(self, value, val_min, val_max, w, h, dx, dy, gws, **extra): alpha = (1 - (value - val_min) / (val_max - val_min)) * math.pi Ro = w / 2 - w / 10 Ri = Ro - w / 6.666666666666667 * gws Cx = w / 2 + dx Cy = h / 1.25 + dy Xo = w / 2 + dx + Ro * math.cos(alpha) Yo = h - (h - Cy) + dy - Ro * math.sin(alpha) Xi = w / 2 + dx + Ri * math.cos(alpha) Yi = h - (h - Cy) + dy - Ri * math.sin(alpha) path = [] path.append(u"M%d,%d " % ((Cx - Ri), Cy)) path.append(u"L%d,%d " % ((Cx - Ro), Cy)) path.append(u"A%d,%d 0 0,1 %d,%d " % (Ro, Ro, Xo, Yo)) path.append(u"L%d,%d " % (Xi, Yi)) path.append(u"A%d,%d 0 0,0 %d,%d " % (Ri, Ri, (Cx - Ri), Cy)) path.append(u"z ") return Path(d=path, **extra) def needle_path(self, value, val_min, val_max, w, h, dx, dy, gws, **extra): xO = w / 2 + dx yO = h / 1.25 + dy Rext = w / 2 - w / 10 Rint = Rext - w / 6.666666666666667 * gws x_offset = xO y_offset = h - (h - yO) + dy val = (value - val_min) / (val_max - val_min) angle_b = val<0.5 and val*math.pi or (math.pi - val*math.pi) # Angle de la pointe angle_a = math.pi/2 - angle_b angle_c = math.pi/2 - angle_b rayon_base = 7 rayon_b = Rint + (Rext-Rint)*10/100 xA = x_offset + -1 * rayon_base * math.cos(angle_a) yA = y_offset - (val<0.5 and -1 or 1) * rayon_base * math.sin(angle_a) xC = x_offset + 1 * rayon_base * math.cos(angle_c) yC = y_offset - (val<0.5 and 1 or -1) * rayon_base * math.sin(angle_c) xB = x_offset + (val<0.5 and -1 or 1) * rayon_b * math.cos(angle_b) yB = y_offset - rayon_b * math.sin(angle_b) path = [] path.append(u"M%d,%d " % (xA, yA)) path.append(u"L%d,%d " % (xB, yB)) path.append(u"L%d,%d " % (xC, yC)) path.append(u"A%d,%d 0 1,1 %d,%d " % (rayon_base, rayon_base, xA, yA)) path.append(u"z ") return Path(d=path, **extra) def get_color_for_value(self, pct, color, grad): no = len(color); if no == 1: return color[0] HEX = r'[a-fA-F\d]{2}' HEX_COLOR = r'#(?P<red>%(hex)s)(?P<green>%(hex)s)(?P<blue>%(hex)s)' % {'hex': HEX} inc = grad and (1 / (no - 1)) or (1 / no) colors = [] i = 0 while i < no: percentage = (grad) and (inc * i) or (inc * (i + 1)) parts = re.match(HEX_COLOR,color[i]).groupdict() rval = int(parts['red'], 16) gval = int(parts['green'], 16) bval = int(parts['blue'], 16) colors.append({ 'pct': percentage, 'color': { 'r': rval, 'g': gval, 'b': bval } }) i+=1 if pct == 0: return 'rgb(%d,%d,%d)' % (colors[0]['color']['r'], colors[0]['color']['g'], colors[0]['color']['b']) i = 0 while i < len(colors): if pct <= colors[i]['pct']: if (grad == True): lower = colors[i-1] upper = colors[i] _range = upper['pct'] - lower['pct'] rangePct = (pct - lower['pct']) / _range pctLower = 1 - rangePct pctUpper = rangePct color = { 'r': math.floor(lower['color']['r'] * pctLower + upper['color']['r'] * pctUpper), 'g': math.floor(lower['color']['g'] * pctLower + upper['color']['g'] * pctUpper), 'b': math.floor(lower['color']['b'] * pctLower + upper['color']['b'] * pctUpper) } return 'rgb(%d,%d,%d)' % (color['r'], color['g'], color['b']) else: return 'rgb(%d,%d,%d)' % (colors[i]['color']['r'], colors[i]['color']['g'], colors[i]['color']['b']) i+=1 def show_minmax(self): # min txtMin_config = { "font-size" : '%d' % self.params['minFontSize'], "font-weight" : "normal", "font-family" : "Arial", "fill" : self.config['labelFontColor'], "fill-opacity" : self.config['showMinMax'] and "1" or "0", "text-anchor" : 'middle' } txtMin = self.canvas.text(self.config['min'], insert=(self.params['minX'], self.params['minY']), **txtMin_config) self.canvas.add(txtMin) # max txtMax_config = { "font-size" : '%d' % self.params['maxFontSize'], "font-weight" :"normal", "font-family" :"Arial", "fill" : self.config['labelFontColor'], "fill-opacity" : self.config['showMinMax'] and "1" or "0", "text-anchor" : 'middle' } txtMax = self.canvas.text(self.config['max'], insert=(self.params['maxX'], self.params['maxY']), **txtMax_config) self.canvas.add(txtMax)
def profile(id: str, format: str): drawing = Drawing() w = 320 h = 320 drawing["width"] = "%dpx" % w drawing["height"] = "%dpx" % h prng = Random(id) background: Renderable if format == "png": background = random_background(prng, w, h, local_paths=format == "png") else: background_data = build_team_background(id, w, h) background = EmbeddedBackground(background_data, w, h) drawing.add(background.render(drawing)) distance = w / 2 profile_scale = 10 * (7 / 80) g = generate_profile_image(id, drawing) g.translate(distance, prng.uniform(h * 0.25, h * .5)) g.rotate(prng.gauss(0, 20)) g.scale(profile_scale) drawing.add(g) svg_code = drawing.tostring() if format == "svg": res = Response(drawing.tostring()) res.headers["Content-Type"] = "image/svg+xml" res.headers["Cache-Control"] = "public, immutable, max-age=%d" % ( 86400 * 30) return res elif format == "png": requested_width: int try: requested_width = int(request.args.get("s")) except: requested_width = 320 res = Response( svg2png(bytestring=bytearray(svg_code, "utf-8"), scale=requested_width / w)) res.headers["Content-Type"] = "image/png" res.headers["Cache-Control"] = "public, immutable, max-age=%d" % ( 86400 * 30) return res return None, 406
def eye_pop() -> str: drawing = Drawing() e = PopEye(50, .6, .6, .5 * math.pi) g = e.render(drawing) g.translate(50, 50) drawing.add(g) return drawing.tostring()
def generate_multiple(rows, cols, adj_scale, num_files, path): out = [] previous = [] for i in range(num_files): dwg = Drawing(filename=os.path.join(path, '{}.svg'.format(i)), debug=True) previous.extend(generate(dwg, rows, cols, adj_scale, previous)) #dwg.save() out.append(dwg.tostring()) return out
def leg_foot() -> str: drawing = Drawing() r = drawing.rect((-50, -50), (200, 200), stroke="black", fill="white") drawing.add(r) a = LegWithFoot(leg_length=50, leg_color="#606060", foot_color="#ff0000") g = a.render(drawing) g.translate(50, 50) drawing.add(g) return drawing.tostring()
def arm_hand() -> str: drawing = Drawing() r = drawing.rect((-50, -50), (200, 200), stroke="black", fill="white") drawing.add(r) a = ArmWithHand(arm_length=50, arm_color="#606060", hand_color="#ff0000") g = a.render(drawing) g.translate(50, 50) drawing.add(g) return drawing.tostring()
def render( self, data: DataContainer, accepted_media_type: Optional[str] = None, renderer_context: Optional[Dict[str, Union[str, int, float]]] = None, ) -> bytes: """ :param data: point datum array, or dict that include those array in certain key. point datum represent by dict consists with x, y positions and value. when dict specified, need to specify corresponding key via view's attribute as 'svg_render_map'. :param accepted_media_type: :param renderer_context: :return: SVG formatted heatmap in bytestring. """ renderer_context = renderer_context or {} view = renderer_context["view"] width = getattr(view, "width", self.default_width) height = getattr(view, "height", self.default_height) translatorArgs = { "colorScale": getattr(view, "color_scale", DEFAULT_COLOR_SCALE), "grid": getattr(view, "grid", GRID), "opacity": getattr(view, "opacity", OPACITY), } svg_render_map = getattr(view, "svg_render_map", None) bare_data = data if svg_render_map is None else data[svg_render_map] datagroup = self.translate( bare_data, width, height, x_key=getattr(view, "x_key", "x"), y_key=getattr(view, "y_key", "y"), v_key=getattr(view, "v_key", "value"), is_1d=getattr(view, "is_1d", False), **translatorArgs, ) drawSize = (width, height) svgargs = getattr(view, "svgargs", {}) drawing = Drawing(self.filename, size=drawSize, profile=self.svg_profile, **svgargs) drawing.add(datagroup) return drawing.tostring().encode("ascii")
def write_svg(svgpath, defs, paths, svg_attributes): # Create an SVG file assert svg_attributes is not None dwg = Drawing(filename=svgpath, **svg_attributes) doc = parseString(dwg.tostring()) svg = doc.firstChild if defs != '': defsnode = parseString(defs).firstChild svg.replaceChild(defsnode, svg.firstChild) for i, path in enumerate(paths): svg.appendChild(path.cloneNode(deep=True)) xmlstring = doc.toprettyxml() doc.unlink() with open(svgpath, 'w') as f: f.write(xmlstring)
def alien_glorb() -> str: drawing = Drawing() prng = random.Random() #background = drawing.rect((0,0), size=(200,200), fill="black") background = StarsBackground(200, 200) drawing.add(background.render(drawing)) #a = GlorbAlien(position=(75, 75), size=60) a = random_glorb(prng, size=prng.randint(40, 80)) g = a.render(drawing) g.translate(prng.randint(50, 150), prng.randint(50, 150)) g.rotate(prng.gauss(0, 20)) drawing.add(g) return drawing.tostring()
def astro_glorb() -> str: drawing = Drawing() prng = random.Random() #background = drawing.rect((0,0), size=(200,200), fill="black") background = StarsBackground(300, 300) drawing.add(background.render(drawing)) #a = GlorbAlien(position=(75, 75), size=60) a = random_glorb(prng, size=prng.randint(40, 80)) #astro = DomeHelmetAstronaut(a, a.size) astro = random_domed_astronaut(prng, a) g = astro.render(drawing) g.translate(prng.randint(100, 200), prng.randint(100, 200)) g.rotate(prng.gauss(0, 20)) drawing.add(g) return drawing.tostring()
def __str__(self): draw = SVGDrawing( size=(self.size[0] * mm, self.size[1] * mm), profile='full', viewBox="0 0 {} {}".format(self.size[0], self.size[1])) for obj in self.objects: drawed = obj._draw() # object can consists of several objects if hasattr(drawed, '__iter__'): for svg_obj in drawed: draw.add(svg_obj) else: draw.add(drawed) # defs section (masks, clips, markers, etc) defs = obj._defs() if defs: if hasattr(defs, '__iter__'): for defs_item in defs: draw.defs.add(defs_item) else: draw.defs.add(defs) return draw.tostring()
def display_svg(): dwg = Drawing() hlines = dwg.add(dwg.g(id="hlines", stroke="green")) for y in range(20): hlines.add( dwg.line(start=(2 * cm, (2 + y) * cm), end=(18 * cm, (2 + y) * cm))) vlines = dwg.add(dwg.g(id="vline", stroke="blue")) for x in range(17): vlines.add( dwg.line(start=((2 + x) * cm, 2 * cm), end=((2 + x) * cm, 21 * cm))) shapes = dwg.add(dwg.g(id="shapes", fill="red")) # set presentation attributes at object creation as SVG-Attributes circle = dwg.circle(center=(15 * cm, 8 * cm), r="2.5cm", stroke="blue", stroke_width=3) circle["class"] = "class1 class2" shapes.add(circle) # override the 'fill' attribute of the parent group 'shapes' shapes.add( dwg.rect( insert=(5 * cm, 5 * cm), size=(45 * mm, 45 * mm), fill="blue", stroke="red", stroke_width=3, )) # or set presentation attributes by helper functions of the Presentation-Mixin ellipse = shapes.add( dwg.ellipse(center=(10 * cm, 15 * cm), r=("5cm", "10mm"))) ellipse.fill("green", opacity=0.5).stroke("black", width=5).dasharray([20, 20]) return Response(dwg.tostring(), mimetype="image/svg+xml")
def disvg(paths=None, colors=None, filename=os_path.join(getcwd(), 'disvg_output.svg'), stroke_widths=None, nodes=None, node_colors=None, node_radii=None, timestamp=False, margin_size=0.1, mindim=600, dimensions=None, viewbox=None, text=None, text_path=None, font_size=None, attributes=None, svg_attributes=None, svgwrite_debug=False, paths2Drawing=False): """Takes in a list of paths and creates an SVG file containing said paths. REQUIRED INPUTS: :param paths - a list of paths OPTIONAL INPUT: :param colors - specifies the path stroke color. By default all paths will be black (#000000). This paramater can be input in a few ways 1) a list of strings that will be input into the path elements stroke attribute (so anything that is understood by the svg viewer). 2) a string of single character colors -- e.g. setting colors='rrr' is equivalent to setting colors=['red', 'red', 'red'] (see the 'color_dict' dictionary above for a list of possibilities). 3) a list of rgb 3-tuples -- e.g. colors = [(255, 0, 0), ...]. :param filename - the desired location/filename of the SVG file created (by default the SVG will be stored in the current working directory and named 'disvg_output.svg'). :param stroke_widths - a list of stroke_widths to use for paths (default is 0.5% of the SVG's width or length) :param nodes - a list of points to draw as filled-in circles :param node_colors - a list of colors to use for the nodes (by default nodes will be red) :param node_radii - a list of radii to use for the nodes (by default nodes will be radius will be 1 percent of the svg's width/length) :param text - string or list of strings to be displayed :param text_path - if text is a list, then this should be a list of path (or path segments of the same length. Note: the path must be long enough to display the text or the text will be cropped by the svg viewer. :param font_size - a single float of list of floats. :param margin_size - The min margin (empty area framing the collection of paths) size used for creating the canvas and background of the SVG. :param mindim - The minimum dimension (height or width) of the output SVG (default is 600). :param dimensions - The (x,y) display dimensions of the output SVG. I.e. this specifies the `width` and `height` SVG attributes. Note that these also can be used to specify units other than pixels. Using this will override the `mindim` parameter. :param viewbox - This specifies the coordinated system used in the svg. The SVG `viewBox` attribute works together with the the `height` and `width` attrinutes. Using these three attributes allows for shifting and scaling of the SVG canvas without changing the any values other than those in `viewBox`, `height`, and `width`. `viewbox` should be input as a 4-tuple, (min_x, min_y, width, height), or a string "min_x min_y width height". Using this will override the `mindim` parameter. :param attributes - a list of dictionaries of attributes for the input paths. Note: This will override any other conflicting settings. :param svg_attributes - a dictionary of attributes for output svg. :param svgwrite_debug - This parameter turns on/off `svgwrite`'s debugging mode. By default svgwrite_debug=False. This increases speed and also prevents `svgwrite` from raising of an error when not all `svg_attributes` key-value pairs are understood. :param paths2Drawing - If true, an `svgwrite.Drawing` object is returned and no file is written. This `Drawing` can later be saved using the `svgwrite.Drawing.save()` method. NOTES: * The `svg_attributes` parameter will override any other conflicting settings. * Any `extra` parameters that `svgwrite.Drawing()` accepts can be controlled by passing them in through `svg_attributes`. * The unit of length here is assumed to be pixels in all variables. * If this function is used multiple times in quick succession to display multiple SVGs (all using the default filename), the svgviewer/browser will likely fail to load some of the SVGs in time. To fix this, use the timestamp attribute, or give the files unique names, or use a pause command (e.g. time.sleep(1)) between uses. """ _default_relative_node_radius = 5e-3 _default_relative_stroke_width = 1e-3 _default_path_color = '#000000' # black _default_node_color = '#ff0000' # red _default_font_size = 12 # append directory to filename (if not included) if os_path.dirname(filename) == '': filename = os_path.join(getcwd(), filename) # append time stamp to filename if timestamp: fbname, fext = os_path.splitext(filename) dirname = os_path.dirname(filename) tstamp = str(time()).replace('.', '') stfilename = os_path.split(fbname)[1] + '_' + tstamp + fext filename = os_path.join(dirname, stfilename) # check paths and colors are set if isinstance(paths, Path) or is_path_segment(paths): paths = [paths] if paths: if not colors: colors = [_default_path_color] * len(paths) else: assert len(colors) == len(paths) if isinstance(colors, str): colors = str2colorlist(colors, default_color=_default_path_color) elif isinstance(colors, list): for idx, c in enumerate(colors): if is3tuple(c): colors[idx] = "rgb" + str(c) # check nodes and nodes_colors are set (node_radii are set later) if nodes: if not node_colors: node_colors = [_default_node_color] * len(nodes) else: assert len(node_colors) == len(nodes) if isinstance(node_colors, str): node_colors = str2colorlist(node_colors, default_color=_default_node_color) elif isinstance(node_colors, list): for idx, c in enumerate(node_colors): if is3tuple(c): node_colors[idx] = "rgb" + str(c) # set up the viewBox and display dimensions of the output SVG # along the way, set stroke_widths and node_radii if not provided assert paths or nodes stuff2bound = [] if viewbox: if not isinstance(viewbox, str): viewbox = '%s %s %s %s' % viewbox if dimensions is None: dimensions = viewbox.split(' ')[2:4] elif dimensions: dimensions = tuple(map(str, dimensions)) def strip_units(s): return re.search(r'\d*\.?\d*', s.strip()).group() viewbox = '0 0 %s %s' % tuple(map(strip_units, dimensions)) else: if paths: stuff2bound += paths if nodes: stuff2bound += nodes if text_path: stuff2bound += text_path xmin, xmax, ymin, ymax = big_bounding_box(stuff2bound) dx = xmax - xmin dy = ymax - ymin if dx == 0: dx = 1 if dy == 0: dy = 1 # determine stroke_widths to use (if not provided) and max_stroke_width if paths: if not stroke_widths: sw = max(dx, dy) * _default_relative_stroke_width stroke_widths = [sw]*len(paths) max_stroke_width = sw else: assert len(paths) == len(stroke_widths) max_stroke_width = max(stroke_widths) else: max_stroke_width = 0 # determine node_radii to use (if not provided) and max_node_diameter if nodes: if not node_radii: r = max(dx, dy) * _default_relative_node_radius node_radii = [r]*len(nodes) max_node_diameter = 2*r else: assert len(nodes) == len(node_radii) max_node_diameter = 2*max(node_radii) else: max_node_diameter = 0 extra_space_for_style = max(max_stroke_width, max_node_diameter) xmin -= margin_size*dx + extra_space_for_style/2 ymin -= margin_size*dy + extra_space_for_style/2 dx += 2*margin_size*dx + extra_space_for_style dy += 2*margin_size*dy + extra_space_for_style viewbox = "%s %s %s %s" % (xmin, ymin, dx, dy) if dx > dy: szx = str(mindim) + 'px' szy = str(int(ceil(mindim * dy / dx))) + 'px' else: szx = str(int(ceil(mindim * dx / dy))) + 'px' szy = str(mindim) + 'px' dimensions = szx, szy # Create an SVG file if svg_attributes is not None: dimensions = (svg_attributes.get("width", dimensions[0]), svg_attributes.get("height", dimensions[1])) debug = svg_attributes.get("debug", svgwrite_debug) dwg = Drawing(filename=filename, size=dimensions, debug=debug, **svg_attributes) else: dwg = Drawing(filename=filename, size=dimensions, debug=svgwrite_debug, viewBox=viewbox) # add paths if paths: for i, p in enumerate(paths): if isinstance(p, Path): ps = p.d() elif is_path_segment(p): ps = Path(p).d() else: # assume this path, p, was input as a Path d-string ps = p if attributes: good_attribs = {'d': ps} for key in attributes[i]: val = attributes[i][key] if key != 'd': try: dwg.path(ps, **{key: val}) good_attribs.update({key: val}) except Exception as e: warn(str(e)) dwg.add(dwg.path(**good_attribs)) else: dwg.add(dwg.path(ps, stroke=colors[i], stroke_width=str(stroke_widths[i]), fill='none')) # add nodes (filled in circles) if nodes: for i_pt, pt in enumerate([(z.real, z.imag) for z in nodes]): dwg.add(dwg.circle(pt, node_radii[i_pt], fill=node_colors[i_pt])) # add texts if text: assert isinstance(text, str) or (isinstance(text, list) and isinstance(text_path, list) and len(text_path) == len(text)) if isinstance(text, str): text = [text] if not font_size: font_size = [_default_font_size] if not text_path: pos = complex(xmin + margin_size*dx, ymin + margin_size*dy) text_path = [Line(pos, pos + 1).d()] else: if font_size: if isinstance(font_size, list): assert len(font_size) == len(text) else: font_size = [font_size] * len(text) else: font_size = [_default_font_size] * len(text) for idx, s in enumerate(text): p = text_path[idx] if isinstance(p, Path): ps = p.d() elif is_path_segment(p): ps = Path(p).d() else: # assume this path, p, was input as a Path d-string ps = p # paragraph = dwg.add(dwg.g(font_size=font_size[idx])) # paragraph.add(dwg.textPath(ps, s)) pathid = 'tp' + str(idx) dwg.defs.add(dwg.path(d=ps, id=pathid)) txter = dwg.add(dwg.text('', font_size=font_size[idx])) txter.add(txt.TextPath('#'+pathid, s)) if paths2Drawing: return dwg ss = dwg.tostring() return ss # save svg if not os_path.exists(os_path.dirname(filename)): makedirs(os_path.dirname(filename)) dwg.save(pretty=False)
def export_node(node, store, size=None): """Construct a SVG description for a workflow node. Args: node (NodeDef) store (dict of uid, def): elements definitions size (int, int): size of drawing in pixels Returns: (str) - SVG description of workflow node """ pfs = port_font_size # node size pr = port_radius pspace = pr * 9 nw = compute_node_width(node, node['name'], pspace) nh = label_font_size + 2 * pr + 2 * pfs + 2 + (2 * node_padding) # draw if size is None: size = (600, 600) paper = Drawing("workflow_node.svg", size, id="repr") lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="in_port") lg.add_stop_color(0, color='#3333ff') lg.add_stop_color(1, color='#2222ff') paper.defs.add(lg) lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="out_port") lg.add_stop_color(0, color='#ffff33') lg.add_stop_color(1, color='#9a9a00') paper.defs.add(lg) # body g = paper.add(paper.g()) # background lg = paper.linearGradient((0.5, 0), (0.5, 1.)) lg.add_stop_color(0, color='#8c8cff') lg.add_stop_color(1, color='#c8c8c8') paper.defs.add(lg) bg = paper.rect((-nw / 2, -nh / 2), (nw, nh), rx=node_padding, ry=node_padding, stroke_width=1) bg.stroke('#808080') bg.fill(lg) g.add(bg) # label style = ('font-size: %dpx; font-family: %s; ' 'text-anchor: middle' % (label_font_size, label_font)) frag = paper.tspan(node['name'], dy=[label_font_size // 3]) label = paper.text("", style=style, fill='#000000') label.add(frag) g.add(label) # ports port_style = ('font-size: %dpx; ' % pfs + 'font-family: %s; ' % label_font) onstyle = port_style + 'text-anchor: end' instyle = port_style + 'text-anchor: start' istyle = port_style + 'text-anchor: middle' nb = len(node['inputs']) py = -nh / 2 for i, pdef in enumerate(node['inputs']): px = i * pspace - pspace * (nb - 1) / 2 pg = g.add(paper.g()) pg.translate(px, py) idef = store.get(pdef['interface'], None) if idef is not None and 'url' in idef: link = pg.add(paper.a(href=idef['url'], target='_top')) else: link = pg port = paper.circle((0, 0), pr, stroke='#000000', stroke_width=1) port.fill("url(#in_port)") link.add(port) # port name frag = paper.tspan(pdef['name'], dy=[-2 * pr]) label = paper.text("", style=instyle, fill='#000000') label.rotate(-45) label.add(frag) pg.add(label) # port interface if idef is None: itxt = pdef['interface'] else: itxt = idef['name'] if len(itxt) > 10: itxt = itxt[:7] + "..." frag = paper.tspan(itxt, dy=[pr + pfs]) label = paper.text("", style=istyle, fill='#000000') label.add(frag) link.add(label) nb = len(node['outputs']) py = nh / 2 for i, pdef in enumerate(node['outputs']): px = i * pspace - pspace * (nb - 1) / 2 pg = g.add(paper.g()) pg.translate(px, py) idef = store.get(pdef['interface'], None) if idef is not None and 'url' in idef: link = pg.add(paper.a(href=idef['url'], target='_top')) else: link = pg port = paper.circle((0, 0), pr, stroke='#000000', stroke_width=1) port.fill("url(#out_port)") link.add(port) # port name frag = paper.tspan(pdef['name'], dy=[2 * pr + pfs // 2]) label = paper.text("", style=onstyle, fill='#000000') label.rotate(-45) label.add(frag) pg.add(label) # port interface if idef is None: itxt = pdef['interface'] else: itxt = idef['name'] if len(itxt) > 10: itxt = itxt[:7] + "..." frag = paper.tspan(itxt, dy=[-pr - 2]) label = paper.text("", style=istyle, fill='#000000') label.add(frag) link.add(label) # reformat whole drawing to fit screen xmin = -nw / 2 - draw_padding / 10. xmax = +nw / 2 + draw_padding / 10. if len(node['inputs']) == 0: inames_extend = 0 else: inames = [(len(pdef['name']), pdef['name']) for pdef in node['inputs']] inames_extend = string_size(sorted(inames)[-1][1], pfs) * 0.7 + pfs ymin = -nh / 2 - pr - inames_extend - draw_padding / 10. if len(node['outputs']) == 0: onames_extend = 0 else: onames = [(len(pdef['name']), pdef['name']) for pdef in node['outputs']] onames_extend = string_size(sorted(onames)[-1][1], pfs) * 0.7 + pfs ymax = +nh / 2 + pr + onames_extend + draw_padding / 10. w = float(size[0]) h = float(size[1]) ratio = max((xmax - xmin) / w, (ymax - ymin) / h) xsize = ratio * w ysize = ratio * h bb = (xmin * xsize / (xmax - xmin), ymin * ysize / (ymax - ymin), xsize, ysize) paper.viewbox(*bb) return paper.tostring(), bb
def export_workflow(workflow, store, size=None): """Construct a SVG description for a workflow. Args: workflow (WorkflowDef) store (dict of uid, def): elements definitions size (int, int): size of drawing in pixels Returns: (str) - SVG description of workflow """ # check that each node has a position for node in workflow['nodes']: if 'x' not in node or 'y' not in node: raise UserWarning("need to position workflow first") if size is None: size = (600, 600) # draw paper = Drawing("workflow.svg", size, id="repr") lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="bg_loaded") lg.add_stop_color(0, color='#8c8cff') lg.add_stop_color(1, color='#c8c8c8') paper.defs.add(lg) lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="bg_failed") lg.add_stop_color(0, color='#ff8cff') lg.add_stop_color(1, color='#c8c8c8') paper.defs.add(lg) lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="in_port") lg.add_stop_color(0, color='#3333ff') lg.add_stop_color(1, color='#2222ff') paper.defs.add(lg) lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="out_port") lg.add_stop_color(0, color='#ffff33') lg.add_stop_color(1, color='#9a9a00') paper.defs.add(lg) for i, link in enumerate(workflow['links']): draw_link(paper, workflow, store, link, i) bbs = [] for i, node in enumerate(workflow['nodes']): bb = draw_node(paper, workflow, store, node, i) bbs.append(bb) # reformat whole drawing to fit screen xmin = min(bb[0] for bb in bbs) - draw_padding xmax = max(bb[2] for bb in bbs) + draw_padding ymin = min(bb[1] for bb in bbs) - draw_padding ymax = max(bb[3] for bb in bbs) + draw_padding w = float(size[0]) h = float(size[1]) xratio = (xmax - xmin) / w yratio = (ymax - ymin) / h if xratio > yratio: xsize = int(xratio * w) ysize = int(xratio * h) ymin -= (ysize - (ymax - ymin)) / 2 else: xsize = int(yratio * w) ysize = int(yratio * h) xmin -= (xsize - (xmax - xmin)) / 2 paper.viewbox(xmin, ymin, xsize, ysize) return paper.tostring(), (xmin, ymin, xsize, ysize)
def _draw_json_circuit(json_circuit, unit: str = 'px', round_index: int = 0, show_clbits: bool = True, bit_order: dict = None) -> Tuple[str, Tuple[int, int]]: """Draw a circuit represented as a JSON string. Args: json_circuit (dict): A quantum circuit in JSON format. This can be obtained with the QISKit object qiskit.unroll.JsonBackend. unit (str) : Unit used to draw the circuit. This parameter is not really tested at the moment and values different from "px" could cause the function to fail. round_index (int) : Number of digits after the decimal point to keep in the SVG. A value different from "0" could cause the function to fail, this parameter need to be tested. show_clbits (bool): True if the function should draw the classical bits, False otherwise. bit_order (dict): A Python dictionnary storing the bit ordering. Returns: Tuple[str, Tuple[int, int]]: (SVG, (width, height)) - SVG: string representing the given circuit in SVG format. - width: computed width in pixels. - height: computed height in pixels. """ # TEMPORARY FIX FOR THE QOBJ STRUCTURE # "issue": the json_circuit['header']['clbit_labels'] and # json_circuit['header']['qubit_labels'] don't have the same meaning and it # seems unintuitive. I fix that here. This part should be removed if the # qobj structure change to fix this behaviour. new_clbit_labels = list() for clbit_label in json_circuit['header']['clbit_labels']: for i in range(clbit_label[1]+1): new_clbit_labels.append([clbit_label[0], i]) json_circuit['header']['clbit_labels'] = new_clbit_labels # Take the appropriate default value for bit_order if not provided by the # user. if bit_order is None: bit_order = dict() for qubit_index, qubit_label in enumerate(json_circuit['header']['qubit_labels']): bit_order["".join(map(str, qubit_label))] = qubit_index for clbit_index, clbit_label in enumerate(json_circuit['header']['clbit_labels']): bit_order["".join(map(str, clbit_label))] = clbit_index # Transform the bit_order structure in a more useful one: the bit_order structure # associates bit labels to their index, but in the json circuit we don't have bit # labels but rather bit indices. So we want to have a dictionnary mapping indices # in the json circuit to indices on the drawn circuit. bit_mapping = {'clbits': dict(), 'qubits': dict()} for qubit_index, qubit_label in enumerate(json_circuit['header']['qubit_labels']): bit_mapping['qubits'][qubit_index] = bit_order["".join(map(str, qubit_label))] for clbit_index, clbit_label in enumerate(json_circuit['header']['clbit_labels']): bit_mapping['clbits'][clbit_index] = bit_order["".join(map(str, clbit_label))] # Compute the width and height width, height = _helpers.get_dimensions(json_circuit, show_clbits) width, height = round(width, round_index), round(height, round_index) width_str, height_str = str(width)+unit, str(height)+unit # Create the drawing drawing = Drawing(size=(width_str, height_str)) # Create the internal structure used by the drawing functions index_last_gate_on_reg = {'clbits': [0] * json_circuit['header'].get('number_of_clbits', 0), 'qubits': [0] * json_circuit['header'].get('number_of_qubits', 0)} # And draw! # First the registers names and lines _draw_registers_names_and_lines(drawing, width, json_circuit, show_clbits) # And then each gate for operation in json_circuit['operations']: _draw_gate(drawing, index_last_gate_on_reg, operation, show_clbits, bit_mapping) return (drawing.tostring(), (width, height))
def species_marker(request, genus_name='-', species_name='-'): """ Generate a SVG marker for a given species Args: request: genus_name: species_name: Returns: """ if species_name == '-': color = 'bbbbbb' species_name = '?' else: color = species_to_color(genus_name, species_name) marker_width = 60 marker_height = 100 marker_border = 5 stroke_width = 1 line_color = 'black' marker_color = '#' + color bezier_length = marker_width / 3 width = marker_width + marker_border * 2 height = marker_height + marker_border * 2 font_size = marker_height / 4 arc_centre_drop = (marker_height / 3.5 ) # Distance from top of marker to rotation centre arc_radius_vertical = arc_centre_drop image = Drawing(size=('%dpx' % width, '%dpx' % height)) marker = Path(stroke=line_color, stroke_width=stroke_width, fill=marker_color) marker.push(f'M {marker_border} {arc_centre_drop + marker_border} ' ) # Left arc edge marker.push( f'C {marker_border} {arc_centre_drop + marker_border + bezier_length} ' f'{width / 2 - bezier_length / 3} {height - marker_border - bezier_length} ' f'{width / 2} {height - marker_border}' # Point ) marker.push( f'C {width / 2 + bezier_length / 3} {height - marker_border - bezier_length} ' f'{width - marker_border} {arc_centre_drop + marker_border + bezier_length} ' f'{width - marker_border} {arc_centre_drop + marker_border} ' # Right edge ) # Right arc edge marker.push_arc(target=(marker_border, arc_centre_drop + marker_border), rotation=180, r=(marker_width / 2, arc_radius_vertical), absolute=True, angle_dir='-') marker.push('z') image.add(marker) image.add( Text(species_name, (width / 2, marker_border + arc_centre_drop + marker_height / 20), font_family='Arial', font_size=font_size, dominant_baseline="middle", text_anchor="middle")) return HttpResponse(image.tostring(), content_type='image/svg+xml')
def export_node(node, store, size=None): """Construct a SVG description for a workflow node. Args: node (NodeDef) store (dict of uid, def): elements definitions size (int, int): size of drawing in pixels Returns: (str) - SVG description of workflow node """ pfs = port_font_size # node size pr = port_radius pspace = pr * 9 nw = compute_node_width(node, node['name'], pspace) nh = label_font_size + 2 * pr + 2 * pfs + 2 + (2 * node_padding) # draw if size is None: size = (600, 600) paper = Drawing("workflow_node.svg", size, id="repr") lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="in_port") lg.add_stop_color(0, color='#3333ff') lg.add_stop_color(1, color='#2222ff') paper.defs.add(lg) lg = paper.linearGradient((0.5, 0), (0.5, 1.), id="out_port") lg.add_stop_color(0, color='#ffff33') lg.add_stop_color(1, color='#9a9a00') paper.defs.add(lg) # body g = paper.add(paper.g()) # background lg = paper.linearGradient((0.5, 0), (0.5, 1.)) lg.add_stop_color(0, color='#8c8cff') lg.add_stop_color(1, color='#c8c8c8') paper.defs.add(lg) bg = paper.rect((-nw / 2, -nh / 2), (nw, nh), rx=node_padding, ry=node_padding, stroke_width=1) bg.stroke('#808080') bg.fill(lg) g.add(bg) # label style = ('font-size: %dpx; font-family: %s; ' 'text-anchor: middle' % (label_font_size, label_font)) frag = paper.tspan(node['name'], dy=[label_font_size // 3]) label = paper.text("", style=style, fill='#000000') label.add(frag) g.add(label) # ports port_style = ('font-size: %dpx; ' % pfs + 'font-family: %s; ' % label_font) onstyle = port_style + 'text-anchor: end' instyle = port_style + 'text-anchor: start' istyle = port_style + 'text-anchor: middle' nb = len(node['inputs']) py = -nh / 2 for i, pdef in enumerate(node['inputs']): px = i * pspace - pspace * (nb - 1) / 2 pg = g.add(paper.g()) pg.translate(px, py) idef = store.get(pdef['interface'], None) if idef is not None and 'url' in idef: link = pg.add(paper.a(href=idef['url'], target='_top')) else: link = pg port = paper.circle((0, 0), pr, stroke='#000000', stroke_width=1) port.fill("url(#in_port)") link.add(port) # port name frag = paper.tspan(pdef['name'], dy=[-2 * pr]) label = paper.text("", style=instyle, fill='#000000') label.rotate(-45) label.add(frag) pg.add(label) # port interface if idef is None: itxt = pdef['interface'] else: itxt = idef['name'] if len(itxt) > 10: itxt = itxt[:7] + "..." frag = paper.tspan(itxt, dy=[pr + pfs]) label = paper.text("", style=istyle, fill='#000000') label.add(frag) link.add(label) nb = len(node['outputs']) py = nh / 2 for i, pdef in enumerate(node['outputs']): px = i * pspace - pspace * (nb - 1) / 2 pg = g.add(paper.g()) pg.translate(px, py) idef = store.get(pdef['interface'], None) if idef is not None and 'url' in idef: link = pg.add(paper.a(href=idef['url'], target='_top')) else: link = pg port = paper.circle((0, 0), pr, stroke='#000000', stroke_width=1) port.fill("url(#out_port)") link.add(port) # port name frag = paper.tspan(pdef['name'], dy=[2 * pr + pfs // 2]) label = paper.text("", style=onstyle, fill='#000000') label.rotate(-45) label.add(frag) pg.add(label) # port interface if idef is None: itxt = pdef['interface'] else: itxt = idef['name'] if len(itxt) > 10: itxt = itxt[:7] + "..." frag = paper.tspan(itxt, dy=[- pr - 2]) label = paper.text("", style=istyle, fill='#000000') label.add(frag) link.add(label) # reformat whole drawing to fit screen xmin = - nw / 2 - draw_padding / 10. xmax = + nw / 2 + draw_padding / 10. if len(node['inputs']) == 0: inames_extend = 0 else: inames = [(len(pdef['name']), pdef['name']) for pdef in node['inputs']] inames_extend = string_size(sorted(inames)[-1][1], pfs) * 0.7 + pfs ymin = - nh / 2 - pr - inames_extend - draw_padding / 10. if len(node['outputs']) == 0: onames_extend = 0 else: onames = [(len(pdef['name']), pdef['name']) for pdef in node['outputs']] onames_extend = string_size(sorted(onames)[-1][1], pfs) * 0.7 + pfs ymax = + nh / 2 + pr + onames_extend + draw_padding / 10. w = float(size[0]) h = float(size[1]) ratio = max((xmax - xmin) / w, (ymax - ymin) / h) xsize = ratio * w ysize = ratio * h bb = (xmin * xsize / (xmax - xmin), ymin * ysize / (ymax - ymin), xsize, ysize) paper.viewbox(*bb) return paper.tostring(), bb
def write_to_pdf(dwg: Drawing, path: str): svg_str = dwg.tostring() svg = ET.fromstring(svg_str, etree.XMLParser()) svgRenderer = SvgRenderer(None) drawing = svgRenderer.render(svg) renderPDF.drawToFile(drawing, path)
class DrawTemplateSVG(object): def __init__(self, tmax, grid, darea=DrawArea, symbols=SymbolAssets, m3d=Matrix3D): self._off = (2.2, 2.5) size = 100 self._size = (size, size * 1.9) self._matrix3d = m3d(tmax[0], self._size, self._off, grid) self._area = darea(self._off, self._size, tmax[0], tmax[1], grid).area() self.dwg = Drawing('graph.svg', size=self._area, id="graph") viewp = self.fixOneLineRoot(grid) self.dwg.viewbox(*viewp) self._symbols = symbols(self.dwg) self.setup() # When you have only one or two items, we need to fix the grid area def fixOneLineRoot(self, grid): key_min = min(grid.keys()) rootSize = len(grid[key_min]) size = len(grid) area = [0, 0, *self._area] if key_min <= 1 and rootSize <= 2: ajy = self._size[0] * 1.8 ajx = self._size[0] * 0.5 area[1] = 0 - ajx area[3] = self._area[1] + ajy if size <= 1: area[0] = 0 - ajx return area def setup(self): self.setup_brightness() self.setup_marker() def setup_brightness(self): self._symbols.brightness() self._symbols.brightness("darker", 0.4) def setup_marker(self): s = self._size[0] / 8 opts = {'insert': (152, 3), 'size': (s, s)} self._symbols.asset_marker('markers.arrow', opts) def boundary_box(self, pos, node): opts = {'id': node.get('_id')} symbol = self._symbols.asset('boundaries_box.front', 'default boundaries', (pos[0], pos[1]), self._size, opts) self.add(symbol) def draw_label(self, pos, node): text = HelperDrawLabel(self._size, self._matrix3d) \ .label_by_node(pos, node) symbol = self._symbols.text(*text) self.add(symbol) def draw_app(self, item): cad1 = [item[x] for x in range(2)] node = item[3] pos = self._matrix3d.rotateNodeXY(item)() self.draw_root(item) self.draw_grid_size(cad1, item[2]) self.grid_box(pos) self.draw_execute(pos, node) self.boundary_box(pos, node) self.draw_label(pos, node) self.draw_tooltips(node) def draw_execute(self, pos, node): hDrawApp = HelperDrawApplication(self._size) hDrawApp.execute(pos, node) pSymb = hDrawApp.get_apps() for symb in pSymb: symbol = self._symbols.asset(*symb) self.add(symbol) def draw_tooltips(self, node): _id = "tool-" + node.get('_id') g = self._symbols.create_group(_id) hDrawTooltips = HelperDrawTooltips(self._size, self._off) hDrawTooltips.execute(node) ltxt = hDrawTooltips.get_text() symbol = self._symbols.multiline(ltxt, (0, 0)) g.add(symbol) def grid_box(self, pos, opts={'fill-opacity': '0.4'}): symbol = self._symbols.asset('grid.base', 'default', pos, self._size, opts) self.add(symbol) def draw_grid_size(self, cad1, size): cad2 = (cad1[0], cad1[1] + size - 1) points = HelperDrawBasePolyline(self._size, self._matrix3d) \ .create_polyline_by_pos(cad1, cad2) symbol = self._symbols.polyline(points, { 'fill': '#ccc', 'fill-opacity': 0.2 }) self.add(symbol) def draw_root(self, item): root = item[3].get('root') if root: nitem = (item[0] - 1, item[1], item[2], item[3]) pos = self._matrix3d.rotateNodeXY(nitem)() symbol = self._symbols.asset('grid.entry', 'default', pos, self._size) self.draw_connect(nitem, item) self.add(symbol) def add(self, symbol): self.dwg.add(symbol) def save(self): return self.dwg.tostring() def draw_connect(self, node1, node2, details=None): id = "%s-%s" % (node1[0], node2[0]) g = self.dwg.g(id=id) d = HelperDrawConnector(self._size, self._off, self._matrix3d).connect(node1, node2) symbol = self._symbols.conn(d) g.add(symbol) cls = " conn-%s" % node1[3].get('_id') symbol = self._symbols.conn_holder(d, cls) g.add(symbol) symbol = self._symbols.text(details, (0, 0), {'display': 'none'}) g.add(symbol) self.add(g)
def team(id: str, format: str) -> Union[Response, Tuple[Any, int]]: drawing = Drawing() w = 1000 h = 400 drawing["width"] = "%dpx" % w drawing["height"] = "%dpx" % h team_id = id emails = request.args.getlist("user")[0:10] generate_random = request.args.get("random") if generate_random: team_id = "%s" % random.random() email_count = random.randint(1, 8) emails = [random.random() for r in range(email_count)] team_prng = Random(team_id) background: Renderable if format == "png": background = random_background(team_prng, w, h, local_paths=format == "png") else: background_data = build_team_background(team_id, w, h) background = EmbeddedBackground(background_data, w, h) drawing.add(background.render(drawing)) distance = w / (len(emails) + 1) profile_scale = 1 - (len(emails) - 4) * (7 / 80) for i, email in enumerate(emails): g = generate_profile_image(email, drawing) g.translate((i + 1) * distance, team_prng.randint(100, 200)) g.rotate(team_prng.gauss(0, 20)) g.scale(profile_scale) drawing.add(g) svg_code = drawing.tostring() if format == "svg": res = Response(drawing.tostring()) res.headers["Content-Type"] = "image/svg+xml" res.headers["Cache-Control"] = "public, immutable, max-age=%d" % ( 86400 * 30) return res elif format == "png": requested_width: int try: requested_width = int(request.args.get("s")) except: requested_width = 1000 res = Response( svg2png(bytestring=bytearray(svg_code, "utf-8"), scale=requested_width / w)) res.headers["Content-Type"] = "image/png" res.headers["Cache-Control"] = "public, immutable, max-age=%d" % ( 86400 * 30) return res return None, 406