#! /usr/bin/env python import cairo from math import pi, sqrt class Diagram(object): def __init__(self, filename, width, height): self.surface = cairo.SVGSurface(filename + '.svg', width, height) cr = self.cr = cairo.Context(self.surface) cr.scale(width, height) cr.set_line_width(0.01) cr.rectangle(0, 0, 1, 1) cr.set_source_rgb(1, 1, 1) cr.fill() self.draw_dest(cr) cr.set_line_width( max(cr.device_to_user_distance(2, 2)) ) cr.set_source_rgb(0, 0, 0) cr.rectangle(0, 0, 1, 1) cr.stroke() self.surface.write_to_png(filename + '.png') cr.show_page() self.surface.finish() class SetSourceRGBA(Diagram): def draw_dest(self, cr): cr.set_source_rgb(0, 0, 0) #rgba cr.move_to(0, 0) #rgba cr.line_to(1, 1) #rgba cr.move_to(1, 0) #rgba cr.line_to(0, 1) #rgba cr.set_line_width(0.2) #rgba cr.stroke() #rgba #rgba cr.rectangle(0, 0, 0.5, 0.5) #rgba cr.set_source_rgba(1, 0, 0, 0.80) #rgba cr.fill() #rgba #rgba cr.rectangle(0, 0.5, 0.5, 0.5) #rgba cr.set_source_rgba(0, 1, 0, 0.60) #rgba cr.fill() #rgba #rgba cr.rectangle(0.5, 0, 0.5, 0.5) #rgba cr.set_source_rgba(0, 0, 1, 0.40) #rgba cr.fill() #rgba class SetSourceGradient(Diagram): def draw_dest(self, cr): radial = cairo.RadialGradient(0.25, 0.25, 0.1, 0.5, 0.5, 0.5) #gradient radial.add_color_stop_rgb(0, 1.0, 0.8, 0.8) #gradient radial.add_color_stop_rgb(1, 0.9, 0.0, 0.0) #gradient #gradient for i in range(1, 10): #gradient for j in range(1, 10): #gradient cr.rectangle(i/10.0 - 0.04, j/10.0 - 0.04, 0.08, 0.08) #gradient cr.set_source(radial) #gradient cr.fill() #gradient #gradient linear = cairo.LinearGradient(0.25, 0.35, 0.75, 0.65) #gradient linear.add_color_stop_rgba(0.00, 1, 1, 1, 0) #gradient linear.add_color_stop_rgba(0.25, 0, 1, 0, 0.5) #gradient linear.add_color_stop_rgba(0.50, 1, 1, 1, 0) #gradient linear.add_color_stop_rgba(0.75, 0, 0, 1, 0.5) #gradient linear.add_color_stop_rgba(1.00, 1, 1, 1, 0) #gradient #gradient cr.rectangle(0.0, 0.0, 1, 1) #gradient cr.set_source(linear) #gradient cr.fill() #gradient class PathDiagram(Diagram): def draw_dest(self, cr): self.draw_dest_path(cr) path = list(cr.copy_path_flat()) cr.set_line_width(max(cr.device_to_user_distance(3, 3))) cr.set_source_rgb(0, 0.6, 0) cr.stroke() if len(path) and path[-1][0] != cairo.PATH_CLOSE_PATH: x, y = path[0][1] cr.arc(x, y, max(cr.device_to_user_distance(5, 5)), 0, 2*pi) cr.set_source_rgba(0.0, 0.6, 0.0, 0.5) cr.fill() x, y = path[-1][1] cr.arc(x, y, max(cr.device_to_user_distance(5, 5)), 0, 2*pi) cr.set_source_rgba(0.0, 0.0, 0.75, 0.5) cr.fill() class PathDiagramMoveTo(PathDiagram): def draw_dest_path(self, cr): cr.move_to(0.25, 0.25) #moveto class PathDiagramLineTo(PathDiagramMoveTo): def draw_dest_path(self, cr): PathDiagramMoveTo.draw_dest_path(self, cr) cr.line_to(0.5, 0.375) #lineto cr.rel_line_to(0.25, -0.125) #lineto class PathDiagramArcTo(PathDiagramLineTo): def draw_dest_path(self, cr): PathDiagramLineTo.draw_dest_path(self, cr) cr.arc(0.5, 0.5, 0.25 * sqrt(2), -0.25 * pi, 0.25 * pi) #arc class PathDiagramCurveTo(PathDiagramArcTo): def draw_dest_path(self, cr): if type(self) == PathDiagramCurveTo: cr.save() for x, y in ((0.5, 0.625), (0.5, 0.875)): cr.new_sub_path() cr.arc(x, y, max(cr.device_to_user_distance(3, 3)), 0, 2*pi) cr.set_source_rgba(0.5, 0, 0, 0.5) cr.fill() for (x, y, w, h) in ((0.25, 0.75, 0.25, 0.125), (0.75, 0.75, -0.25, -0.125)): cr.move_to(x, y) cr.rel_line_to(w, h) cr.set_line_width(max(cr.device_to_user_distance(2, 2))) cr.set_source_rgba(0.5, 0, 0, 0.25) cr.stroke() cr.restore() PathDiagramArcTo.draw_dest_path(self, cr) cr.rel_curve_to(-0.25, -0.125, -0.25, 0.125, -0.5, 0) #curveto class PathDiagramClose(PathDiagramCurveTo): def draw_dest_path(self, cr): PathDiagramCurveTo.draw_dest_path(self, cr) cr.close_path() #closepath class TextExtents(Diagram): def draw_dest(self, cr): text = 'joy' cr.select_font_face('Georgia', cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD) cr.set_font_size(0.5) px = max(cr.device_to_user_distance(1, 1)) fascent, fdescent, fheight, fxadvance, fyadvance = cr.font_extents() xbearing, ybearing, width, height, xadvance, yadvance = \ cr.text_extents(text) x = 0.5 - xbearing - width / 2 y = 0.5 - fdescent + fheight / 2 # baseline, descent, ascent, height cr.set_line_width(4 * px) cr.set_dash([9 * px], 0) cr.set_source_rgba(0, 0.6, 0, 0.5) cr.move_to(x + xbearing, y) cr.rel_line_to(width, 0) cr.move_to(x + xbearing, y + fdescent) cr.rel_line_to(width, 0) cr.move_to(x + xbearing, y - fascent) cr.rel_line_to(width, 0) cr.move_to(x + xbearing, y - fheight) cr.rel_line_to(width, 0) cr.stroke() # extents: width & height cr.set_source_rgba(0, 0, 0.75, 0.5) cr.set_line_width(1 * px) cr.set_dash([3 * px], 0) cr.rectangle(x + xbearing, y + ybearing, width, height) cr.stroke() # text cr.move_to(x, y) cr.set_source_rgb(0, 0, 0) cr.show_text(text) # bearing cr.set_dash([], 0) cr.set_line_width(2 * px) cr.set_source_rgba(0, 0, 0.75, 0.5) cr.move_to(x, y) cr.rel_line_to(xbearing, ybearing) cr.stroke() # text's advance cr.set_source_rgba(0, 0, 0.75, 0.5) cr.arc(x + xadvance, y + yadvance, 5 * px, 0, 2 * pi) cr.fill() # reference point cr.arc(x, y, 5 * px, 0, 2 * pi) cr.set_source_rgba(0.75, 0, 0, 0.5) cr.fill() if __name__ == '__main__': size = 120 SetSourceRGBA('setsourcergba', size, size) SetSourceGradient('setsourcegradient', size, size) PathDiagramMoveTo('path-moveto', size, size) PathDiagramLineTo('path-lineto', size, size) PathDiagramArcTo('path-arcto', size, size) PathDiagramCurveTo('path-curveto', size, size) PathDiagramClose('path-close', size, size) TextExtents('textextents', size, size)