-static void
-xr_destroy (struct output_driver *driver)
-{
- struct xr_driver *xr = xr_driver_cast (driver);
- size_t i;
-
- xr_driver_destroy_fsm (xr);
-
- if (xr->cairo != NULL)
- {
- cairo_surface_finish (xr->surface);
- cairo_status_t status = cairo_status (xr->cairo);
- if (status != CAIRO_STATUS_SUCCESS)
- msg (ME, _("error drawing output for %s driver: %s"),
- output_driver_get_name (driver),
- cairo_status_to_string (status));
- cairo_surface_destroy (xr->surface);
-
- cairo_destroy (xr->cairo);
- }
-
- for (i = 0; i < XR_N_FONTS; i++)
- {
- struct xr_font *font = &xr->fonts[i];
-
- if (font->desc != NULL)
- pango_font_description_free (font->desc);
- if (font->layout != NULL)
- g_object_unref (font->layout);
- }
-
- free (xr->params);
- free (xr);
-}
-
-static void
-xr_flush (struct output_driver *driver)
-{
- struct xr_driver *xr = xr_driver_cast (driver);
-
- cairo_surface_flush (cairo_get_target (xr->cairo));
-}
-
-static void
-xr_update_page_setup (struct output_driver *driver,
- const struct page_setup *ps)
-{
- struct xr_driver *xr = xr_driver_cast (driver);
-
- xr->initial_page_number = ps->initial_page_number;
- xr->object_spacing = ps->object_spacing * 72 * XR_POINT;
-
- if (xr->cairo)
- return;
-
- int usable[TABLE_N_AXES];
- for (int i = 0; i < 2; i++)
- usable[i] = (ps->paper[i]
- - (ps->margins[i][0] + ps->margins[i][1])) * 72 * XR_POINT;
-
- int headings_height[2];
- usable[V] -= xr_measure_headings (
- xr->surface, xr->fonts[XR_FONT_PROPORTIONAL].desc, ps->headings,
- usable[H], xr->object_spacing, headings_height);
-
- enum table_axis h = ps->orientation == PAGE_LANDSCAPE;
- enum table_axis v = !h;
- if (!xr_check_fonts (xr->surface, xr->fonts, usable[h], usable[v]))
- return;
-
- for (int i = 0; i < 2; i++)
- {
- page_heading_uninit (&xr->headings[i]);
- page_heading_copy (&xr->headings[i], &ps->headings[i]);
- xr->headings_height[i] = headings_height[i];
- }
- xr->width = usable[h];
- xr->length = usable[v];
- xr->left_margin = ps->margins[h][0] * 72 * XR_POINT;
- xr->right_margin = ps->margins[h][1] * 72 * XR_POINT;
- xr->top_margin = ps->margins[v][0] * 72 * XR_POINT;
- xr->bottom_margin = ps->margins[v][1] * 72 * XR_POINT;
- cairo_pdf_surface_set_size (xr->surface,
- ps->paper[h] * 72.0, ps->paper[v] * 72.0);
-}
-
-static void
-xr_submit (struct output_driver *driver, const struct output_item *output_item)
-{
- struct xr_driver *xr = xr_driver_cast (driver);
-
- if (is_page_setup_item (output_item))
- {
- xr_update_page_setup (driver,
- to_page_setup_item (output_item)->page_setup);
- return;
- }
-
- if (!xr->cairo)
- {
- xr->page_number = xr->initial_page_number - 1;
- xr_set_cairo (xr, cairo_create (xr->surface));
- cairo_save (xr->cairo);
- xr_driver_next_page (xr, xr->cairo);
- }
-
- xr_driver_output_item (xr, output_item);
- while (xr_driver_need_new_page (xr))
- {
- cairo_restore (xr->cairo);
- cairo_show_page (xr->cairo);
- cairo_save (xr->cairo);
- xr_driver_next_page (xr, xr->cairo);
- }
-}
-\f
-/* Functions for rendering a series of output items to a series of Cairo
- contexts, with pagination.
-
- Used by PSPPIRE for printing, and by the basic Cairo output driver above as
- its underlying implementation.
-
- See the big comment in cairo.h for intended usage. */
-
-/* Gives new page CAIRO to XR for output. */
-void
-xr_driver_next_page (struct xr_driver *xr, cairo_t *cairo)
-{
- cairo_save (cairo);
- cairo_set_source_rgb (cairo, xr->bg.red, xr->bg.green, xr->bg.blue);
- cairo_rectangle (cairo, 0, 0, xr->width, xr->length);
- cairo_fill (cairo);
- cairo_restore (cairo);
-
- cairo_translate (cairo,
- xr_to_pt (xr->left_margin),
- xr_to_pt (xr->top_margin + xr->headings_height[0]));
-
- xr->page_number++;
- xr->cairo = cairo;
- xr->x = xr->y = 0;
-
- xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
- &xr->headings[0], xr->page_number, xr->width, true,
- -xr->headings_height[0]);
- xr_render_page_heading (xr->cairo, xr->fonts[XR_FONT_PROPORTIONAL].desc,
- &xr->headings[1], xr->page_number, xr->width, true,
- xr->length);
-
- xr_driver_run_fsm (xr);
-}
-
-/* Start rendering OUTPUT_ITEM to XR. Only valid if XR is not in the middle of
- rendering a previous output item, that is, only if xr_driver_need_new_page()
- returns false. */
-void
-xr_driver_output_item (struct xr_driver *xr,
- const struct output_item *output_item)
-{
- assert (xr->fsm == NULL);
- xr->fsm = xr_render_output_item (xr, output_item);
- xr_driver_run_fsm (xr);
-}
-
-/* Returns true if XR is in the middle of rendering an output item and needs a
- new page to be appended using xr_driver_next_page() to make progress,
- otherwise false. */
-bool
-xr_driver_need_new_page (const struct xr_driver *xr)
-{
- return xr->fsm != NULL;
-}
-
-/* Returns true if the current page doesn't have any content yet. */
-bool
-xr_driver_is_page_blank (const struct xr_driver *xr)
-{
- return xr->y == 0;
-}
-
-static void
-xr_driver_destroy_fsm (struct xr_driver *xr)
-{
- if (xr->fsm != NULL)
- {
- xr->fsm->destroy (xr->fsm);
- xr->fsm = NULL;
- }
-}
-
-static void
-xr_driver_run_fsm (struct xr_driver *xr)
-{
- if (xr->fsm != NULL && !xr->fsm->render (xr->fsm, xr))
- xr_driver_destroy_fsm (xr);
-}
-\f
-static void
-xr_layout_cell (struct xr_driver *, const struct table_cell *,
- int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
- int *width, int *height, int *brk);
-
-static void
-set_source_rgba (cairo_t *cairo, const struct cell_color *color)
-{
- cairo_set_source_rgba (cairo,
- color->r / 255., color->g / 255., color->b / 255.,
- color->alpha / 255.);
-}
-
-static void
-dump_line (struct xr_driver *xr, int x0, int y0, int x1, int y1, int style,
- const struct cell_color *color)
-{
- cairo_new_path (xr->cairo);
- set_source_rgba (xr->cairo, color);
- cairo_set_line_width (
- xr->cairo,
- xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2
- : style == RENDER_LINE_THIN ? XR_LINE_WIDTH / 2
- : XR_LINE_WIDTH));
- cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
- cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
- cairo_stroke (xr->cairo);
-}
-
-static void UNUSED
-dump_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
-{
- cairo_new_path (xr->cairo);
- cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
- cairo_move_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y));
- cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y0 + xr->y));
- cairo_line_to (xr->cairo, xr_to_pt (x1 + xr->x), xr_to_pt (y1 + xr->y));
- cairo_line_to (xr->cairo, xr_to_pt (x0 + xr->x), xr_to_pt (y1 + xr->y));
- cairo_close_path (xr->cairo);
- cairo_stroke (xr->cairo);
-}
-
-static void
-fill_rectangle (struct xr_driver *xr, int x0, int y0, int x1, int y1)
-{
- cairo_new_path (xr->cairo);
- cairo_set_line_width (xr->cairo, xr_to_pt (XR_LINE_WIDTH));
- cairo_rectangle (xr->cairo,
- xr_to_pt (x0 + xr->x), xr_to_pt (y0 + xr->y),
- xr_to_pt (x1 - x0), xr_to_pt (y1 - y0));
- cairo_fill (xr->cairo);
-}
-
-/* Draws a horizontal line X0...X2 at Y if LEFT says so,
- shortening it to X0...X1 if SHORTEN is true.
- Draws a horizontal line X1...X3 at Y if RIGHT says so,
- shortening it to X2...X3 if SHORTEN is true. */
-static void
-horz_line (struct xr_driver *xr, int x0, int x1, int x2, int x3, int y,
- enum render_line_style left, enum render_line_style right,
- const struct cell_color *left_color,
- const struct cell_color *right_color,
- bool shorten)