Cairo binding for use with Gdk and Gtk widgets. It is extension for compile-time generated bindings to "libcairo-gobject2" library. This bindings is generated "gobject" shard (https://github.com/jhass/crystal-gobject). I used mainly code from "cairo-cr" shard , so it is a fork "cairo-cr" shard (https://github.com/TamasSzekeres/cairo-cr)
First install cairo:
sudo apt-get install libgirepository1.0-dev libgtk-3-dev libcairo-gobject2 gir1.2-freedesktopNote: "crystal-gobject" uses cairo-1.0.typelib from gir1.2-freedesktop package for generating cairo bindings. Different versions of Ubuntu use different versions of gir1.2-freedesktop package. If you use Ubuntu16.04 based distributives uncomment line 3 in src/cairo.cr file
require "./patch_for_ubuntu1604.cr"If you use Ubuntu18.04 based distributives uncomment Rectangle structure in src/lib_cairo.cr file (lines 7...12).
If you use Ubuntu 20.04 LTS (Focal Fossa) based distributives need install libevent-2.1-6 pkg :
sudo add-apt-repository "deb http://mirrors.kernel.org/ubuntu/ eoan main" sudo apt-get update sudo apt-get install libevent-2.1-6-
Add the dependency to your
shard.yml:dependencies: cairo-gobject: github: viachpaliy/cairo-gobject
-
Run
shards install
require "gobject/gtk" require "cairo-gobject/cairo"For more details see the sample in /samples folder.
Run sample :
cd cairo-gobject shards install crystal run samples/sample_name.crSee also samples in :
- gtk_custom_widgets - Collection of custom widgets.
TODO: Write development instructions here
- Fork it (https://github.com/viachpaliy/cairo-gobject/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
- viachpaliy - creator and maintainer
Here's a well known list of them:
- gtk_custom_widgets - Collection of custom widgets.
Cairo-gobject is a Crystal shard for working with the Cairo library. It is a set of Crystal bindings to the Cairo C library. It closely matches the C API with the exception of cases, where more Crystal way is desirable.
Cairo is a library for creating 2D vector graphics. It is written in the C programming language.
Bindings for other computer languages exist, including Python, Perl, C++, C#, or Java.
Cairo is a multiplatform library; it works on Linux, BSDs, Windows, and OSX.
Cairo supports various backends. Backends are output devices for displaying the created graphics.
- X Window System
- Win32 GDI
- Mac OS X Quartz
- PNG
- PostScript
- SVG This means that we can use the library to draw on Windows, Linux, Windows, OSX
and we can use the library to create PNG images, PDF files, PostScript files, and SVG files.
We can compare the Cairo library to the GDI+ library on Windows OS and the Quartz 2D on Mac OS.
Cairo is an open source software library. From version 2.8, Cairo is part of the GTK system.
Here we provide some useful definitions. To do some drawing in Cairo, we must first create a drawing context.
The drawing context holds all of the graphics state parameters that describe how drawing is to be done.
This includes information such as line width, color, the surface to draw to, and many other things.
It allows the actual drawing functions to take fewer arguments to simplify the interface.
A path is a collection of points used to create primitive shapes such as lines, arcs, and curves. There are two kinds of paths: open and closed paths. In a closed path, starting and ending points meet.
In an open path, starting and ending point do not meet. In Cairo, we start with an empty path.
First, we define a path and then we make them visible by stroking and/or filling them.
After each stroke or fill method call, the path is emptied. We have to define a new path.
If we want to keep the existing path for later drawing, we can use the stroke_preserve and fill_preserve methods.
A path is made of subpaths.
A source is the paint we use in drawing. We can compare the source to a pen or ink that we use to draw the outlines
and fill the shapes. There are four kinds of basic sources: colors, gradients, patterns, and images.
A surface is a destination that we are drawing to. We can render documents using the PDF or PostScript surfaces,
directly draw to a platform via the Xlib and Win32 surfaces.
Before the source is applied to the surface, it is filtered first. The mask is used as a filter.
It determines where the source is applied and where not. Opaque parts of the mask allow to copy the source. Transparent parts do not let to copy the source to the surface.
A pattern represents a source when drawing onto a surface. In Cairo, a pattern is something
that you can read from and that is used as the source or mask of a drawing operation.
Patterns can be solid, surface-based, or gradients.
In the first example, we draw on a GTK window.
This backend will be used throughout the rest of the tutorial.
require "gobject/gtk/autorun" require "../src/cairo" class CairoApp @window : Gtk::Window delegate show_all, to: @window def initialize @window = Gtk::Window.new @window.title = "Simple drawing" @window.resize 600,150 @window.connect "destroy", &->Gtk.main_quit darea = Gtk::DrawingArea.new darea.connect "draw",&->drawfun @window.add darea end def drawfun context = Gdk.cairo_create(@window.window.not_nil!) context.set_source_rgb(0, 0, 100) context.select_font_face("Sans", Cairo::FontSlant::NORMAL , Cairo::FontWeight::NORMAL) context.font_size=40 context.move_to(10,50) context.show_text("Cairo draw on a GTK window!") end end app=CairoApp.new app.show_allThe example pops up a GTK window on which we draw the "Cairo draw on a GTK window!" text.
Gtk specifically has a convenience wrapper that starts the mainloop automatically:
require "gobject/gtk/autorun"We import the Cairo module:
require "../src/cairo"In the next line we create an empty window:
@window = Gtk::Window.newWe tell it to set the value of the property title to "Simple drawing":
@window.title = "Simple drawing"We set a size of window :
@window.resize 250,150Followed by connecting to the window\92s delete event to ensure that
the application is terminated if we click on the x to close the window:
@window.connect "destroy", &->Gtk.main_quitWe will be drawing on a Gtk.DrawingArea widget:
darea = Gtk::DrawingArea.newWhen the window is redrawn, a draw signal is emitted.
We connect that signal to the drawfun callback:
darea.connect "draw",&->drawfunThe drawing is done inside the drawfun method.
We create a Cairo context from window :
context = Gdk.cairo_create(@window.window.not_nil!)We draw our text in blue ink. The ink is specified with the set_source_rgb method:
context.set_source_rgb(0, 0, 100)We choose a font type with the select_font_face method
and set its size with the set_font_size method:
context.select_font_face("Sans", Cairo::FontSlant::NORMAL , Cairo::FontWeight::NORMAL) context.font_size=40 We move to a position at x=10.0, y=50.0 and draw the text:
context.move_to(10,50) context.show_text("Cairo draw on a GTK window!")The stroke operation draws the outlines of shapes and the fill operation fills the insides of shapes.
In the example, we draw a circle and fill it with a solid color.
require "gobject/gtk/autorun" require "../src/cairo" require "math" class CairoApp @window : Gtk::Window delegate show_all, to: @window def initialize @window = Gtk::Window.new @window.title = "Fill and stroke" @window.resize 400,300 @window.connect "destroy", &->Gtk.main_quit darea = Gtk::DrawingArea.new darea.connect "draw",&->drawfun @window.add darea end def drawfun context = Gdk.cairo_create(@window.window.not_nil!) context.line_width=9 context.set_source_rgb( 0.69, 0.19, 0) context.translate(200,150) context.arc(0,0,50,0,2*Math::PI) context.stroke_preserve context.set_source_rgb( 0.30, 0.40, 0.60) context.fill end end app=CairoApp.new app.show_allThis module is needed for the pi constant which is used to draw a circle.
require "math"We set a line width with the line_width= method. We set the source to some dark red color using the set_source_rgb() method.
context.line_width=9 context.set_source_rgb( 0.69, 0.19, 0)With the translate() method, we move the drawing origin to the center of the window. We want our circle to be centered.
context.translate(200,150)The arc() method adds a new circular path to the Cairo drawing context.
context.arc(0,0,50,0,2*Math::PI)Finally, the stroke_preserve() method draws the outline of the circle. Unlike the stroke() method, it also preserves the shape for later drawing.
context.stroke_preserveWe change the color for drawing and fill the circle with a new color using the fill() method.
context.set_source_rgb( 0.30, 0.40, 0.60) context.fillEach line can be drawn with a different pen dash. A pen dash defines the style of the line. The dash pattern is specified by the set_dash() method. The pattern is set by the dash list which is a list of floating values. They set the on and off parts of the dash pattern. The dash is used by the stroke() method to create a line. If the number of dashes is 0, dashing is disabled. If the number of dashes is 1, a symmetric pattern is assumed with alternating on and off portions of the size specified by the single value in dashes.
def drawfun context = Gdk.cairo_create(@window.window.not_nil!) context.set_source_rgb( 0.69, 0.19, 0) context.line_width=1.5 context.set_dash([4.0, 21.0, 2.0], 0) context.move_to(40, 30) context.line_to(200, 30) context.stroke context.set_dash([14.0, 6.0], 0) context.move_to(40,50) context.line_to(200,50) context.stroke context.set_dash([1.0], 0) context.move_to(40,70) context.line_to(200,70) context.stroke end We draw three lines in three different pen dashes.
context.set_dash([4.0, 21.0, 2.0], 0)We have a pattern of three numbers. We have 4 points drawn, 21 not drawn, and 2 drawn, then 4 points not drawn, 21 points drawn. and 2 not drawn. This pattern takes turns until the end of the line.
context.set_dash([14.0, 6.0], 0)In this pattern, we have always 14 points drawn and 6 not drawn.
context.set_dash([1.0], 0)Here we create a pen dash of a symmetric pattern of alternating single on and off points.
The line caps are end points of lines.
There are three different line cap styles in Cairo :
- Cairo::LineCap::SQUARE
- Cairo::LineCap::ROUND
- Cairo::LineCap::BUTT

A line with aCairo::LINE_CAP_SQUAREcap has a different size than a line with aCairo::LINE_CAP_BUTTcap. If a line is x units wide, the line with aCairo::LINE_CAP_SQUAREcap will be exactly x units greater in size; x/2 units at the beginning and x/2 units at the end.
def drawfun context = Gdk.cairo_create(@window.window.not_nil!) context.set_source_rgb( 0.3, 0.19, 0.4) context.line_width=10 context.line_cap=Cairo::LineCap::BUTT context.move_to(30, 50) context.line_to(150, 50) context.stroke context.line_cap=Cairo::LineCap::ROUND context.move_to(30, 90) context.line_to(150, 90) context.stroke context.line_cap=Cairo::LineCap::SQUARE context.move_to(30, 130) context.line_to(150, 130) context.stroke context.line_width=1.5 context.move_to(30, 40) context.line_to(30, 140) context.stroke context.move_to(150, 40) context.line_to(150, 140) context.stroke context.move_to(155, 40) context.line_to(155, 140) context.stroke end The example draws three lines with three different line caps. It will also graphically demonstrate the differences in size of the lines by drawing three additional thin vertical lines.
context.line_width=10Our lines will be 10 px wide.
context.line_cap=Cairo::LineCap::ROUND context.move_to(30, 90) context.line_to(150, 90) context.stroke Here we draw a horizontal line with a Cairo::LINE_CAP_ROUND cap.
context.line_width=1.5 context.move_to(30, 40) context.line_to(30, 140) context.strokeThis is one of the three vertical lines used to demostrate the differences in size.
The lines can be joined using three different join styles :
def drawfun context = Gdk.cairo_create(@window.window.not_nil!) context.set_source_rgb( 0.3, 0.19, 0.4) context.line_width=14 context.rectangle(30, 30, 100, 100) context.line_join=Cairo::LineJoin::MITER context.stroke context.rectangle(160, 30, 100, 100) context.line_join=Cairo::LineJoin::BEVEL context.stroke context.rectangle(100, 160, 100, 100) context.line_join=Cairo::LineJoin::ROUND context.stroke end In this example, we draw three thick rectangles with various line joins.
context.line_width=14The lines are 14 px wide.
context.rectangle(30, 30, 100, 100) context.line_join=Cairo::LineJoin::MITER context.stroke Here we draw a rectangle with cairo.LINE_JOIN_MITER join style.
