X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Foutput%2Fcairo-fsm.c;h=bedf3147ea281d4cba8ccd1c3b579cc052eb5844;hb=80ff0f10da00eae4c7b3b07266a03e403e97d640;hp=1aec006c9b1e9153c00bf12937e16fee1d89079f;hpb=50df556805bb5505ce153cb364ff2d3728694a4b;p=pspp diff --git a/src/output/cairo-fsm.c b/src/output/cairo-fsm.c index 1aec006c9b..bedf3147ea 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" @@ -52,7 +48,7 @@ /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */ #define H TABLE_HORZ #define V TABLE_VERT - + struct xr_fsm_style * xr_fsm_style_ref (const struct xr_fsm_style *style_) { @@ -63,6 +59,23 @@ xr_fsm_style_ref (const struct xr_fsm_style *style_) return style; } +struct xr_fsm_style * +xr_fsm_style_unshare (struct xr_fsm_style *old) +{ + assert (old->ref_cnt > 0); + if (old->ref_cnt == 1) + return old; + + xr_fsm_style_unref (old); + + struct xr_fsm_style *new = xmemdup (old, sizeof *old); + new->ref_cnt = 1; + if (old->font) + new->font = pango_font_description_copy (old->font); + + return new; +} + void xr_fsm_style_unref (struct xr_fsm_style *style) { @@ -71,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); } } @@ -86,30 +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->transparent != b->transparent - || a->font_scale != b->font_scale) + || 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). @@ -168,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 @@ -177,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 @@ -394,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; } @@ -433,7 +463,8 @@ xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx, struct xr_fsm *xr = xr_; int w, h, brk; - if (!xr->style->transparent) + 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); int bg_clip[TABLE_N_AXES][2]; @@ -448,7 +479,7 @@ xrr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx, bg_clip[axis][1] += spill[axis][1]; } xr_clip (xr, bg_clip); - xr_set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]); + xr_set_source_rgba (xr->cairo, bg); fill_rectangle (xr, bb[H][0] - spill[H][0], bb[V][0] - spill[V][0], @@ -458,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); @@ -477,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]) @@ -522,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; @@ -593,38 +632,45 @@ 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 ( font_style->typeface, - font_style->size ? font_style->size * 1000 * xr->style->font_scale : 10000, + 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); - PangoLayout *layout = pango_cairo_create_layout (xr->cairo); + PangoContext *context = pango_cairo_create_context (xr->cairo); + pango_cairo_context_set_resolution (context, xr->style->font_resolution); + PangoLayout *layout = pango_layout_new (context); + g_object_unref (context); + 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)); @@ -636,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 @@ -653,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); } } @@ -691,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_ex *ex = pivot_value_ex (cell->value); + if (ex->n_footnotes || ex->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 < ex->n_subscripts; i++) { if (i) - ds_put_byte (&tmp, ','); - ds_put_cstr (&tmp, cell->subscripts[i]); + ds_put_byte (&body, ','); + ds_put_cstr (&body, ex->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 < ex->n_footnotes; i++) { - if (i) - ds_put_byte (&tmp, ','); - ds_put_cstr (&tmp, cell->footnotes[i]->marker); + const struct pivot_footnote *f + = pt->footnotes[ex->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 (ex->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 ( @@ -757,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 (ex->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 (ex->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. */ @@ -773,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 @@ -788,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); @@ -795,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); } @@ -835,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) @@ -888,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; } @@ -920,152 +970,57 @@ xr_layout_cell (struct xr_fsm *xr, const struct table_cell *cell, *brk = bb[V][0]; *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk); } - -#if 0 -static bool -xr_table_render (struct xr_render_fsm *fsm, struct xr_fsm *xr) -{ - struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm); - - while (render_pager_has_next (ts->p)) - { - int used; - - used = render_pager_draw_next (ts->p, xr->length); - if (!used) - { - assert (xr->y > 0); - return true; - } - else - xr->y += used; - } - return false; -} - -static void -xr_table_destroy (struct xr_render_fsm *fsm) -{ - struct xr_table_state *ts = UP_CAST (fsm, struct xr_table_state, fsm); - - render_pager_destroy (ts->p); - free (ts); -} - -static struct xr_render_fsm * -xr_render_table (struct xr_fsm *xr, struct table_item *table_item) -{ - struct xr_table_state *ts; - - ts = xmalloc (sizeof *ts); - ts->fsm.render = xr_table_render; - ts->fsm.destroy = xr_table_destroy; - - if (xr->y > 0) - xr->y += xr->char_height; - - ts->p = render_pager_create (xr->params, table_item); - table_item_unref (table_item); - - return &ts->fsm; -} - -static bool -xr_eject_render (struct xr_render_fsm *fsm UNUSED, struct xr_fsm *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; -} - #define CHART_WIDTH 500 #define CHART_HEIGHT 375 -static struct xr_render_fsm * -xr_render_text (struct xr_fsm *xr, const struct text_item *text_item) +static struct xr_fsm * +xr_fsm_create (const struct output_item *item_, + const struct xr_fsm_style *style, cairo_t *cr, + bool print) { - enum text_item_type type = text_item_get_type (text_item); + struct output_item *item; - switch (type) + switch (item_->type) { - case 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 TEXT_ITEM_EJECT_PAGE: - if (xr->y > 0) - return xr_render_eject (); + case OUTPUT_ITEM_GROUP: + 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 = text_item_to_table_item (output_item_ref (item_)); break; default: - return xr_render_table ( - xr, text_item_to_table_item (text_item_ref (text_item))); + NOT_REACHED (); } - return NULL; -} -#endif - -#define CHART_WIDTH 500 -#define CHART_HEIGHT 375 + assert (item->type == OUTPUT_ITEM_TABLE + || item->type == OUTPUT_ITEM_CHART + || item->type == OUTPUT_ITEM_IMAGE + || item->type == OUTPUT_ITEM_PAGE_BREAK); -struct xr_fsm * -xr_fsm_create (const struct output_item *item_, - const struct xr_fsm_style *style, cairo_t *cr) -{ - 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_)) + size_t *layer_indexes = NULL; + if (item->type == OUTPUT_ITEM_TABLE) { - if (to_text_item (item_)->type == TEXT_ITEM_PAGE_TITLE) + layer_indexes = pivot_output_next_layer (item->table, NULL, print); + if (!layer_indexes) return NULL; - - item = table_item_super ( - text_item_to_table_item ( - to_text_item ( - output_item_ref (item_)))); - } - else if (is_group_open_item (item_)) - { - item = table_item_super ( - text_item_to_table_item ( - text_item_create (TEXT_ITEM_TITLE, - to_group_open_item (item_)->command_name))); } - 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, @@ -1073,6 +1028,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 }; @@ -1090,46 +1046,57 @@ 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, .size = { [H] = style->size[H], [V] = style->size[V] }, - /* XXX font_size */ .line_widths = xr_line_widths, .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++) - { - PangoLayout *layout = pango_cairo_create_layout (cr); - pango_layout_set_font_description (layout, style->fonts[i]); + pango_layout_set_font_description (layout, style->font); - 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 j = 0; j < TABLE_N_AXES; j++) - { - int csj = pango_to_xr (char_size[j]); - fsm->rp.font_size[j] = MAX (fsm->rp.font_size[j], csj); - } + 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) { @@ -1137,34 +1104,55 @@ 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: + case OUTPUT_ITEM_MESSAGE: + case OUTPUT_ITEM_PAGE_BREAK: + 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; @@ -1172,52 +1160,91 @@ xr_fsm_measure (struct xr_fsm *fsm, cairo_t *cr, int *wp, int *hp) *hp = h; } +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); +} + static int -xr_fsm_draw_table (struct xr_fsm *fsm, int space) +mul_XR_POINT (int x) { - int used = 0; - while (render_pager_has_next (fsm->p)) + return (x >= INT_MAX / XR_POINT ? INT_MAX + : x <= INT_MIN / XR_POINT ? INT_MIN + : 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) +{ + assert (!fsm->print); + switch (fsm->item->type) { - int chunk = render_pager_draw_next (fsm->p, space - used); - if (!chunk) - return used; + case OUTPUT_ITEM_CHART: + xr_draw_chart (fsm->item->chart, cr, CHART_WIDTH, CHART_HEIGHT); + break; - used += chunk; - cairo_translate (fsm->cairo, 0, chunk); + 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: + case OUTPUT_ITEM_MESSAGE: + case OUTPUT_ITEM_PAGE_BREAK: + case OUTPUT_ITEM_TEXT: + NOT_REACHED (); } - return used; } + +/* Printing API. */ -static void -xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr, - double width, double height) +static int +xr_fsm_draw_table (struct xr_fsm *fsm, int space) { - struct xrchart_geometry geom; - - cairo_translate (cr, 0, height); - cairo_scale (cr, 1.0, -1.0); - xrchart_geometry_init (cr, &geom, width, height); - if (is_boxplot (chart_item)) - xrchart_draw_boxplot (chart_item, cr, &geom); - else if (is_histogram_chart (chart_item)) - xrchart_draw_histogram (chart_item, cr, &geom); - else if (is_np_plot_chart (chart_item)) - xrchart_draw_np_plot (chart_item, cr, &geom); - else if (is_piechart (chart_item)) - xrchart_draw_piechart (chart_item, cr, &geom); - else if (is_barchart (chart_item)) - xrchart_draw_barchart (chart_item, cr, &geom); - else if (is_roc_chart (chart_item)) - xrchart_draw_roc (chart_item, cr, &geom); - else if (is_scree (chart_item)) - xrchart_draw_scree (chart_item, cr, &geom); - else if (is_spreadlevel_plot_chart (chart_item)) - xrchart_draw_spreadlevel (chart_item, cr, &geom); - else if (is_scatterplot_chart (chart_item)) - xrchart_draw_scatterplot (chart_item, cr, &geom); - else - NOT_REACHED (); - xrchart_geometry_free (cr, &geom); + int used = render_pager_draw_next (fsm->p, space); + if (!render_pager_has_next (fsm->p)) + { + 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; + } + } + return MIN (used, space); } static int @@ -1228,77 +1255,107 @@ xr_fsm_draw_chart (struct xr_fsm *fsm, int space) return 0; fsm->done = true; - xr_draw_chart (to_chart_item (fsm->item), fsm->cairo, + 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_eject (struct xr_fsm *fsm, int space) +xr_fsm_draw_image (struct xr_fsm *fsm, int space) { - if (space >= fsm->rp.size[V]) - fsm->done = true; - return 0; -} + 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; -void -xr_fsm_draw_all (struct xr_fsm *fsm, cairo_t *cr) -{ - xr_fsm_draw_region (fsm, cr, 0, 0, INT_MAX, INT_MAX); -} - -static int -mul_XR_POINT (int x) -{ - return (x >= INT_MAX / XR_POINT ? INT_MAX - : x <= INT_MIN / XR_POINT ? INT_MIN - : x * XR_POINT); -} - -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)) + if (height > fsm->rp.size[V]) { - 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; + double scale = fsm->rp.size[V] / (double) height; + width *= scale; + height *= scale; + if (!width || !height) + goto error; + + cairo_scale (fsm->cairo, scale, scale); } - 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)) + + if (width > fsm->rp.size[H]) { - /* Nothing to do. */ + double scale = fsm->rp.size[H] / (double) width; + width *= scale; + height *= scale; + if (!width || !height) + goto error; + + cairo_scale (fsm->cairo, scale, scale); } - else - NOT_REACHED (); + + 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: + case OUTPUT_ITEM_MESSAGE: + 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; }