X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fcairo-fsm.c;h=183cdb0b7ff925a3aff80a2dccaca337957bf643;hb=29917c4f5908454803e663d2ad78bca4bc35e805;hp=c485eb7225bd0a64f580d4fb73232477304265b2;hpb=7fceea6249f2a570ecc44b5cb35f94711da957c1;p=pspp diff --git a/src/output/cairo-fsm.c b/src/output/cairo-fsm.c index c485eb7225..183cdb0b7f 100644 --- a/src/output/cairo-fsm.c +++ b/src/output/cairo-fsm.c @@ -26,8 +26,7 @@ #include "libpspp/assertion.h" #include "libpspp/str.h" #include "output/cairo-chart.h" -#include "output/chart-item-provider.h" -#include "output/chart-item.h" +#include "output/chart-provider.h" #include "output/charts/barchart.h" #include "output/charts/boxplot.h" #include "output/charts/np-plot.h" @@ -37,13 +36,10 @@ #include "output/charts/scatterplot.h" #include "output/charts/scree.h" #include "output/charts/spreadlevel-plot.h" -#include "output/group-item.h" -#include "output/message-item.h" -#include "output/page-eject-item.h" -#include "output/page-setup-item.h" +#include "output/pivot-output.h" +#include "output/pivot-table.h" #include "output/render.h" -#include "output/table-item.h" -#include "output/text-item.h" +#include "output/output-item.h" #include "gl/c-ctype.h" #include "gl/c-strcase.h" @@ -74,9 +70,8 @@ xr_fsm_style_unshare (struct xr_fsm_style *old) struct xr_fsm_style *new = xmemdup (old, sizeof *old); new->ref_cnt = 1; - for (int i = 0; i < XR_N_FONTS; i++) - if (old->fonts[i]) - new->fonts[i] = pango_font_description_copy (old->fonts[i]); + if (old->font) + new->font = pango_font_description_copy (old->font); return new; } @@ -89,8 +84,7 @@ xr_fsm_style_unref (struct xr_fsm_style *style) assert (style->ref_cnt > 0); if (!--style->ref_cnt) { - for (size_t i = 0; i < XR_N_FONTS; i++) - pango_font_description_free (style->fonts[i]); + pango_font_description_free (style->font); free (style); } } @@ -104,29 +98,42 @@ xr_fsm_style_equals (const struct xr_fsm_style *a, || a->size[V] != b->size[V] || a->min_break[H] != b->min_break[H] || a->min_break[V] != b->min_break[V] + || !pango_font_description_equal (a->font, b->font) || a->use_system_colors != b->use_system_colors + || a->object_spacing != b->object_spacing || a->font_resolution != b->font_resolution) return false; - for (size_t i = 0; i < XR_N_FONTS; i++) - if (!pango_font_description_equal (a->fonts[i], b->fonts[i])) - return false; - return true; } +/* Renders a single output_item to an output device in one of two ways: + + - 'print == true': Broken across multiple pages if necessary. + + - 'print == false': In a single region that the user may scroll around if + needed. + + Normally 'output_item' corresponds to a single rendering. There is a + special case when 'print == true' and 'output_item' is a table_item with + multiple layers and 'item->pt->table_look->print_all_layers == true'. In + that case, each layer is rendered separately from the FSM's internal point + of view; from the client's point of view, it is all one operation. +*/ struct xr_fsm { struct xr_fsm_style *style; struct output_item *item; + bool print; + + /* Print mode only. */ + bool done; /* Table items only. */ + size_t *layer_indexes; struct render_params rp; struct render_pager *p; cairo_t *cairo; /* XXX should this be here?! */ - - /* Chart and page-eject items only. */ - bool done; }; /* The unit used for internal measurements is inch/(72 * XR_POINT). @@ -185,8 +192,6 @@ xr_draw_line (struct xr_fsm *xr, int x0, int y0, int x1, int y1, int style, const struct cell_color *color) { cairo_new_path (xr->cairo); - if (!xr->style->use_system_colors) - xr_set_source_rgba (xr->cairo, color); cairo_set_line_width ( xr->cairo, xr_to_pt (style == RENDER_LINE_THICK ? XR_LINE_WIDTH * 2 @@ -194,7 +199,14 @@ xr_draw_line (struct xr_fsm *xr, int x0, int y0, int x1, int y1, int style, : XR_LINE_WIDTH)); cairo_move_to (xr->cairo, xr_to_pt (x0), xr_to_pt (y0)); cairo_line_to (xr->cairo, xr_to_pt (x1), xr_to_pt (y1)); + + if (!xr->style->use_system_colors) + xr_set_source_rgba (xr->cairo, color); + if (style == RENDER_LINE_DASHED) + cairo_set_dash (xr->cairo, (double[]) { 2 }, 1, 0); cairo_stroke (xr->cairo); + if (style == RENDER_LINE_DASHED) + cairo_set_dash (xr->cairo, NULL, 0, 0); } static void UNUSED @@ -411,31 +423,32 @@ xrr_measure_cell_width (void *xr_, const struct table_cell *cell, bb[H][1] = 1; xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL); + const int (*margin)[2] = cell->cell_style->margin; if (*min_width > 0) - *min_width += px_to_xr (cell->style->cell_style.margin[H][0] - + cell->style->cell_style.margin[H][1]); + *min_width += px_to_xr (margin[H][0] + margin[H][1]); if (*max_width > 0) - *max_width += px_to_xr (cell->style->cell_style.margin[H][0] - + cell->style->cell_style.margin[H][1]); + *max_width += px_to_xr (margin[H][0] + margin[H][1]); } static int xrr_measure_cell_height (void *xr_, const struct table_cell *cell, int width) { struct xr_fsm *xr = xr_; - int bb[TABLE_N_AXES][2]; - int clip[TABLE_N_AXES][2]; - int w, h; - bb[H][0] = 0; - bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0] - + cell->style->cell_style.margin[H][1]); - bb[V][0] = 0; - bb[V][1] = INT_MAX; - clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0; + const int (*margin)[2] = cell->cell_style->margin; + + int bb[TABLE_N_AXES][2] = { + [H][0] = 0, + [H][1] = width - px_to_xr (margin[H][0] + margin[H][1]), + [V][0] = 0, + [V][1] = INT_MAX, + }; + + int clip[TABLE_N_AXES][2] = { { 0, 0 }, { 0, 0 } }; + + int w, h; xr_layout_cell (xr, cell, bb, clip, &w, &h, NULL); - h += px_to_xr (cell->style->cell_style.margin[V][0] - + cell->style->cell_style.margin[V][1]); + h += px_to_xr (margin[V][0] + margin[V][1]); return h; } @@ -450,7 +463,7 @@ xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx, struct xr_fsm *xr = xr_; int w, h, brk; - const struct cell_color *bg = &cell->style->font_style.bg[color_idx]; + const struct cell_color *bg = &cell->font_style->bg[color_idx]; if ((bg->r != 255 || bg->g != 255 || bg->b != 255) && bg->alpha) { cairo_save (xr->cairo); @@ -476,14 +489,14 @@ xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx, } cairo_save (xr->cairo); if (!xr->style->use_system_colors) - xr_set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]); + xr_set_source_rgba (xr->cairo, &cell->font_style->fg[color_idx]); bb[V][0] += valign_offset; for (int axis = 0; axis < TABLE_N_AXES; axis++) { - bb[axis][0] += px_to_xr (cell->style->cell_style.margin[axis][0]); - bb[axis][1] -= px_to_xr (cell->style->cell_style.margin[axis][1]); + bb[axis][0] += px_to_xr (cell->cell_style->margin[axis][0]); + bb[axis][1] -= px_to_xr (cell->cell_style->margin[axis][1]); } if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1]) xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk); @@ -495,25 +508,34 @@ xrr_adjust_break (void *xr_, const struct table_cell *cell, int width, int height) { struct xr_fsm *xr = xr_; - int bb[TABLE_N_AXES][2]; - int clip[TABLE_N_AXES][2]; - int w, h, brk; if (xrr_measure_cell_height (xr_, cell, width) < height) return -1; - bb[H][0] = 0; - bb[H][1] = width - px_to_xr (cell->style->cell_style.margin[H][0] - + cell->style->cell_style.margin[H][1]); + const int (*margin)[2] = cell->cell_style->margin; + + int bb[TABLE_N_AXES][2] = { + [H][0] = 0, + [V][0] = 0, + [H][1] = width - px_to_xr (margin[H][0] + margin[H][1]), + [V][1] = height - px_to_xr (margin[V][0] + margin[V][1]), + }; if (bb[H][1] <= 0) return 0; - bb[V][0] = 0; - bb[V][1] = height - px_to_xr (cell->style->cell_style.margin[V][0] - + cell->style->cell_style.margin[V][1]); - clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0; + + int clip[TABLE_N_AXES][2] = { { 0, 0 }, { 0, 0 } }; + + int w, h, brk; xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk); return brk; } + +static void +xrr_scale (void *xr_, double scale) +{ + struct xr_fsm *xr = xr_; + cairo_scale (xr->cairo, scale, scale); +} static void xr_clip (struct xr_fsm *xr, int clip[TABLE_N_AXES][2]) @@ -540,10 +562,9 @@ add_attr (PangoAttrList *list, PangoAttribute *attr, } static void -markup_escape (struct string *out, unsigned int options, - const char *in, size_t len) +markup_escape (struct string *out, bool markup, const char *in, size_t len) { - if (!(options & TAB_MARKUP)) + if (!markup) { ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len)); return; @@ -611,17 +632,15 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2], int *widthp, int *brk) { - const struct font_style *font_style = &cell->style->font_style; - const struct cell_style *cell_style = &cell->style->cell_style; + const struct pivot_table *pt = xr->item->table; + const struct font_style *font_style = cell->font_style; + const struct cell_style *cell_style = cell->cell_style; unsigned int options = cell->options; enum table_axis X = options & TAB_ROTATE ? V : H; enum table_axis Y = !X; int R = options & TAB_ROTATE ? 0 : 1; - enum xr_font_type font_type = (options & TAB_FIX - ? XR_FONT_FIXED - : XR_FONT_PROPORTIONAL); PangoFontDescription *desc = NULL; if (font_style->typeface) desc = parse_font ( @@ -629,7 +648,7 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, font_style->size ? font_style->size * 1000 : 10000, font_style->bold, font_style->italic); if (!desc) - desc = xr->style->fonts[font_type]; + desc = xr->style->font; assert (xr->cairo); PangoContext *context = pango_cairo_create_context (xr->cairo); @@ -639,14 +658,19 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, pango_layout_set_font_description (layout, desc); - const char *text = cell->text; + struct string body = DS_EMPTY_INITIALIZER; + bool numeric = pivot_value_format_body (cell->value, pt, &body); + enum table_halign halign = table_halign_interpret ( - cell_style->halign, cell->options & TAB_NUMERIC); - if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE)) + cell->cell_style->halign, numeric); + + if (cell_style->halign == TABLE_HALIGN_DECIMAL + && !(cell->options & TAB_ROTATE)) { int margin_adjustment = -px_to_xr (cell_style->decimal_offset); - const char *decimal = strrchr (text, cell_style->decimal_char); + const char *decimal = strrchr (ds_cstr (&body), + cell_style->decimal_char); if (decimal) { pango_layout_set_text (layout, decimal, strlen (decimal)); @@ -658,7 +682,6 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, bb[H][1] += margin_adjustment; } - struct string tmp = DS_EMPTY_INITIALIZER; PangoAttrList *attrs = NULL; /* Deal with an oddity of the Unicode line-breaking algorithm (or perhaps in @@ -675,33 +698,39 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, happen with grouping like 1,234,567.89 or 1.234.567,89 because if groups are present then there will always be a digit on both sides of every period and comma. */ - if (options & TAB_MARKUP) + bool markup = cell->font_style->markup; + if (markup) { PangoAttrList *new_attrs; char *new_text; - if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL)) + if (pango_parse_markup (ds_cstr (&body), -1, 0, + &new_attrs, &new_text, NULL, NULL)) { attrs = new_attrs; - tmp.ss = ss_cstr (new_text); - tmp.capacity = tmp.ss.length; + ds_destroy (&body); + body.ss = ss_cstr (new_text); + body.capacity = body.ss.length; } else { /* XXX should we report the error? */ - ds_put_cstr (&tmp, text); } } else if (options & TAB_ROTATE || bb[H][1] != INT_MAX) { + const char *text = ds_cstr (&body); const char *decimal = text + strcspn (text, ".,"); if (decimal[0] && c_isdigit (decimal[1]) && (decimal == text || !c_isdigit (decimal[-1]))) { - ds_extend (&tmp, strlen (text) + 16); - markup_escape (&tmp, options, text, decimal - text + 1); + struct string tmp = DS_EMPTY_INITIALIZER; + ds_extend (&tmp, ds_length (&body) + 16); + markup_escape (&tmp, markup, text, decimal - text + 1); ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */); - markup_escape (&tmp, options, decimal + 1, -1); + markup_escape (&tmp, markup, decimal + 1, -1); + ds_swap (&tmp, &body); + ds_destroy (&tmp); } } @@ -713,43 +742,39 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, PANGO_UNDERLINE_SINGLE)); } - if (cell->n_footnotes || cell->n_subscripts || cell->superscript) + const struct pivot_value *value = cell->value; + if (value->n_footnotes || value->n_subscripts) { - /* If we haven't already put TEXT into tmp, do it now. */ - if (ds_is_empty (&tmp)) - { - ds_extend (&tmp, strlen (text) + 16); - markup_escape (&tmp, options, text, -1); - } - - size_t subscript_ofs = ds_length (&tmp); - for (size_t i = 0; i < cell->n_subscripts; i++) + size_t subscript_ofs = ds_length (&body); + for (size_t i = 0; i < value->n_subscripts; i++) { if (i) - ds_put_byte (&tmp, ','); - ds_put_cstr (&tmp, cell->subscripts[i]); + ds_put_byte (&body, ','); + ds_put_cstr (&body, value->subscripts[i]); } - size_t superscript_ofs = ds_length (&tmp); - if (cell->superscript) - ds_put_cstr (&tmp, cell->superscript); - - size_t footnote_ofs = ds_length (&tmp); - for (size_t i = 0; i < cell->n_footnotes; i++) + size_t footnote_ofs = ds_length (&body); + size_t n_footnotes = 0; + for (size_t i = 0; i < value->n_footnotes; i++) { - if (i) - ds_put_byte (&tmp, ','); - ds_put_cstr (&tmp, cell->footnotes[i]->marker); + const struct pivot_footnote *f + = pt->footnotes[value->footnote_indexes[i]]; + if (f->show) + { + if (n_footnotes++) + ds_put_byte (&body, ','); + pivot_footnote_format_marker (f, pt, &body); + } } /* Allow footnote markers to occupy the right margin. That way, numbers in the column are still aligned. */ - if (cell->n_footnotes && halign == TABLE_HALIGN_RIGHT) + if (value->n_footnotes && halign == TABLE_HALIGN_RIGHT) { /* Measure the width of the footnote marker, so we know how much we need to make room for. */ - pango_layout_set_text (layout, ds_cstr (&tmp) + footnote_ofs, - ds_length (&tmp) - footnote_ofs); + pango_layout_set_text (layout, ds_cstr (&body) + footnote_ofs, + ds_length (&body) - footnote_ofs); PangoAttrList *fn_attrs = pango_attr_list_new (); pango_attr_list_insert ( @@ -779,12 +804,15 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, PANGO_ATTR_INDEX_TO_TEXT_END); add_attr (attrs, pango_attr_scale_new (PANGO_SCALE_SMALL), subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END); - if (cell->n_subscripts) + if (value->n_subscripts) add_attr (attrs, pango_attr_rise_new (-3000), subscript_ofs, - superscript_ofs - subscript_ofs); - if (cell->superscript || cell->n_footnotes) - add_attr (attrs, pango_attr_rise_new (3000), superscript_ofs, - PANGO_ATTR_INDEX_TO_TEXT_END); + footnote_ofs - subscript_ofs); + if (value->n_footnotes) + { + bool superscript = pt->look->footnote_marker_superscripts; + add_attr (attrs, pango_attr_rise_new (superscript ? 3000 : -3000), + footnote_ofs, PANGO_ATTR_INDEX_TO_TEXT_END); + } } /* Set the attributes, if any. */ @@ -795,11 +823,7 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, } /* Set the text. */ - if (ds_is_empty (&tmp)) - pango_layout_set_text (layout, text, -1); - else - pango_layout_set_text (layout, ds_cstr (&tmp), ds_length (&tmp)); - ds_destroy (&tmp); + pango_layout_set_text (layout, ds_cstr (&body), ds_length (&body)); pango_layout_set_alignment (layout, (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT @@ -810,6 +834,9 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0])); pango_layout_set_wrap (layout, PANGO_WRAP_WORD); + int size[TABLE_N_AXES]; + pango_layout_get_size (layout, &size[H], &size[V]); + if (clip[H][0] != clip[H][1]) { cairo_save (xr->cairo); @@ -817,8 +844,10 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, xr_clip (xr, clip); if (options & TAB_ROTATE) { + int extra = bb[H][1] - bb[H][0] - size[V]; + int halign_offset = extra > 0 ? extra / 2 : 0; cairo_translate (xr->cairo, - xr_to_pt (bb[H][0]), + xr_to_pt (bb[H][0] + halign_offset), xr_to_pt (bb[V][1])); cairo_rotate (xr->cairo, -M_PI_2); } @@ -857,8 +886,6 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, cairo_restore (xr->cairo); } - int size[TABLE_N_AXES]; - pango_layout_get_size (layout, &size[H], &size[V]); int w = pango_to_xr (size[X]); int h = pango_to_xr (size[Y]); if (w > *widthp) @@ -910,9 +937,10 @@ xr_layout_cell_text (struct xr_fsm *xr, const struct table_cell *cell, pango_layout_set_attributes (layout, NULL); - if (desc != xr->style->fonts[font_type]) + if (desc != xr->style->font) pango_font_description_free (desc); g_object_unref (G_OBJECT (layout)); + ds_destroy (&body); return h; } @@ -946,48 +974,55 @@ xr_layout_cell (struct xr_fsm *xr, const struct table_cell *cell, #define CHART_WIDTH 500 #define CHART_HEIGHT 375 -struct xr_fsm * +static struct xr_fsm * xr_fsm_create (const struct output_item *item_, - const struct xr_fsm_style *style, cairo_t *cr) + const struct xr_fsm_style *style, cairo_t *cr, + bool print) { - if (is_page_setup_item (item_) - || is_group_open_item (item_) - || is_group_close_item (item_)) - return NULL; - struct output_item *item; - if (is_table_item (item_) - || is_chart_item (item_) - || is_page_eject_item (item_)) - item = output_item_ref (item_); - else if (is_message_item (item_)) - item = table_item_super ( - text_item_to_table_item ( - message_item_to_text_item ( - to_message_item ( - output_item_ref (item_))))); - else if (is_text_item (item_)) + + switch (item_->type) { - if (to_text_item (item_)->type == TEXT_ITEM_PAGE_TITLE) + case OUTPUT_ITEM_CHART: + case OUTPUT_ITEM_IMAGE: + case OUTPUT_ITEM_PAGE_BREAK: + case OUTPUT_ITEM_TABLE: + item = output_item_ref (item_); + break; + + case OUTPUT_ITEM_GROUP_OPEN: + case OUTPUT_ITEM_GROUP_CLOSE: + case OUTPUT_ITEM_PAGE_SETUP: + return NULL; + + case OUTPUT_ITEM_MESSAGE: + item = text_item_to_table_item (message_item_to_text_item ( + output_item_ref (item_))); + break; + + case OUTPUT_ITEM_TEXT: + if (item_->text.subtype == TEXT_ITEM_PAGE_TITLE) return NULL; - item = table_item_super ( - text_item_to_table_item ( - to_text_item ( - output_item_ref (item_)))); + item = text_item_to_table_item (output_item_ref (item_)); + break; + + default: + NOT_REACHED (); } - else if (is_group_open_item (item_)) + + assert (item->type == OUTPUT_ITEM_TABLE + || item->type == OUTPUT_ITEM_CHART + || item->type == OUTPUT_ITEM_IMAGE + || item->type == OUTPUT_ITEM_PAGE_BREAK); + + size_t *layer_indexes = NULL; + if (item->type == OUTPUT_ITEM_TABLE) { - item = table_item_super ( - text_item_to_table_item ( - text_item_create (TEXT_ITEM_TITLE, - to_group_open_item (item_)->command_name))); + layer_indexes = pivot_output_next_layer (item->table, NULL, print); + if (!layer_indexes) + return NULL; } - else - NOT_REACHED (); - assert (is_table_item (item) - || is_chart_item (item) - || is_page_eject_item (item)); static const struct render_ops xrr_render_ops = { .measure_cell_width = xrr_measure_cell_width, @@ -995,6 +1030,7 @@ xr_fsm_create (const struct output_item *item_, .adjust_break = xrr_adjust_break, .draw_line = xrr_draw_line, .draw_cell = xrr_draw_cell, + .scale = xrr_scale, }; enum { LW = XR_LINE_WIDTH, LS = XR_LINE_SPACE }; @@ -1012,6 +1048,8 @@ xr_fsm_create (const struct output_item *item_, *fsm = (struct xr_fsm) { .style = xr_fsm_style_ref (style), .item = item, + .print = print, + .layer_indexes = layer_indexes, .rp = { .ops = &xrr_render_ops, .aux = fsm, @@ -1020,41 +1058,47 @@ xr_fsm_create (const struct output_item *item_, .min_break = { [H] = style->min_break[H], [V] = style->min_break[V] }, .supports_margins = true, .rtl = render_direction_rtl (), + .printing = print, } }; - if (is_table_item (item)) - { - fsm->cairo = cr; - fsm->p = render_pager_create (&fsm->rp, to_table_item (item)); - fsm->cairo = NULL; - } + /* Get font size. */ + PangoContext *context = pango_cairo_create_context (cr); + pango_cairo_context_set_resolution (context, style->font_resolution); + PangoLayout *layout = pango_layout_new (context); + g_object_unref (context); - for (int i = 0; i < XR_N_FONTS; i++) - { - PangoContext *context = pango_cairo_create_context (cr); - pango_cairo_context_set_resolution (context, style->font_resolution); - PangoLayout *layout = pango_layout_new (context); - g_object_unref (context); + pango_layout_set_font_description (layout, style->font); - pango_layout_set_font_description (layout, style->fonts[i]); + pango_layout_set_text (layout, "0", 1); - pango_layout_set_text (layout, "0", 1); + int char_size[TABLE_N_AXES]; + pango_layout_get_size (layout, &char_size[H], &char_size[V]); + for (int a = 0; a < TABLE_N_AXES; a++) + { + int csa = pango_to_xr (char_size[a]); + fsm->rp.font_size[a] = MAX (fsm->rp.font_size[a], csa); + } - int char_size[TABLE_N_AXES]; - pango_layout_get_size (layout, &char_size[H], &char_size[V]); - for (int a = 0; a < TABLE_N_AXES; a++) - { - int csa = pango_to_xr (char_size[a]); - fsm->rp.font_size[a] = MAX (fsm->rp.font_size[a], csa); - } + g_object_unref (G_OBJECT (layout)); - g_object_unref (G_OBJECT (layout)); + if (item->type == OUTPUT_ITEM_TABLE) + { + fsm->cairo = cr; + fsm->p = render_pager_create (&fsm->rp, item->table, fsm->layer_indexes); + fsm->cairo = NULL; } return fsm; } +struct xr_fsm * +xr_fsm_create_for_printing (const struct output_item *item, + const struct xr_fsm_style *style, cairo_t *cr) +{ + return xr_fsm_create (item, style, cr, true); +} + void xr_fsm_destroy (struct xr_fsm *fsm) { @@ -1062,33 +1106,57 @@ xr_fsm_destroy (struct xr_fsm *fsm) { xr_fsm_style_unref (fsm->style); output_item_unref (fsm->item); + free (fsm->layer_indexes); render_pager_destroy (fsm->p); assert (!fsm->cairo); free (fsm); } } + +/* Scrolling API. */ + +struct xr_fsm * +xr_fsm_create_for_scrolling (const struct output_item *item, + const struct xr_fsm_style *style, cairo_t *cr) +{ + return xr_fsm_create (item, style, cr, false); +} -/* This is primarily meant for use with screen rendering since the result is a - fixed value for charts. */ void xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp) { + assert (!fsm->print); + int w, h; - if (is_table_item (fsm->item)) + switch (fsm->item->type) { + case OUTPUT_ITEM_CHART: + w = CHART_WIDTH; + h = CHART_HEIGHT; + break; + + case OUTPUT_ITEM_IMAGE: + w = cairo_image_surface_get_width (fsm->item->image); + h = cairo_image_surface_get_height (fsm->item->image); + break; + + case OUTPUT_ITEM_TABLE: fsm->cairo = cr; w = render_pager_get_size (fsm->p, H) / XR_POINT; h = render_pager_get_size (fsm->p, V) / XR_POINT; fsm->cairo = NULL; + break; + + case OUTPUT_ITEM_GROUP_OPEN: + case OUTPUT_ITEM_GROUP_CLOSE: + case OUTPUT_ITEM_MESSAGE: + case OUTPUT_ITEM_PAGE_BREAK: + case OUTPUT_ITEM_PAGE_SETUP: + case OUTPUT_ITEM_TEXT: + default: + NOT_REACHED (); } - else if (is_chart_item (fsm->item)) - { - w = CHART_WIDTH; - h = CHART_HEIGHT; - } - else - NOT_REACHED (); if (wp) *wp = w; @@ -1096,38 +1164,10 @@ xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp) *hp = h; } -static int -xr_fsm_draw_table (struct xr_fsm *fsm, int space) -{ - return (render_pager_has_next (fsm->p) - ? render_pager_draw_next (fsm->p, space) - : 0); -} - -static int -xr_fsm_draw_chart (struct xr_fsm *fsm, int space) -{ - const int chart_height = 0.8 * MIN (fsm->rp.size[H], fsm->rp.size[V]); - if (space < chart_height) - return 0; - - fsm->done = true; - xr_draw_chart (to_chart_item (fsm->item), fsm->cairo, - xr_to_pt (fsm->rp.size[H]), xr_to_pt (chart_height)); - return chart_height; -} - -static int -xr_fsm_draw_eject (struct xr_fsm *fsm, int space) -{ - if (space >= fsm->rp.size[V]) - fsm->done = true; - return 0; -} - void xr_fsm_draw_all (struct xr_fsm *fsm, cairo_t *cr) { + assert (!fsm->print); xr_fsm_draw_region (fsm, cr, 0, 0, INT_MAX, INT_MAX); } @@ -1139,50 +1179,191 @@ mul_XR_POINT (int x) : x * XR_POINT); } +static void +draw_image (cairo_surface_t *image, cairo_t *cr) +{ + cairo_save (cr); + cairo_set_source_surface (cr, image, 0, 0); + cairo_rectangle (cr, 0, 0, cairo_image_surface_get_width (image), + cairo_image_surface_get_height (image)); + cairo_clip (cr); + cairo_paint (cr); + cairo_restore (cr); +} + void xr_fsm_draw_region (struct xr_fsm *fsm, cairo_t *cr, int x, int y, int w, int h) { - if (is_table_item (fsm->item)) + assert (!fsm->print); + switch (fsm->item->type) { + case OUTPUT_ITEM_CHART: + xr_draw_chart (fsm->item->chart, cr, CHART_WIDTH, CHART_HEIGHT); + break; + + case OUTPUT_ITEM_IMAGE: + draw_image (fsm->item->image, cr); + break; + + case OUTPUT_ITEM_TABLE: fsm->cairo = cr; render_pager_draw_region (fsm->p, mul_XR_POINT (x), mul_XR_POINT (y), mul_XR_POINT (w), mul_XR_POINT (h)); fsm->cairo = NULL; + break; + + case OUTPUT_ITEM_GROUP_OPEN: + case OUTPUT_ITEM_GROUP_CLOSE: + case OUTPUT_ITEM_MESSAGE: + case OUTPUT_ITEM_PAGE_BREAK: + case OUTPUT_ITEM_PAGE_SETUP: + case OUTPUT_ITEM_TEXT: + NOT_REACHED (); } - else if (is_chart_item (fsm->item)) - xr_draw_chart (to_chart_item (fsm->item), cr, CHART_WIDTH, CHART_HEIGHT); - else if (is_page_eject_item (fsm->item)) +} + +/* Printing API. */ + +static int +xr_fsm_draw_table (struct xr_fsm *fsm, int space) +{ + int used = render_pager_draw_next (fsm->p, space); + if (!render_pager_has_next (fsm->p)) { - /* Nothing to do. */ + render_pager_destroy (fsm->p); + + fsm->layer_indexes = pivot_output_next_layer (fsm->item->table, + fsm->layer_indexes, true); + if (fsm->layer_indexes) + { + fsm->p = render_pager_create (&fsm->rp, fsm->item->table, + fsm->layer_indexes); + if (fsm->item->table->look->paginate_layers) + used = space; + else + used += fsm->style->object_spacing; + } + else + { + fsm->p = NULL; + fsm->done = true; + } } - else - NOT_REACHED (); + return MIN (used, space); +} + +static int +xr_fsm_draw_chart (struct xr_fsm *fsm, int space) +{ + const int chart_height = 0.8 * MIN (fsm->rp.size[H], fsm->rp.size[V]); + if (space < chart_height) + return 0; + + fsm->done = true; + xr_draw_chart (fsm->item->chart, fsm->cairo, + xr_to_pt (fsm->rp.size[H]), xr_to_pt (chart_height)); + return chart_height; +} + +static int +xr_fsm_draw_image (struct xr_fsm *fsm, int space) +{ + cairo_surface_t *image = fsm->item->image; + int width = cairo_image_surface_get_width (image) * XR_POINT; + int height = cairo_image_surface_get_height (image) * XR_POINT; + if (!width || !height) + goto error; + + if (height > fsm->rp.size[V]) + { + double scale = fsm->rp.size[V] / (double) height; + width *= scale; + height *= scale; + if (!width || !height) + goto error; + + cairo_scale (fsm->cairo, scale, scale); + } + + if (width > fsm->rp.size[H]) + { + double scale = fsm->rp.size[H] / (double) width; + width *= scale; + height *= scale; + if (!width || !height) + goto error; + + cairo_scale (fsm->cairo, scale, scale); + } + + if (space < height) + return 0; + + draw_image (image, fsm->cairo); + fsm->done = true; + return height; + +error: + fsm->done = true; + return 0; +} + +static int +xr_fsm_draw_page_break (struct xr_fsm *fsm, int space) +{ + if (space >= fsm->rp.size[V]) + fsm->done = true; + return 0; } int xr_fsm_draw_slice (struct xr_fsm *fsm, cairo_t *cr, int space) { - if (xr_fsm_is_empty (fsm)) + assert (fsm->print); + + if (fsm->done || space <= 0) return 0; cairo_save (cr); fsm->cairo = cr; - int used = (is_table_item (fsm->item) ? xr_fsm_draw_table (fsm, space) - : is_chart_item (fsm->item) ? xr_fsm_draw_chart (fsm, space) - : is_page_eject_item (fsm->item) ? xr_fsm_draw_eject (fsm, space) - : (abort (), 0)); + int used; + switch (fsm->item->type) + { + case OUTPUT_ITEM_CHART: + used = xr_fsm_draw_chart (fsm, space); + break; + + case OUTPUT_ITEM_IMAGE: + used = xr_fsm_draw_image (fsm, space); + break; + + case OUTPUT_ITEM_PAGE_BREAK: + used = xr_fsm_draw_page_break (fsm, space); + break; + + case OUTPUT_ITEM_TABLE: + used = xr_fsm_draw_table (fsm, space); + break; + + case OUTPUT_ITEM_GROUP_OPEN: + case OUTPUT_ITEM_GROUP_CLOSE: + case OUTPUT_ITEM_MESSAGE: + case OUTPUT_ITEM_PAGE_SETUP: + case OUTPUT_ITEM_TEXT: + default: + NOT_REACHED (); + } fsm->cairo = NULL; cairo_restore (cr); return used; } - bool xr_fsm_is_empty (const struct xr_fsm *fsm) { - return (is_table_item (fsm->item) - ? !render_pager_has_next (fsm->p) - : fsm->done); + assert (fsm->print); + + return fsm->done; }