PyCairo Visual Documentation

Author: Nicolas Seriot
Date: 2024-08-12 17:37:28
PyCairo Version: 1.18.0
GitHub: https://github.com/nst/PyCairoVisualDoc

Content

  1. Shapes
  2. Transforms
  3. Clip and mask
  4. Patterns
  5. Gradients
  6. Text and fonts
  7. Antialiasing
  8. Animated gifs
  9. Formats
1_shapes.basic_shapes /1_shapes/basic_shapes.py
import cairo
import math

def draw_png(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 256)
    c = cairo.Context(surface)
    
    # background
    c.set_source_rgb(1,1,1)
    c.paint()

    # lines
    c.set_source_rgb(0,0,0)
    c.move_to(50, 100)
    c.line_to(100, 150)
    c.rel_line_to(150, -100)
    c.rel_move_to(-10, -10)
    c.rel_line_to(20, 20)
    c.set_line_width(2)
    c.stroke()
    
    # rectangles
    c.set_line_width(6)
    c.rectangle(300, 50, 100, 50)
    c.stroke()
    
    # arcs
    c.set_source_rgb(1,0,1)
    # x, y, radius, start_angle, stop_angle
    c.arc(200, 180, 40, 0, 2*math.pi)
    c.fill_preserve()
    c.set_line_width(2)
    c.set_source_rgb(0,0,0)
    c.stroke()
    
    surface.write_to_png(filename)
1_shapes.bezier_curve /1_shapes/bezier_curve.py
import cairo
import math

def draw_png(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 256)
    c = cairo.Context(surface)
    
    x0, y0 = 50, 50
    x1, y1 = 180, 220    
    x2, y2 = 350, 180
    x3, y3 = 400, 50
        
    c.move_to(x0, y0)
    c.curve_to(x1, y1, x2, y2, x3, y3)        
    c.stroke()
    
    for x,y in [(x0, y0), (x1, y1), (x2, y2), (x3, y3)]:
        c.arc(x-2, y-2, 4, 0, 2*math.pi)
        c.stroke()
    
    surface.write_to_png(filename)
1_shapes.dashed_lines /1_shapes/dashed_lines.py
import cairo

def draw_png(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 300)
    c = cairo.Context(surface)

    c.set_line_width(5)
    
    c.set_source_rgb(0,0,0)
    
    # 15 drawn, 10 not drawn, etc
    c.set_dash([15, 10])

    c.move_to(50, 50)  
    c.line_to(400, 50)
    c.stroke()

    # 10 drawn, 30 not drawn, 5 drawn, then
    # 10 *not* drawn, 30 drawn, 5 not drawn, etc
    c.set_dash([10, 30, 5])

    c.move_to(50, 100)
    c.line_to(400, 100)
    c.stroke()

    surface.write_to_png(filename)
1_shapes.line_join_line_cap /1_shapes/line_join_line_cap.py
import cairo

def draw_png(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 300)
    c = cairo.Context(surface)
    
    c.set_line_width(20)
    
    c.move_to(50, 100)
    c.rel_line_to(50, -50)
    c.rel_line_to(50, 50)
    c.set_line_join(cairo.LINE_JOIN_MITER) # default
    c.stroke()
    
    c.move_to(50, 150)
    c.rel_line_to(50, -50)
    c.rel_line_to(50, 50)
    c.set_line_join(cairo.LINE_JOIN_BEVEL)
    c.stroke()

    c.move_to(50, 200)
    c.rel_line_to(50, -50)
    c.rel_line_to(50, 50)
    c.set_line_join(cairo.LINE_JOIN_ROUND)
    c.stroke()

    c.move_to(200, 50)
    c.rel_line_to(0, 150)
    c.set_line_cap(cairo.LINE_CAP_BUTT)
    c.stroke()

    c.move_to(240, 50)
    c.rel_line_to(0, 150)
    c.set_line_cap(cairo.LINE_CAP_ROUND)
    c.stroke()

    c.move_to(280, 50)
    c.rel_line_to(0, 150)
    c.set_line_cap(cairo.LINE_CAP_SQUARE)
    c.stroke()

    c.rectangle(350, 50, 100, 100)        
    c.set_line_join(cairo.LINE_JOIN_ROUND)
    c.stroke()
    
    surface.write_to_png(filename)
2_transforms.invert_y_coordinates /2_transforms/invert_y_coordinates.py
import cairo

def draw_line(c):
    c.move_to(10, 10)
    c.line_to(100, 100)
    c.stroke()

def draw_png(filename):

    s = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 256)
    c = cairo.Context(s)
    
    c.set_source_rgb(1,0,0)
    draw_line(c)
    
    # invert y coordinates
    m = cairo.Matrix(yy=-1, y0=s.get_height())
    c.transform(m)
    
    c.set_source_rgb(0,0,1)
    draw_line(c)
    
    s.write_to_png(filename)

if __name__ == "__main__":

    draw()
2_transforms.matrix /2_transforms/matrix.py
import cairo
import math

def draw_rect(c):
    c.set_source_rgb(1,0,0)
    c.rectangle(20, 20, 150, 100)
    c.fill()

def draw_png_asd(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 256)
    c = cairo.Context(surface)
    
    draw_rect(c)
    
    # x_new = xx * x + xy * y + x0
    # y_new = yx * x + yy * y + y0

    # cairo.Matrix(xx, yx,
    #              xy, yy,
    #              x0, y0)
    
    m = cairo.Matrix(1.0, 0.5,
                     0,   1.0,
                     256, 0.0)
    
    c.transform(m)
    
    draw_rect(c)
    
    surface.write_to_png(filename)
2_transforms.rotate /2_transforms/rotate.py
import cairo
import math

def draw_png_rotate(filename):

    surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, 512, 300)
    c = cairo.Context(surface)
    
    c.set_source_rgb(1,1,1)
    c.paint()
    
    c.set_source_rgb(0,0,0)
    
    c.save()
    c.translate(50, 50)
    c.rectangle(0, 0, 250, 150)
    c.stroke()
    c.restore()
    
    c.save()
    c.translate(250, 150)
    c.rotate(math.pi / 4.0)
    c.rectangle(0, 0, 250, 150)
    c.stroke()
    c.restore()
    
    surface.write_to_png(filename)
2_transforms.translate_and_scale /2_transforms/translate_and_scale.py
import cairo
import math

def draw_picture(c, x, y, scale):

    c.save()
    
    c.translate(x, y)
    c.scale(scale, scale)

    c.set_source_rgb(1, 0, 0)
    c.arc(120, 120, 120, 0, 2*math.pi)
    c.fill()

    c.set_source_rgb(1, 1, 1)
    c.arc(160, 90, 40, 0, 2*math.pi)
    c.fill()
        
    c.restore()

def draw_png_asd(filename):

    surface = cairo.ImageSurface (cairo.FORMAT_ARGB32, 512, 300)
    #surface.set_device_scale(2,2)
    c = cairo.Context(surface)
    #c.set_antialias(cairo.ANTIALIAS_NONE)
    
    c.set_source_rgb(1, 1, 1)
    c.paint()
    
    draw_picture(c, x=30, y=30, scale=1)
    draw_picture(c, x=350, y=50, scale=0.5)
    draw_picture(c, x=290, y=170, scale=0.25)
    
    surface.write_to_png(filename)
3_clip_and_mask.clip /3_clip_and_mask/clip.py
    c.arc(100, 100, 75, 0, 2*math.pi)
    
    c.save()
    c.clip()
    c.set_source_surface(ims, 0, 0)    
    c.paint()
    c.restore()

    c.set_source_surface(ims, 256, 0)    
    c.paint()
3_clip_and_mask.mask /3_clip_and_mask/mask.py
import cairo
import math
import os

def create_from_png(image_name):
    # ensure we can read image when working from another dir
    cwd = os.getcwd()
    dir_path = os.path.dirname(os.path.realpath(__file__))
    os.chdir(dir_path)
    ims = cairo.ImageSurface.create_from_png(image_name)
    os.chdir(cwd)
    return ims

def draw_png(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 256)
    c = cairo.Context(surface)

    # draw mask alone
    mask_img = create_from_png('mask.png')  
    c.set_source_surface(mask_img, 0, 0)
    c.paint()
    
    church_img = create_from_png('church.png')  
    c.set_source_surface(church_img, 256, 0)
    c.paint()
    
    # clip masking area
    c.rectangle(256, 0, church_img.get_width(), church_img.get_height())
    c.clip()
    
    # apply mask
    c.set_operator(cairo.OPERATOR_DEST_IN)
    c.set_source_surface(mask_img, 256, 0)
    c.paint()
    
    surface.write_to_png(filename)

if __name__ == "__main__":

    draw_png("masked_church.png")
4_patterns.patterns /4_patterns/patterns.py
#!/usr/bin/env python

import cairo
import math
import os

def create_from_png(image_name):
    # ensure we can read image when working from another dir
    cwd = os.getcwd()
    dir_path = os.path.dirname(os.path.realpath(__file__))
    os.chdir(dir_path)
    ims = cairo.ImageSurface.create_from_png(image_name)
    os.chdir(cwd)
    return ims

def draw_png(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 256)
    c = cairo.Context(surface)
    
    pat_surface = create_from_png("brick.png")
    pat = cairo.SurfacePattern(pat_surface)
    pat.set_extend(cairo.EXTEND_REPEAT)
    
    c.set_source(pat)
    c.arc(100, 100, 50, 0, 2*math.pi)
    c.fill()
    
    c.set_source_surface(pat_surface, 256, 50)    
    c.paint()

    surface.write_to_png(filename)

if __name__ == "__main__":

    draw_gif_sqlogo("brick.png")
4_patterns.repeating_pattern /4_patterns/repeating_pattern.py
#!/usr/bin/env python

import cairo
import math
import os

def create_from_png(image_name):
    # ensure we can read image when working from another dir
    cwd = os.getcwd()
    dir_path = os.path.dirname(os.path.realpath(__file__))
    os.chdir(dir_path)
    ims = cairo.ImageSurface.create_from_png(image_name)
    os.chdir(cwd)
    return ims

def draw_png(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 256)
    c = cairo.Context(surface)
    
    # pattern surface
    ps = cairo.ImageSurface(cairo.FORMAT_ARGB32, 50, 50)
    pc = cairo.Context(ps)
    
    # draw blue circles on the pattern surface context
    pc.set_source_rgb(1, 1, 1)
    pc.paint()
    pc.set_source_rgb(0, 0, 1)
    pc.arc(25, 25, 10, 0, 2 * 3.14159)
    pc.fill()

    # create the pattern
    p = cairo.SurfacePattern(ps)
    p.set_extend(cairo.EXTEND_REPEAT)

    # Use the pattern to fill the main surface
    c.set_source(p)
    c.rectangle(100, 50, 300, 200)
    c.fill()
    
    surface.write_to_png(filename)

if __name__ == "__main__":

    draw_png("repeating_pattern.png")
5_gradients.linear_gradient /5_gradients/linear_gradient.py
#!/usr/bin/env python

import cairo
import math

def draw_png(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 256)
    c = cairo.Context(surface)
    
    x0, y0 = 20, 20
    x1, y1 = 20, 220
    lg = cairo.LinearGradient(x0, y0, x1, y1)
    lg.add_color_stop_rgba(0.0, 1, 0, 0, 1) 
    lg.add_color_stop_rgba(0.5, 0, 1, 0, 1) 
    lg.add_color_stop_rgba(1.0, 0, 0, 1, 1) 

    c.rectangle(20, 20, 200, 200)
    c.set_source(lg)
    c.fill()
    
    surface.write_to_png(filename)

if __name__ == "__main__":

    draw_png("x.gif")
5_gradients.radial_gradients /5_gradients/radial_gradients.py
#!/usr/bin/env python

import cairo
import math

def draw_png(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 256)
    c = cairo.Context(surface)
    
    # cx0 - x coordinate for the center of the start circle
    # cy0 - y coordinate for the center of the start circle
    # radius0 - radius of the start circle
    # cx1 - x coordinate for the center of the end circle
    # cy1 - y coordinate for the center of the end circle
    # radius1 - radius of the end circle

    pat = cairo.RadialGradient(135, 130, 15,
                               140, 140, 40)
    pat.add_color_stop_rgba(0, 1, 1, 1, 1)
    pat.add_color_stop_rgba(0.3, 0.5, 0.5, 1, 1)
    pat.add_color_stop_rgba(1, 0, 0, 1, 1)
    c.set_source(pat)
    c.arc(150, 150, 50, 0, 2*math.pi)
    c.fill()
    
    surface.write_to_png(filename)

if __name__ == "__main__":

    draw_png("radial_gradient.png")
6_text_and_fonts.text /6_text_and_fonts/text.py
#!/usr/bin/env python

import cairo

def draw_png(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 256)
    c = cairo.Context(surface)

    # choose font
        
    c.select_font_face("Courier New")
    c.set_font_size(14)

    # basic example

    c.move_to(50, 50)
    c.show_text("Hello World")

    # right align

    for y, adj in [(100, "naughty"), (120, "cool"), (140, "bad")]:
        s = "%s cats" % adj
        _, _, txt_width, _, _, _ = c.text_extents(s)        
        c.move_to(150 - txt_width, y)
        c.show_text(s)
    
    # pick another font
       
    c.select_font_face("Times",
                       cairo.FONT_SLANT_ITALIC,
                       cairo.FONT_WEIGHT_BOLD)
    c.set_font_size(56)

    c.move_to(200, 100)
    c.show_text("Hi there")

    surface.write_to_png(filename)

if __name__ == "__main__":

    draw_gif_sqlogo("x.gif")
7_antialiasing.antialiasing /7_antialiasing/antialiasing.py
#!/usr/bin/env python

import cairo
import math

def draw_png(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 512, 256)
    c = cairo.Context(surface)
    
    c.select_font_face("Courier New")
    c.set_font_size(28)

    fo = cairo.FontOptions()
    fo.set_antialias(cairo.ANTIALIAS_DEFAULT)
    c.set_font_options(fo)

    c.move_to(50, 50)
    c.show_text("Antialias Default")    

    fo = cairo.FontOptions()
    fo.set_antialias(cairo.ANTIALIAS_NONE)
    c.set_font_options(fo)

    c.move_to(50, 100)
    c.show_text("Antialias None")    

    #
    
    c.set_antialias(cairo.ANTIALIAS_DEFAULT)

    c.arc(170, 170, 50, 0, 2*math.pi)
    c.set_source_rgba(1,0,0,1)
    c.fill()

    c.set_antialias(cairo.ANTIALIAS_NONE)

    c.arc(370, 170, 50, 0, 2*math.pi)
    c.set_source_rgba(1,0,0,1)
    c.fill()
    
    surface.write_to_png(filename)

if __name__ == "__main__":

    draw_png("x.gif")
8_animated_gifs.gif /8_animated_gifs/gif.py
import os
import imageio

def images_paths():
    # ensure we can read images when working from another dir
    cwd = os.getcwd()
    dir_path = os.path.dirname(os.path.realpath(__file__))
    os.chdir(dir_path)
    files = [os.path.sep.join([dir_path, s])
             for s in os.listdir(dir_path)
             if s.endswith('.png')]
    files.sort()
    os.chdir(cwd)
    return files

def draw_gif(filename):

    images = []
    
    for s in images_paths():
        images.append(imageio.imread(s))
    
    imageio.mimsave(filename, images, format='GIF', duration=0.5)

if __name__ == "__main__":

    draw_gif("movie.gif")
8_animated_gifs.sq_logo_animated /8_animated_gifs/sq_logo_animated.py
#!/usr/bin/env python

import cairo
import math
import imageio
import numpy

from PIL import Image

COLOR_ORANGE_SQ = (250/255., 92/255., 53/255.)
COLOR_WHITE = (1, 1, 1)

def as_numpy_array(surface):

    w = surface.get_width()
    h = surface.get_height()
    
    data = surface.get_data()
    
    a = numpy.ndarray(shape=(h,w), dtype=numpy.uint32, buffer=data)
    
    i = Image.frombytes("RGBA", (w,h), a, "raw", "BGRA", 0, 1)
    
    return numpy.asarray(i)

def add_image(writer, surface):
    
    a = as_numpy_array(surface)
    writer.append_data(a)
    
def draw_gif_sqlogo(filename):

    gif_writer = imageio.get_writer(filename, mode='I', duration=0.5)
        
    w, h = 280, 280
    
    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
    c = cairo.Context(surface)
    
    # white background
    
    c.set_source_rgb(1, 1, 1)
    c.paint()
    c.fill()
    #add_image(gif_writer, surface)
    
    c.translate(20, 20)

    # main circle
    
    c.set_source_rgb (*COLOR_ORANGE_SQ)
    c.arc(120, 120, 120, 0, 2*math.pi)
    c.fill()
    add_image(gif_writer, surface)
    
    # white rects
    
    c.set_source_rgb (*COLOR_WHITE)
    c.rectangle(60, 70, 120, 20)
    c.fill()
    add_image(gif_writer, surface)

    c.rectangle(60, 110, 120, 60)
    c.fill()
    add_image(gif_writer, surface)
    
    # orange part of the bridge
    
    c.set_source_rgb (*COLOR_ORANGE_SQ)
    c.rectangle(80, 145, 30, 25)
    c.fill()
    add_image(gif_writer, surface)
    
    c.rectangle(130, 145, 30, 25)
    c.fill()
    add_image(gif_writer, surface)

    c.arc(95, 145,  15, 0, 2*math.pi)
    c.fill()
    add_image(gif_writer, surface)

    c.arc(145, 145, 15, 0, 2*math.pi)
    c.fill()
    add_image(gif_writer, surface)

if __name__ == "__main__":

    draw_gif_sqlogo("sq.gif")
eps.eps /9_formats/eps.py
import cairo
from cairo import PSSurface
import math

def draw_eps(filename):
    
    surface = PSSurface(filename, 320, 240)
    surface.set_eps(True)
    c = cairo.Context(surface)

    c.set_source_rgb(1, 1, 1)
    c.paint()

    c.arc(100, 80, 50, 0, 2*math.pi)
    c.set_source_rgba(1,0,0,1)
    c.fill()
    
    surface.show_page()
multipage_pdf.pdf /9_formats/multipage_pdf.py
import cairo
from cairo import PDFSurface
import math

def draw_pdf(filename):
    
    surface = PDFSurface(filename, 320, 240)
    c = cairo.Context(surface)

    c.set_source_rgb(1, 1, 1)
    c.paint()

    c.arc(150, 150, 50, 0, 2*math.pi)
    c.set_source_rgba(1,0,0,1)
    c.fill()

    surface.show_page()

    c.set_source_rgb(1, 1, 1)
    c.paint()

    c.arc(100, 100, 50, 0, 2*math.pi)
    c.set_source_rgba(1,0,1,1)
    c.fill()

    surface.show_page()
pdf.pdf /9_formats/pdf.py
import cairo
from cairo import PDFSurface
import math

def draw_pdf(filename):
    
    surface = PDFSurface(filename, 320, 240)
    c = cairo.Context(surface)

    c.set_source_rgb(1, 1, 1)
    c.paint()

    c.arc(100, 80, 50, 0, 2*math.pi)
    c.set_source_rgba(1,0,0,1)
    c.fill()

    surface.show_page()
9_formats.png /9_formats/png.py
import cairo
import math

def draw_png(filename):

    surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 320, 240)
    c = cairo.Context(surface)

    c.set_source_rgb(1, 1, 1)
    c.paint()

    c.arc(100, 80, 50, 0, 2*math.pi)
    c.set_source_rgba(1,0,0,1)
    c.fill()
    
    surface.write_to_png(filename)
ps.ps /9_formats/ps.py
import cairo
from cairo import PSSurface
import math

def draw_ps(filename):
    
    surface = PSSurface(filename, 320, 240)
    c = cairo.Context(surface)

    c.set_source_rgb(1, 1, 1)
    c.paint()

    c.arc(100, 80, 50, 0, 2*math.pi)
    c.set_source_rgba(1,0,0,1)
    c.fill()

    surface.show_page()
9_formats.svg /9_formats/svg.py
import cairo
from cairo import SVGSurface
import math

def draw_svg_asd(filename):
    
    surface = SVGSurface(filename, 320, 240)
    c = cairo.Context(surface)

    c.set_source_rgb(1, 1, 1)
    c.paint()

    c.arc(100, 80, 50, 0, 2*math.pi)
    c.set_source_rgba(1,0,0,1)
    c.fill()

    surface.finish()