cairo
| Language | C (C++ bindings: cairomm) |
|---|---|
| License | LGPL-2.1 or MPL-1.1 |
| Repository | gitlab.freedesktop.org/cairo/cairo |
| Documentation | PostScript surfaces, cairo manual |
| Depends on | pixman, FreeType, fontconfig, zlib (libpng for PNG output) |
| Read / write | no / yes (PostScript Level 2 and 3, EPS) |
cairo draws paths, text and images with one API onto many targets: windows, PNG images, PDF, SVG and PostScript. Draw on a PostScript surface and cairo writes a DSC-conforming file with embedded font subsets. GTK, Firefox (in the past), Inkscape, Poppler and many others use it.
cairo only writes PostScript. PostScript has no transparency, so cairo turns transparent areas into embedded raster images (“fallback images”); avoid alpha when you want pure vector output. Unlike PostScript, cairo’s origin is the top-left corner and y goes down.
Installation#
# Debian / Ubuntu
sudo apt install libcairo2-dev
# Fedora
sudo dnf install cairo-devel
# macOS
brew install cairo
# Windows (vcpkg)
vcpkg install cairo
cc app.c $(pkg-config --cflags --libs cairo) -o app
Basic usage: write a two-page PostScript file#
#include <cairo.h>
#include <cairo-ps.h>
#include <stdio.h>
int main(void) {
// page size in points (1/72 inch): A4 is 595 x 842
cairo_surface_t *surface = cairo_ps_surface_create("out.ps", 595, 842);
cairo_t *cr = cairo_create(surface);
// page 1: text and a filled rectangle
cairo_select_font_face(cr, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
cairo_set_font_size(cr, 24);
cairo_move_to(cr, 72, 72); // 1 inch from the left and from the top
cairo_show_text(cr, "Hello, PostScript");
cairo_set_source_rgb(cr, 0.2, 0.4, 0.8);
cairo_rectangle(cr, 72, 120, 300, 150);
cairo_fill(cr);
cairo_show_page(cr);
// page 2: a circle
cairo_set_source_rgb(cr, 0.8, 0.1, 0.1);
cairo_set_line_width(cr, 4);
cairo_arc(cr, 297, 421, 100, 0, 2 * 3.14159265);
cairo_stroke(cr);
cairo_show_page(cr);
cairo_destroy(cr);
cairo_surface_finish(surface); // writes the trailer
cairo_status_t status = cairo_surface_status(surface);
cairo_surface_destroy(surface);
if (status != CAIRO_STATUS_SUCCESS) {
fprintf(stderr, "cairo error: %s\n", cairo_status_to_string(status));
return 1;
}
return 0;
}
How to use different page sizes in one file#
Call cairo_ps_surface_set_size before drawing on the page:
cairo_ps_surface_set_size(surface, 842, 595); // landscape A4 for the next page
// draw ...
cairo_show_page(cr);
How to add DSC comments (title, paper size)#
Header comments go before any drawing; page setup comments go after cairo_ps_surface_dsc_begin_page_setup on each page:
cairo_surface_t *surface = cairo_ps_surface_create("out.ps", 842, 1191);
cairo_ps_surface_dsc_comment(surface, "%%Title: Quarterly report");
cairo_ps_surface_dsc_begin_setup(surface);
cairo_ps_surface_dsc_comment(surface, "%%IncludeFeature: *MediaColor White");
cairo_ps_surface_dsc_begin_page_setup(surface);
cairo_ps_surface_dsc_comment(surface, "%%IncludeFeature: *PageSize A3");
Each comment must start with % and be at most 255 characters.
How to write PostScript Level 2 for old printers#
cairo_ps_surface_restrict_to_level(surface, CAIRO_PS_LEVEL_2);
Call it before drawing. The default is Level 3.
How to write PostScript to memory or a socket#
static cairo_status_t write_cb(void *closure, const unsigned char *data, unsigned int length) {
FILE *f = closure;
return fwrite(data, 1, length, f) == length ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_WRITE_ERROR;
}
cairo_surface_t *surface = cairo_ps_surface_create_for_stream(write_cb, stdout, 595, 842);
Tips#
cairo_ps_surface_set_eps(surface, 1)writes Encapsulated PostScript instead; see cairo for EPS.- C++ code can use cairomm, which wraps the same API in classes (
Cairo::PsSurface). - Fonts are embedded as subsets (Type 1, Type 42 or Type 3), so the file prints without the fonts installed.