+static struct output_driver *
+xr_png_create (struct file_handle *fh, enum settings_output_devices device_type,
+ struct string_map *o)
+{
+ return xr_create (fh, device_type, o, XR_PNG);
+}
+
+static void
+xr_set_surface_size (cairo_surface_t *surface, enum xr_output_type output_type,
+ double width, double height)
+{
+ switch (output_type)
+ {
+ case XR_PDF:
+ cairo_pdf_surface_set_size (surface, width, height);
+ break;
+
+ case XR_PS:
+ cairo_ps_surface_set_size (surface, width, height);
+ break;
+
+ case XR_SVG:
+ case XR_PNG:
+ NOT_REACHED ();
+ }
+}
+
+static void
+xr_copy_surface (cairo_surface_t *dst, cairo_surface_t *src,
+ double x, double y)
+{
+ cairo_t *cr = cairo_create (dst);
+ cairo_set_source_surface (cr, src, x, y);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+}
+
+static void
+clear_rectangle (cairo_surface_t *surface,
+ double x0, double y0, double x1, double y1)
+{
+ cairo_t *cr = cairo_create (surface);
+ cairo_set_source_rgb (cr, 1, 1, 1);
+ cairo_new_path (cr);
+ cairo_rectangle (cr, x0, y0, x1 - x0, y1 - y0);
+ cairo_fill (cr);
+ cairo_destroy (cr);
+}
+
+static void
+xr_report_error (cairo_status_t status, const char *file_name)
+{
+ if (status != CAIRO_STATUS_SUCCESS)
+ fprintf (stderr, "%s: %s\n", file_name, cairo_status_to_string (status));
+}
+
+static void
+xr_finish_page (struct xr_driver *xr)
+{
+ xr_pager_finish_page (xr->pager);
+
+ /* For 'trim' true:
+
+ - If the destination is PDF or PostScript, set the dest surface size, copy
+ ink extent, show_page.
+
+ - If the destination is PNG, create image surface, copy ink extent,
+ cairo_surface_write_to_png(), destroy image surface.
+
+ - If the destination is SVG, create svg surface, copy ink extent, close.
+
+ then destroy drawing_surface and make a new one.
+
+ For 'trim' false:
+
+ - If the destination is PDF or PostScript, show_page.
+
+ - If the destination is PNG, cairo_surface_write_to_png(), destroy image
+ surface, create new image surface.
+
+ - If the destination is SVG, create svg surface, copy whole thing, close.
+
+ */
+ double paper[TABLE_N_AXES];
+ for (int a = 0; a < TABLE_N_AXES; a++)
+ paper[a] = xr_to_pt (xr_page_style_paper_size (
+ xr->page_style, xr->fsm_style, a));
+
+ xr->page_number++;
+ char *file_name = (xr->page_number > 1
+ ? xasprintf ("%s-%d", xr->driver.name, xr->page_number)
+ : xr->driver.name);
+
+ if (xr->trim)
+ {
+ /* Get the bounding box for the drawing surface. */
+ double ofs[TABLE_N_AXES], size[TABLE_N_AXES];
+ cairo_recording_surface_ink_extents (xr->drawing_surface,
+ &ofs[H], &ofs[V],
+ &size[H], &size[V]);
+ const int (*margins)[2] = xr->page_style->margins;
+ for (int a = 0; a < TABLE_N_AXES; a++)
+ {
+ double scale = XR_POINT;
+ size[a] += (margins[a][0] + margins[a][1]) / scale;
+ ofs[a] = -ofs[a] + margins[a][0] / scale;
+ }
+
+ switch (xr->output_type)
+ {
+ case XR_PDF:
+ case XR_PS:
+ xr_set_surface_size (xr->dest_surface, xr->output_type,
+ size[H], size[V]);
+ xr_copy_surface (xr->dest_surface, xr->drawing_surface,
+ ofs[H], ofs[V]);
+ cairo_surface_show_page (xr->dest_surface);
+ break;
+
+ case XR_SVG:
+ {
+ cairo_surface_t *svg = cairo_svg_surface_create (
+ file_name, size[H], size[V]);
+ xr_copy_surface (svg, xr->drawing_surface, ofs[H], ofs[V]);
+ xr_report_error (cairo_surface_status (svg), file_name);
+ cairo_surface_destroy (svg);
+ }
+ break;
+
+ case XR_PNG:
+ {
+ cairo_surface_t *png = cairo_image_surface_create (
+ CAIRO_FORMAT_ARGB32, size[H], size[V]);
+ clear_rectangle (png, 0, 0, size[H], size[V]);
+ xr_copy_surface (png, xr->drawing_surface, ofs[H], ofs[V]);
+ xr_report_error (cairo_surface_write_to_png (png, file_name),
+ file_name);
+ cairo_surface_destroy (png);
+ }
+ break;
+ }
+
+ /* Destroy the recording surface and create a fresh one of the same
+ size. */
+ cairo_surface_destroy (xr->drawing_surface);
+ xr->drawing_surface = cairo_recording_surface_create (
+ CAIRO_CONTENT_COLOR_ALPHA,
+ &(cairo_rectangle_t) { .width = paper[H], .height = paper[V] });
+ }
+ else
+ {
+ switch (xr->output_type)
+ {
+ case XR_PDF:
+ case XR_PS:
+ cairo_surface_show_page (xr->dest_surface);
+ break;
+
+ case XR_SVG:
+ {
+ cairo_surface_t *svg = cairo_svg_surface_create (
+ file_name, paper[H], paper[V]);
+ xr_copy_surface (svg, xr->drawing_surface, 0.0, 0.0);
+ xr_report_error (cairo_surface_status (svg), file_name);
+ cairo_surface_destroy (svg);
+ }
+ break;
+
+ case XR_PNG:
+ xr_report_error (cairo_surface_write_to_png (xr->drawing_surface,
+ file_name), file_name);
+ cairo_surface_destroy (xr->drawing_surface);
+ xr->drawing_surface = cairo_image_surface_create (
+ CAIRO_FORMAT_ARGB32, paper[H], paper[V]);
+ break;
+ }
+ }
+
+ if (file_name != xr->driver.name)
+ free (file_name);
+}
+