def fileformat(filename, width, height): ''' figure out the output image format Args: filename: output filename, which will raise an error if it does not end in one of '.pdf', '.png', '.ps', or '.svg' width: width of the output image, in pixels height: height of the output image, in pixels Returns: tuple of cairo.Surface and filetype string e.g. 'pdf' or 'png' ''' if filename is not None: _, ext = os.path.splitext(filename) if not ext: ext = '.png' ext = ext[1:].lower() else: ext = None assert ext in ['png', 'pdf', 'ps', 'svg', None], 'unknown format: .{}'.format(ext) if ext == 'pdf': surface = cairo.PDFSurface(filename, width, height) elif ext == 'svg': surface = cairo.SVGSurface(filename, width, height) elif ext == 'ps': surface = cairo.PSSurface(filename, width, height) else: surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height) return ext, surface
def create_new_surface(file_format, target=None, width=1024, height=768): """ Create a new surface of the specified `file_format`: "png" for :class:`ImageSurface` "svg" for :class:`SVGSurface` "pdf" for :class:`PDFSurface` "ps" for :class:`PSSurface` The surface will be written to the `target` parameter , which can be a path to save the surface to, or file-like object with a `write()` method. You can also optionally specify the `width` and `height` of the generated surface if you know what it is; otherwise a default size of 1024 by 768 is used. """ file_format = file_format.lower() if file_format == 'png': surface = cairo.ImageSurface( cairo.FORMAT_ARGB32, int(width), int(height)) elif file_format == 'svg': surface = cairo.SVGSurface(target, width, height) elif file_format == 'pdf': surface = cairo.PDFSurface(target, width, height) elif file_format == 'ps': surface = cairo.PSSurface(target, width, height) else: raise ValueError( 'Invalid value "{0}" for type parameter; valid values are "png", "svg", "pdf", and "ps".'.format(type)) return surface
def output_file(ctx): root, extension = os.path.splitext(target) if file_number: filename = '%s_%04d%s' % (root, file_number, extension) else: filename = target extension = extension.lower() if extension == '.png': surface = ctx.get_target() surface.write_to_png(target) elif extension == '.pdf': target_ctx = cairo.Context( cairo.PDFSurface(filename, *self.size_or_default())) target_ctx.set_source_surface(ctx.get_target()) target_ctx.paint() elif extension in ('.ps', '.eps'): target_ctx = cairo.Context( cairo.PSSurface(filename, *self.size_or_default())) if extension == '.eps': target_ctx.set_eps(extension='.eps') target_ctx.set_source_surface(ctx.get_target()) target_ctx.paint() elif extension == '.svg': target_ctx = cairo.Context( cairo.SVGSurface(filename, *self.size_or_default())) target_ctx.set_source_surface(ctx.get_target()) target_ctx.paint() return filename
def __init__( self, image=None, # PIL image size=None, ctx=None, imageType=None, # determines file type fileName=None, # if set determines output file name ): """ Canvas can be used in four modes: 1) using the supplied PIL image 2) using the supplied cairo context ctx 3) writing to a file fileName with image type imageType 4) creating a cairo surface and context within the constructor """ self.image = None self.imageType = imageType if image is not None: try: imgd = getattr(image, 'tobytes', image.tostring)("raw", "BGRA") except SystemError: r, g, b, a = image.split() mrg = Image.merge("RGBA", (b, g, r, a)) imgd = getattr(mrg, 'tobytes', mrg.tostring)("raw", "RGBA") a = array.array('B', imgd) stride = image.size[0] * 4 surface = cairo.ImageSurface.create_for_data( a, cairo.FORMAT_ARGB32, image.size[0], image.size[1], stride) ctx = cairo.Context(surface) size = image.size[0], image.size[1] self.image = image elif ctx is None and size is not None: if hasattr(cairo, "PDFSurface") and imageType == "pdf": surface = cairo.PDFSurface(fileName, size[0], size[1]) elif hasattr(cairo, "SVGSurface") and imageType == "svg": surface = cairo.SVGSurface(fileName, size[0], size[1]) elif hasattr(cairo, "PSSurface") and imageType == "ps": surface = cairo.PSSurface(fileName, size[0], size[1]) elif imageType == "png": surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, size[0], size[1]) else: raise ValueError( "Unrecognized file type. Valid choices are pdf, svg, ps, and png" ) ctx = cairo.Context(surface) ctx.set_source_rgb(1, 1, 1) ctx.paint() else: surface = ctx.get_target() if size is None: try: size = surface.get_width(), surface.get_height() except AttributeError: size = None self.ctx = ctx self.size = size self.surface = surface self.fileName = fileName
def _make_vector_surface(self, output_path, image_format, image_width, crop_margin): """ Make a vector surface in the appropriate format and with the appropriate size depending on whether or not there is a crop margin. In a vector image, 1 screen pixel is scaled to a certain number of points, such that the figure as a whole will conform to a certain physical size. """ if crop_margin is None: scale = image_width / self.screen_width image_height = self.screen_height * scale else: crop_margin = _mm_to_pts(crop_margin) if crop_margin > image_width / 3: raise ValueError( "The crop margin set on this image is too large for the image width. Increase the image width or decrease the crop margin." ) scale = (image_width - crop_margin * 2) / ( self._block_extents[2] - self._block_extents[0] ) image_height = ( self._block_extents[3] - self._block_extents[1] ) * scale + crop_margin * 2 if image_format == "PDF": surface = _cairo.PDFSurface(output_path, image_width, image_height) surface.set_metadata(_cairo.PDF_METADATA_CREATOR, f"eyekit {__version__}") elif image_format == "EPS": surface = _cairo.PSSurface(output_path, image_width, image_height) surface.set_eps(True) elif image_format == "SVG": surface = _cairo.SVGSurface(output_path, image_width, image_height) surface.set_device_scale(scale, scale) return surface, scale, crop_margin
def _save(self, fo, fmt, **kwargs): # save PDF/PS/SVG orientation = kwargs.get('orientation', 'portrait') dpi = 72 self.figure.dpi = dpi w_in, h_in = self.figure.get_size_inches() width_in_points, height_in_points = w_in * dpi, h_in * dpi if orientation == 'landscape': width_in_points, height_in_points = ( height_in_points, width_in_points) if fmt == 'ps': if not hasattr(cairo, 'PSSurface'): raise RuntimeError('cairo has not been compiled with PS ' 'support enabled') surface = cairo.PSSurface(fo, width_in_points, height_in_points) elif fmt == 'pdf': if not hasattr(cairo, 'PDFSurface'): raise RuntimeError('cairo has not been compiled with PDF ' 'support enabled') surface = cairo.PDFSurface(fo, width_in_points, height_in_points) elif fmt in ('svg', 'svgz'): if not hasattr(cairo, 'SVGSurface'): raise RuntimeError('cairo has not been compiled with SVG ' 'support enabled') if fmt == 'svgz': if isinstance(fo, six.string_types): fo = gzip.GzipFile(fo, 'wb') else: fo = gzip.GzipFile(None, 'wb', fileobj=fo) surface = cairo.SVGSurface(fo, width_in_points, height_in_points) else: warnings.warn("unknown format: %s" % fmt) return # surface.set_dpi() can be used renderer = RendererCairo(self.figure.dpi) renderer.set_width_height(width_in_points, height_in_points) renderer.set_ctx_from_surface(surface) ctx = renderer.gc.ctx if orientation == 'landscape': ctx.rotate(np.pi/2) ctx.translate(0, -height_in_points) # cairo/src/cairo_ps_surface.c # '%%Orientation: Portrait' is always written to the file header # '%%Orientation: Landscape' would possibly cause problems # since some printers would rotate again ? # TODO: # add portrait/landscape checkbox to FileChooser self.figure.draw(renderer) ctx.show_page() surface.finish() if fmt == 'svgz': fo.close()
def __init__(self, filename, entity, options): self.color = (0, 0, 0) self.factor = 1 self.background_color = (0, 0, 0) self.analyse_options(options) self.surface = cairo.SVGSurface(filename, 10, 10) self.context = cairo.Context(self.surface) self.factor = 1 self.height = self.compute_height(entity) self.width = self.compute_width(entity) self.line_length = default_line_length self.surface = cairo.PDFSurface( filename, self.width + self.line_length * 2 + bbox_w_margin * 2, self.height + bbox_h_margin * 2) self.context = cairo.Context(self.surface) self.compute_wire_length(entity) if options.format.lower() == "svg": self.factor = 1 self.surface = cairo.SVGSurface( filename, self.width + self.line_length * 2 + bbox_w_margin * 2, self.height + bbox_h_margin * 2) if options.format.lower() == "pdf": self.factor = 1 self.surface = cairo.PDFSurface( filename, self.width + self.line_length * 2 + bbox_w_margin * 2, self.height + bbox_h_margin * 2) if options.format.lower() == "ps": self.factor = 1 self.surface = cairo.PSSurface( filename, self.width + self.line_length * 2 + bbox_w_margin * 2, self.height + bbox_h_margin * 2) if options.format.lower() == "png": self.factor = float( options.width / (self.width + self.factor * self.line_length * 2 + self.factor * bbox_w_margin * 2)) stride = cairo.ImageSurface.format_stride_for_width( cairo.FORMAT_ARGB32, 10000) data = bytearray(stride * 10000) # stride = cairo.ImageSurface.format_stride_for_width(cairo.FORMAT_ARGB32, int(self.width)+1) # data = bytearray(stride * int(self.height)) self.surface = cairo.ImageSurface( cairo.FORMAT_ARGB32, int(self.factor * self.width + self.factor * self.line_length * 2 + self.factor * bbox_w_margin * 2), int(self.factor * self.height + self.factor * bbox_h_margin * 2), data, stride) self.context = cairo.Context(self.surface) self.draw_background(self.context) self.draw_entity(entity) self.surface.write_to_png(options.filename)
def _save(self, fo, fmt, **kwargs): # save PDF/PS/SVG orientation = kwargs.get('orientation', 'portrait') dpi = 72 self.figure.dpi = dpi w_in, h_in = self.figure.get_size_inches() width_in_points, height_in_points = w_in * dpi, h_in * dpi if orientation == 'landscape': width_in_points, height_in_points = (height_in_points, width_in_points) if fmt == 'ps': if not hasattr(cairo, 'PSSurface'): raise RuntimeError('cairo has not been compiled with PS ' 'support enabled') surface = cairo.PSSurface(fo, width_in_points, height_in_points) elif fmt == 'pdf': if not hasattr(cairo, 'PDFSurface'): raise RuntimeError('cairo has not been compiled with PDF ' 'support enabled') surface = cairo.PDFSurface(fo, width_in_points, height_in_points) elif fmt in ('svg', 'svgz'): if not hasattr(cairo, 'SVGSurface'): raise RuntimeError('cairo has not been compiled with SVG ' 'support enabled') if fmt == 'svgz': if isinstance(fo, str): fo = gzip.GzipFile(fo, 'wb') else: fo = gzip.GzipFile(None, 'wb', fileobj=fo) surface = cairo.SVGSurface(fo, width_in_points, height_in_points) else: warnings.warn("unknown format: %s" % fmt, stacklevel=2) return # surface.set_dpi() can be used renderer = RendererCairo(self.figure.dpi) renderer.set_width_height(width_in_points, height_in_points) renderer.set_ctx_from_surface(surface) ctx = renderer.gc.ctx if orientation == 'landscape': ctx.rotate(np.pi / 2) ctx.translate(0, -height_in_points) # Perhaps add an '%%Orientation: Landscape' comment? self.figure.draw(renderer) ctx.show_page() surface.finish() if fmt == 'svgz': fo.close()
def create_rcontext(self, size, frame): """ Called when CairoCanvas needs a cairo context to draw on """ if self.format == 'pdf': surface = cairo.PDFSurface(self._output_file(frame), *size) elif self.format in ('ps', 'eps'): surface = cairo.PSSurface(self._output_file(frame), *size) elif self.format == 'svg': surface = cairo.SVGSurface(self._output_file(frame), *size) else: surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, *size) return cairo.Context(surface)
def _make_surface(self, output_path, figure_format, figure_width, figure_height): """ Make the relevant Cairo surface and context with appropriate sizing. """ if figure_format == "PDF": surface = _cairo.PDFSurface(output_path, figure_width, figure_height) surface.set_metadata(_cairo.PDF_METADATA_CREATOR, f"eyekit {__version__}") elif figure_format == "EPS": surface = _cairo.PSSurface(output_path, figure_width, figure_height) surface.set_eps(True) elif figure_format == "SVG": surface = _cairo.SVGSurface(output_path, figure_width, figure_height) context = _cairo.Context(surface) return surface, context
def main(): import argparse parser = argparse.ArgumentParser(description='A tool to generate mazes') def maze_size(s): result = int(s) if result < 1: raise argparse.ArgumentTypeError( 'The maze size must be greater than 0') else: return result parser.add_argument('--maze-size', type=maze_size, nargs=2, metavar=('WIDTH', 'HEIGHT'), default=(15, 10), help='The size of the maze.') maze_classes = dict( (len(mc.Wall.WALLS), mc) for mc in (Maze, TriMaze, HexMaze)) parser.add_argument('--walls', type=int, choices=maze_classes.keys(), default=4, help='The number of walls for every room.') def char(s): if len(s) != 1: raise argparse.ArgumentTypeError('%s is not a valid character' % s) else: return s parser.add_argument( '--print-wall-char', type=char, default='@', help='The character used for walls when printing the maze.') parser.add_argument( '--print-path-char', type=char, default='.', help='The character used for the path when printing the maze.') parser.add_argument( '--print-floor-char', type=char, default=' ', help='The character used for the floor when printing the maze.') def print_room_size(s): result = int(s) if result < 3: raise argparse.ArgumentTypeError( 'The room size must be greater than or equal to 3') else: return result parser.add_argument( '--print-room-size', type=print_room_size, nargs=2, default=(5, 4), help='The size of each room in characters when printing the maze.') def image_room_size(s): result = int(s) if result < 1: raise argparse.ArgumentTypeError( 'The maze room size in the image must be greater than 0') else: return int(0.5 * math.sqrt(2.0) * result) parser.add_argument('--image-room-size', type=image_room_size, nargs=2, metavar=('WIDTH', 'HEIGHT'), default=(30, 30), help='The size of the rooms in the maze image.') surface_types = { 'pdf': (lambda f, w, h: cairo.PDFSurface(f, w, h), lambda f, surface: None), 'png': (lambda f, w, h: cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h), lambda f, surface: surface.write_to_png(f)), 'ps': (lambda f, w, h: cairo.PSSurface(f, w, h), lambda f, surface: None), 'svg': (lambda f, w, h: cairo.SVGSurface(f, w, h), lambda f, surface: None) } def surface(s): try: ext = s.rsplit(os.path.extsep, 1)[1] return (lambda w, h: surface_types[ext][0] (s, w, h), lambda surface: surface_types[ext][1] (s, surface)) except KeyError as e: argparse.ArgumentTypeError('"%s" is not a valid file extension' % e.args[0]) except IndexError: argparse.ArgumentTypeError( 'The image file must have a valid extension') class default: def write(*args, **kwargs): pass parser.add_argument( '--image-output', type=surface, metavar='FILENAME', required=True, help=('The name of the image file to create. Valid types are %s.') % (', '.join(surface_types.keys()))) def color(allow_rgba): def rgb(s): result = None m = re.match( r'''(?x) \#(?P<red>[0-9A-Fa-f]{2}) (?P<green>[0-9A-Fa-f]{2}) (?P<blue>[0-9A-Fa-f]{2})''', s) if not m is None: result = tuple(float(int(d, 16)) / 255 for d in m.groups()) else: m = re.match( r'''(?x) rgb\( \s*(?P<red>\d{1,3}%?)\s*, \s*(?P<green>\d{1,3}%?)\s*, \s*(?P<blue>\d{1,3}%?)\s*\)''', s) if not m is None: result = tuple( float(d) / 255 if d[-1].isdigit() else float(d[:-1]) / 100 for d in m.groups()) if result is None or any(r < 0 or r > 1.0 for r in result): raise argparse.ArgumentTypeError( '"%s" is not a valid colour.' % s) return result + (1.0, ) def rgba(s): result = None m = re.match( r'''(?x) \#(?P<alpha>[0-9A-Fa-f]{2}) (?P<red>[0-9A-Fa-f]{2}) (?P<green>[0-9A-Fa-f]{2}) (?P<blue>[0-9A-Fa-f]{2})''', s) if not m is None: result = tuple( float(int(d, 16)) / 255 for d in m.groups()[1:] + (m.group(1), )) else: m = re.match( r'''(?x) rgba\( \s*(?P<red>\d{1,3}%?)\s*, \s*(?P<green>\d{1,3}%?)\s*, \s*(?P<blue>\d{1,3}%?)\s*\, \s*(?P<alpha>\d{1,3}%?)\s*\)''', s) if not m is None: result = tuple( float(d) / 255 if d[-1].isdigit() else float(d[:-1]) / 100 for d in m.groups()) if result is None: return rgb(s) elif any(r < 0 or r > 1.0 for r in result): raise argparse.ArgumentTypeError( '"%s" is not a valid colour.' % s) return result if allow_rgba: return rgba else: return rgb parser.add_argument( '--image-background-color', type=color(True), metavar='COLOUR', default=(0.0, 0.0, 0.0), help='The background colour of the image. This must be specified as ' 'an HTML colour on the form #RRGGBB or rgb(r, g, b), or #AARRGGBB ' 'or rgba(r, g, b, a).') parser.add_argument( '--image-wall-color', type=color(False), metavar='COLOUR', default=(1.0, 1.0, 1.0), help='The colour of the wall in the image. This must be specified as ' 'an HTML colour on the form #RRGGBB or rgb(r, g, b).') parser.add_argument( '--image-path-color', type=color(True), metavar='COLOUR', default=(0.8, 0.4, 0.2), help='The colour of the path in the image. This must be specified as ' 'an HTML colour on the form #RRGGBB or rgb(r, g, b), or #AARRGGBB ' 'or rgba(r, g, b, a).') def line_width(s): result = int(s) if result < 2: raise argparse.ArgumentTypeError( 'Line widths must be greater than 1') elif result & 1: raise argparse.ArgumentTypeError( 'Line widths must be an even number') else: return result parser.add_argument('--image-wall-width', type=line_width, metavar='WIDTH', default=2, help='The width of the maze wall lines.') parser.add_argument('--image-path-width', type=line_width, metavar='WIDTH', default=2, help='The width of the maze path lines.') parser.add_argument( '--image-path-smooth', action='store_true', default=False, help='Whether the path should be painted as a smooth curve instead ' 'of a sharp line.') namespace = parser.parse_args() # Create and initialise the maze maze = maze_classes[namespace.walls](*namespace.maze_size) initialize(maze, lambda max: random.randint(0, max - 1)) solution = list(maze.walk_path((0, 0), (maze.width - 1, maze.height - 1))) print_maze( maze, solution, **dict((name.split('_', 1)[1], value) for name, value in vars(namespace).items() if name.startswith('print_'))) make_image( maze, solution, **dict((name.split('_', 1)[1], value) for name, value in vars(namespace).items() if name.startswith('image_')))
def _save(self, fo, format, **kwargs): # save PDF/PS/SVG orientation = kwargs.get('orientation', 'portrait') dpi = 72 self.figure.dpi = dpi w_in, h_in = self.figure.get_size_inches() width_in_points, height_in_points = w_in * dpi, h_in * dpi if orientation == 'landscape': width_in_points, height_in_points = (height_in_points, width_in_points) if format == 'ps': if not hasattr(cairo, 'PSSurface'): raise RuntimeError('cairo has not been compiled with PS ' 'support enabled') surface = cairo.PSSurface(fo, width_in_points, height_in_points) elif format == 'pdf': if not hasattr(cairo, 'PDFSurface'): raise RuntimeError('cairo has not been compiled with PDF ' 'support enabled') surface = cairo.PDFSurface(fo, width_in_points, height_in_points) elif format in ('svg', 'svgz'): if not hasattr(cairo, 'SVGSurface'): raise RuntimeError('cairo has not been compiled with SVG ' 'support enabled') if format == 'svgz': filename = fo if is_string_like(fo): fo = open(fo, 'wb') close = True else: close = False try: fo = gzip.GzipFile(None, 'wb', fileobj=fo) finally: if close: fo.close() surface = cairo.SVGSurface(fo, width_in_points, height_in_points) else: warnings.warn("unknown format: %s" % format) return # surface.set_dpi() can be used renderer = RendererCairo(self.figure.dpi) renderer.set_width_height(width_in_points, height_in_points) renderer.set_ctx_from_surface(surface) ctx = renderer.gc.ctx if orientation == 'landscape': ctx.rotate(np.pi / 2) ctx.translate(0, -height_in_points) # cairo/src/cairo_ps_surface.c # '%%Orientation: Portrait' is always written to the file header # '%%Orientation: Landscape' would possibly cause problems # since some printers would rotate again ? # TODO: # add portrait/landscape checkbox to FileChooser self.figure.draw(renderer) show_fig_border = False # for testing figure orientation and scaling if show_fig_border: ctx.new_path() ctx.rectangle(0, 0, width_in_points, height_in_points) ctx.set_line_width(4.0) ctx.set_source_rgb(1, 0, 0) ctx.stroke() ctx.move_to(30, 30) ctx.select_font_face('sans-serif') ctx.set_font_size(20) ctx.show_text('Origin corner') ctx.show_page() surface.finish()
def _create_surface(self, width, height): """Create and return ``(cairo_surface, width, height)``.""" cairo_surface = cairo.PSSurface(self.output, width, height) cairo_surface.set_eps(True) return cairo_surface, width, height