# Vector graphics: Cairo and SVG Raster tools commit to a resolution; vector tools describe shapes and decide the pixels later, or never. This chapter covers the two vector routes in Perl. `Cairo` is a full anti-aliased 2D engine that rasterizes paths to a pixel surface (or to PDF and SVG): you draw with real-number coordinates and it works out the coverage. `SVG` generates SVG markup, a text description of shapes that a browser or renderer draws later at whatever size it likes. Use `Cairo` when you need pixels now with the quality of a proper rasterizer; use `SVG` when you want resolution-independent output as a file that other tools consume. `Cairo` also returns in [chapter 9](ffi-cairo-showcase.md): pperl ships a native reimplementation of it, so the same code runs with no CPAN Cairo installed, and that chapter shows how the native binding works. Here it is the ordinary CPAN module. ## Cairo: paths and the drawing model `Cairo`’s model is a **path** built up from segments, then either **stroked** (the outline drawn) or **filled** (the interior painted). You set a source color, build a path with `move_to` and `line_to` and curves, and commit it. Colors are floats in `0 .. 1`, not `0 .. 255`. ```perl use Cairo; # an ARGB surface is a pixel buffer with alpha my $surface = Cairo::ImageSurface->create('argb32', 200, 200); my $cr = Cairo::Context->create($surface); # paint a white background: set source, then paint fills everything $cr->set_source_rgb(1, 1, 1); $cr->paint; # a stroked path $cr->set_source_rgb(0, 0, 0); $cr->set_line_width(3); $cr->move_to(20, 20); $cr->line_to(180, 20); $cr->curve_to(180, 100, 20, 100, 20, 180); # a bezier curve $cr->stroke; # a filled, semi-transparent circle (arc angles are in radians) $cr->arc(100, 100, 40, 0, 6.28318); # full circle = 2*pi $cr->set_source_rgba(1, 0, 0, 0.5); # red at 50% alpha $cr->fill; $surface->write_to_png('output.png'); ``` Three things to internalize. Coordinates are real numbers, so `move_to(100.5, 47.25)` is meaningful and the [half-pixel offset](foundations.md) from the foundations chapter is how you get crisp thin lines. Arc angles are in **radians**, not degrees, so a full circle is `2 * pi`. And `set_source_rgba`’s alpha gives you the [compositing](foundations.md) for free: fill or stroke with alpha below 1 and Cairo blends over what is already there. `stroke` and `fill` consume the current path. If you want to both fill and stroke the same shape, use `fill_preserve` (or `stroke_preserve`), which keeps the path for a second operation: ```perl $cr->arc(100, 100, 40, 0, 6.28318); $cr->set_source_rgb(0.4, 0.8, 1); $cr->fill_preserve; # fill, but keep the path $cr->set_source_rgb(0, 0, 0); $cr->set_line_width(2); $cr->stroke; # now outline the same circle ``` ### Premultiplied alpha The `argb32` surface stores pixels with [premultiplied alpha](foundations.md): each color channel is already multiplied by its alpha. This is invisible when you draw through Cairo’s API, but it matters the moment you read raw pixel bytes off the surface or feed Cairo’s buffer to another library, because the bytes are not the straight RGBA you might assume. When you cross that boundary, un-multiply first. Within Cairo’s own drawing it never comes up. ## SVG: markup, not pixels `SVG` is pure Perl and does something different: it builds an XML document describing shapes, and produces text. Nothing is rasterized; the consumer (a browser, Inkscape, a PDF converter) draws it. That makes it perfect for output that must stay sharp at any zoom, or that other tools will process further. ```perl use SVG; my $svg = SVG->new(width => 200, height => 200); # a group carries shared style; children inherit it my $g = $svg->group(id => 'shapes', style => { stroke => 'black', fill => 'none' }); $g->line(x1 => 0, y1 => 0, x2 => 200, y2 => 200); $g->circle(cx => 100, cy => 100, r => 50, style => { fill => 'skyblue' }); $g->rect(x => 10, y => 10, width => 40, height => 40); $g->path(d => 'M10 190 L100 100 L190 190 Z'); # a triangle via path data $svg->text(x => 60, y => 110)->cdata('Hello'); print $svg->xmlify; ``` The style hash maps to SVG’s presentation attributes: `stroke`, `fill`, `stroke-width`, `opacity`, and the rest. A `group` (aliased `g`) with a style is how you avoid repeating attributes on every element; children inherit and can override. Text is attached with a chained `cdata` because the character content is a child node, not an attribute. For a polygon from computed points, `get_path` assembles the points string for you: ```perl my @x = (50, 150, 100); my @y = (10, 10, 90); my $points = $svg->get_path(x => \@x, y => \@y, -type => 'polygon'); $g->polygon(%$points, style => { fill => 'coral' }); ``` `xmlify` (also spelled `to_xml` or `render`) returns the finished document as a string. Write it to a `.svg` file and it opens in any browser; hand it to a converter for PDF or PNG. ## Cairo or SVG? | You want … | Reach for | |--------------------------------------------------------------|-------------| | Pixels now, anti-aliased, from real-number coordinates | `Cairo` | | Vector output as a file for browsers or other tools | `SVG` | | To render the same drawing to PNG, PDF, and SVG from one API | `Cairo` | | No compiled dependency at all (pure Perl) | `SVG` | The two also compose: generate structure as `SVG` markup, then render it to pixels with a rasterizer when you need a bitmap. For applied vector output in the specific shape of charts and graphs, and for reading the metadata already inside image files, the [next chapter](metadata-and-charts.md) covers the pragmatic bookends of raster and vector work.