def svg_xml_rotated(xml, **kwargs): """ Returns a pygame.Surface list from the given xml. For kwargs, see "rotated_svg" """ degrees = kwargs.pop("degrees", None) images = kwargs.pop("images", None) frames = kwargs.pop("frames", images) rotations = kwargs.pop("rotations", None) center_x = kwargs.pop("center_x", None) center_y = kwargs.pop("center_y", None) if center_x and not center_y or center_y and not center_x: raise Exception( "Invalid center - must provide both center_x and center_y, or none" ) if len(kwargs): logging.warning("Unexpected keyword arguments: %s" % kwargs) if rotations: if degrees or frames: raise Exception("Incompatible arguments") else: if degrees: if frames: raise Exception("Incompatible arguments") frames = floor(360 / degrees) elif frames: if degrees: raise Exception("Incompatible arguments") degrees = 360 / frames else: raise Exception("No rotation spec!") rotations = [degrees * i for i in range(frames)] images = [] # Read the svg into ElementTree: svg_node = ET.fromstring(xml) # Create group with transform to contain rest of svg: wrapper = ET.SubElement(svg_node, "g", { "id": "legame-image-rotator", "transform": "" }) # Move all nodes not the wrapper to inside the wrapper: for e in svg_node: if e is not wrapper: wrapper.append(e) # Create cairosvg.Tree to be rendered # use the cairosvg.surface.helpers.node_format() func to get width, height: cairosvg_tree = Tree(bytestring=ET.tostring(svg_node.getroottree())) width, height, viewbox = node_format(None, cairosvg_tree) if center_x is None: center_x = width / 2 if center_y is None: center_y = height / 2 for degrees in rotations: # Modify "transform" attribute of wrapper node: cairosvg_tree.children[0]["transform"] = "rotate({}, {}, {})".format( degrees, center_x, center_y) # Render modified cairosvg Tree: images.append(surf_from_cairosvgtree(cairosvg_tree, width, height)) return images
def convert(cls, bytestring=None, **kwargs): """Convert a SVG document to the format for this class. Specify the input by passing one of these: :param bytestring: The SVG source as a byte-string. :param file_obj: A file-like object. :param url: A filename. And the output with: :param write_to: The filename of file-like object where to write the output. If None or not provided, return a byte string. Only ``bytestring`` can be passed as a positional argument, other parameters are keyword-only. """ dpi = kwargs.pop('dpi', 96) parent_width = kwargs.pop('parent_width', None) parent_height = kwargs.pop('parent_height', None) scale = kwargs.pop('scale', 1) write_to = kwargs.pop('write_to', None) kwargs['bytestring'] = bytestring tree = Tree(**kwargs) output = write_to or io.BytesIO() instance = cls(tree, output, dpi, None, parent_width, parent_height, scale) instance.finish() if write_to is None: return output.getvalue()
def convertList(urls, writeTo, dpi=72): #see https://github.com/Kozea/CairoSVG/issues/200 import cairocffi from cairosvg.parser import Tree from cairosvg.surface import PDFSurface class RecordingPDFSurface(PDFSurface): surface_class = cairocffi.RecordingSurface def _create_surface(self, width, height): cairo_surface = cairocffi.RecordingSurface( cairocffi.CONTENT_COLOR_ALPHA, (0, 0, width, height)) return cairo_surface, width, height surface = cairocffi.PDFSurface(writeTo, 1, 1) context = cairocffi.Context(surface) for url in urls: if os.name == 'nt': url = url.replace('\\', '/') url = 'file:///{}'.format(url) image_surface = RecordingPDFSurface(Tree(url=url), None, dpi) surface.set_size(image_surface.width, image_surface.height) context.set_source_surface(image_surface.cairo, 0, 0) context.paint() surface.show_page() surface.finish()
def generate_award_pdf_svg(output, data, template_prefix, dpi=96): svgs = [] surface = cairocffi.PDFSurface(output, 1, 1) context = cairocffi.Context(surface) for d in data: t = d['template'] if len(t) < 1: continue text_template_filename = os.path.join(template_prefix, t + '.svg') with open(text_template_filename, 'r') as f: svg = etree.parse(f) svg = _data_into_svg(svg, d) svg_str = etree.tostring(svg) tree = Tree(bytestring=svg_str) svgs.append(svg_str) image_surface = PDFSurface(tree, None, dpi) surface.set_size(image_surface.width, image_surface.height) context.set_source_surface(image_surface.cairo, 0, 0) context.paint() surface.show_page() # with open(text_template_filename, 'r') as f: # svgs.append(_data_into_svg(etree.parse(f), d)) # svgs += b'</svg>' surface.finish() with open(output + '.svg', 'wb') as f: f.write(b"\n".join(svgs))
def load_svg(svg: bytes, width: float = None, height: float = None) -> ImageSurface: """ Load a SVG image to an Cairo ImageSurface. :param svg: Plain SVG data :param width: Designated with of the image. If None, it will be determined from the svg data. :param height: Designated height of the image. If None, it will be determined from the svg data. :return: ImageSurface that contains the image from the SVG data. """ return PNGSurface(Tree(bytestring=svg), None, 1, parent_width=width, parent_height=height).cairo
def draw_svg(): # Draw to a cairo surface but do not write to a file tree = Tree(bytestring=string, url=uri) surface = ScaledSVGSurface(tree, output=None, dpi=96) if not (surface.width > 0 and surface.height > 0): raise ValueError( 'Images without an intrinsic size are not supported.') pattern = cairo.SurfacePattern(surface.cairo) return pattern, surface.width, surface.height
def loadsvg(name, dpi=72., keep_colors=True): assert name.endswith(".svg") s = open(name).read() tree = Tree(bytestring=s) if keep_colors: context = Flatten() else: context = SkipColors() dummy = DummySurf(tree, None, dpi, context=context) item = back.Compound(dummy.paths) return item
def render_svg(surface: cairo.Surface, svg: str, x, y, width, height, rotation=None, dpi=96): tree = Tree(bytestring=svg.encode()) instance = CairoSurface( surface=surface, rotation=rotation, x=x, y=y, tree=tree, dpi=dpi, output_width=width, output_height=height, ) instance.draw(tree)
def from_xml(xml): cairosvg_tree = Tree(bytestring=xml) width, height, viewbox = node_format(None, cairosvg_tree) return surf_from_cairosvgtree(cairosvg_tree, width, height)
self.paths = self.context.paths class SkipColors(Flatten): def set_source_rgba(self, r, g, b, a): pass def loadsvg(name, dpi=72., keep_colors=True): assert name.endswith(".svg") s = open(name).read() tree = Tree(bytestring=s) if keep_colors: context = Flatten() else: context = SkipColors() dummy = DummySurf(tree, None, dpi, context=context) item = back.Compound(dummy.paths) return item if __name__ == "__main__": name = argv.next() s = open(name).read() tree = Tree(bytestring=s) my = DummySurf(tree, None, 72.) cvs = back.Canvas(my.paths) cvs.writePDFfile("output.pdf")
def image(surface, node): """Draw an image ``node``.""" base_url = node.get('{http://www.w3.org/XML/1998/namespace}base') if not base_url and node.url: base_url = os.path.dirname(node.url) + '/' url = parse_url(node.get('{http://www.w3.org/1999/xlink}href'), base_url) if "href" in node and node["href"].startswith("data:image/"): # gif, etc image_bytes = uri2image_bytes(node["href"]) else: image_bytes = node.fetch_url(url, 'image/*') if len(image_bytes) < 5: return x, y = size(surface, node.get('x'), 'x'), size(surface, node.get('y'), 'y') width = size(surface, node.get('width'), 'x') height = size(surface, node.get('height'), 'y') if image_bytes[:4] == b'\x89PNG': png_file = BytesIO(image_bytes) elif (image_bytes[:5] in (b'<svg ', b'<?xml', b'<!DOC') or image_bytes[:2] == b'\x1f\x8b'): if 'x' in node: del node['x'] if 'y' in node: del node['y'] tree = Tree(url=url.geturl(), url_fetcher=node.url_fetcher, bytestring=image_bytes, tree_cache=surface.tree_cache, unsafe=node.unsafe) tree_width, tree_height, viewbox = node_format(surface, tree, reference=False) if not viewbox: tree_width = tree['width'] = width tree_height = tree['height'] = height node.image_width = tree_width or width node.image_height = tree_height or height scale_x, scale_y, translate_x, translate_y = preserve_ratio( surface, node) # Clip image region surface.context.rectangle(x, y, width, height) surface.context.clip() # Draw image surface.context.save() surface.context.translate(x, y) surface.set_context_size(*node_format(surface, tree, reference=False), scale=1, preserved_ratio=preserved_ratio(tree)) surface.context.translate(*surface.context.get_current_point()) surface.context.scale(scale_x, scale_y) surface.context.translate(translate_x, translate_y) surface.draw(tree) surface.context.restore() return else: png_file = BytesIO() Image.open(BytesIO(image_bytes)).save(png_file, 'PNG') png_file.seek(0) image_surface = cairo.ImageSurface.create_from_png(png_file) ### DSB ## Added: image_surface.pattern = cairo.SurfacePattern(image_surface) image_surface.pattern.set_filter(cairo.FILTER_NEAREST) ### DSB node.image_width = image_surface.get_width() node.image_height = image_surface.get_height() scale_x, scale_y, translate_x, translate_y = preserve_ratio(surface, node) # Clip image region (if necessary) if not (translate_x == 0 and translate_y == 0 and width == scale_x * node.image_width and height == scale_y * node.image_height): surface.context.rectangle(x, y, width, height) surface.context.clip() # Paint raster image surface.context.save() surface.context.translate(x, y) surface.context.scale(scale_x, scale_y) surface.context.translate(translate_x, translate_y) ### DSB ## Removed: #surface.context.set_source_surface(image_surface) ## Added: surface.context.set_source(image_surface.pattern) ### DSB surface.context.paint() surface.context.restore()
def convert(cls, bytestring=None, *, file_obj=None, url=None, dpi=96, parent_width=None, parent_height=None, scale=1, unsafe=False, background_color=None, negate_colors=False, invert_images=False, write_to=None, output_width=None, output_height=None, **kwargs): """Convert an SVG document to the format for this class. Specify the input by passing one of these: :param bytestring: The SVG source as a byte-string. :param file_obj: A file-like object. :param url: A filename. Give some options: :param dpi: The ratio between 1 inch and 1 pixel. :param parent_width: The width of the parent container in pixels. :param parent_height: The height of the parent container in pixels. :param scale: The ouptut scaling factor. :param unsafe: A boolean allowing XML entities and very large files (WARNING: vulnerable to XXE attacks and various DoS). Specifiy the output with: :param write_to: The filename of file-like object where to write the output. If None or not provided, return a byte string. Only ``bytestring`` can be passed as a positional argument, other parameters are keyword-only. """ tree = Tree(bytestring=bytestring, file_obj=file_obj, url=url, unsafe=unsafe, **kwargs) output = write_to or io.BytesIO() instance = cls(tree, output, dpi, None, parent_width, parent_height, scale, output_width, output_height, background_color, map_rgba=negate_color if negate_colors else None, map_image=invert_image if invert_images else None) instance.finish() if write_to is None: return output.getvalue()