+\f
+struct xr_table_state
+ {
+ struct xr_render_fsm fsm;
+ struct table_item *table_item;
+ struct render_break x_break;
+ struct render_break y_break;
+ int caption_height;
+ };
+
+static bool
+xr_table_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
+{
+ struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
+
+ for (;;)
+ {
+ struct render_page *y_slice;
+ int space;
+
+ while (!render_break_has_next (&ts->y_break))
+ {
+ struct render_page *x_slice;
+
+ render_break_destroy (&ts->y_break);
+ if (!render_break_has_next (&ts->x_break))
+ return false;
+
+ x_slice = render_break_next (&ts->x_break, xr->width);
+ render_break_init (&ts->y_break, x_slice, V);
+ }
+
+ space = xr->length - xr->y;
+ if (render_break_next_size (&ts->y_break) > space)
+ {
+ assert (xr->y > 0);
+ return true;
+ }
+
+ y_slice = render_break_next (&ts->y_break, space);
+ if (ts->caption_height)
+ {
+ if (xr->cairo)
+ {
+ struct table_cell cell;
+ int bb[TABLE_N_AXES][2];
+
+ xr_init_caption_cell (table_item_get_caption (ts->table_item),
+ &cell);
+ bb[H][0] = 0;
+ bb[H][1] = xr->width;
+ bb[V][0] = 0;
+ bb[V][1] = ts->caption_height;
+ xr_draw_cell (xr, &cell, bb, bb);
+ }
+ xr->y += ts->caption_height;
+ ts->caption_height = 0;
+ }
+
+ if (xr->cairo)
+ render_page_draw (y_slice);
+ xr->y += render_page_get_size (y_slice, V);
+ render_page_unref (y_slice);
+ }
+}
+
+static void
+xr_table_destroy (struct xr_render_fsm *fsm)
+{
+ struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm);
+
+ table_item_unref (ts->table_item);
+ render_break_destroy (&ts->x_break);
+ render_break_destroy (&ts->y_break);
+ free (ts);
+}
+
+static struct xr_render_fsm *
+xr_render_table (struct xr_driver *xr, const struct table_item *table_item)
+{
+ struct xr_table_state *ts;
+ struct render_page *page;
+
+ ts = xmalloc (sizeof *ts);
+ ts->fsm.render = xr_table_render;
+ ts->fsm.destroy = xr_table_destroy;
+ ts->table_item = table_item_ref (table_item);
+
+ if (xr->y > 0)
+ xr->y += xr->font_height;
+
+ page = xr_render_table_item (xr, table_item, &ts->caption_height);
+ xr->params->size[V] = xr->length - ts->caption_height;
+
+ render_break_init (&ts->x_break, page, H);
+ render_break_init_empty (&ts->y_break);
+
+ return &ts->fsm;
+}
+\f
+struct xr_chart_state
+ {
+ struct xr_render_fsm fsm;
+ struct chart_item *chart_item;
+ };
+
+static bool
+xr_chart_render (struct xr_render_fsm *fsm, struct xr_driver *xr)
+{
+ struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
+
+ if (xr->y > 0)
+ return true;
+
+ if (xr->cairo != NULL)
+ xr_draw_chart (cs->chart_item, xr->cairo, 0.0, 0.0,
+ xr_to_pt (xr->width), xr_to_pt (xr->length));
+ xr->y = xr->length;
+
+ return false;
+}
+
+static void
+xr_chart_destroy (struct xr_render_fsm *fsm)
+{
+ struct xr_chart_state *cs = UP_CAST (fsm, struct xr_chart_state, fsm);
+
+ chart_item_unref (cs->chart_item);
+ free (cs);
+}
+
+static struct xr_render_fsm *
+xr_render_chart (const struct chart_item *chart_item)
+{
+ struct xr_chart_state *cs;
+
+ cs = xmalloc (sizeof *cs);
+ cs->fsm.render = xr_chart_render;
+ cs->fsm.destroy = xr_chart_destroy;
+ cs->chart_item = chart_item_ref (chart_item);
+
+ return &cs->fsm;
+}
+\f
+static bool
+xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_driver *xr)
+{
+ return xr->y > 0;
+}
+
+static void
+xr_eject_destroy (struct xr_render_fsm *fsm UNUSED)
+{
+ /* Nothing to do. */
+}
+
+static struct xr_render_fsm *
+xr_render_eject (void)
+{
+ static struct xr_render_fsm eject_renderer =
+ {
+ xr_eject_render,
+ xr_eject_destroy
+ };
+
+ return &eject_renderer;
+}
+\f
+static struct xr_render_fsm *
+xr_create_text_renderer (struct xr_driver *xr, const char *text)
+{
+ struct table_item *table_item;
+ struct xr_render_fsm *fsm;
+
+ table_item = table_item_create (table_from_string (TAB_LEFT, text), NULL);
+ fsm = xr_render_table (xr, table_item);
+ table_item_unref (table_item);
+
+ return fsm;
+}
+
+static struct xr_render_fsm *
+xr_render_text (struct xr_driver *xr, const struct text_item *text_item)
+{
+ enum text_item_type type = text_item_get_type (text_item);
+ const char *text = text_item_get_text (text_item);
+
+ switch (type)
+ {
+ case TEXT_ITEM_TITLE:
+ free (xr->title);
+ xr->title = xstrdup (text);
+ draw_headers (xr);
+ break;
+
+ case TEXT_ITEM_SUBTITLE:
+ free (xr->subtitle);
+ xr->subtitle = xstrdup (text);
+ draw_headers (xr);
+ break;
+
+ case TEXT_ITEM_COMMAND_CLOSE:
+ break;
+
+ case TEXT_ITEM_BLANK_LINE:
+ if (xr->y > 0)
+ xr->y += xr->font_height;
+ break;
+
+ case TEXT_ITEM_EJECT_PAGE:
+ if (xr->y > 0)
+ return xr_render_eject ();
+ break;
+
+ default:
+ return xr_create_text_renderer (xr, text);
+ }
+
+ return NULL;
+}
+
+static struct xr_render_fsm *
+xr_render_message (struct xr_driver *xr,
+ const struct message_item *message_item)
+{
+ const struct msg *msg = message_item_get_msg (message_item);
+ struct xr_render_fsm *fsm;
+ char *s;
+
+ s = msg_to_string (msg, xr->command_name);
+ fsm = xr_create_text_renderer (xr, s);
+ free (s);
+
+ return fsm;
+}
+
+static struct xr_render_fsm *
+xr_render_output_item (struct xr_driver *xr,
+ const struct output_item *output_item)
+{
+ if (is_table_item (output_item))
+ return xr_render_table (xr, to_table_item (output_item));
+ else if (is_chart_item (output_item))
+ return xr_render_chart (to_chart_item (output_item));
+ else if (is_text_item (output_item))
+ return xr_render_text (xr, to_text_item (output_item));
+ else if (is_message_item (output_item))
+ return xr_render_message (xr, to_message_item (output_item));
+ else
+ return NULL;
+}