#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"
/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
#define H TABLE_HORZ
#define V TABLE_VERT
-
+\f
struct xr_fsm_style *
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;
+ for (int i = 0; i < XR_N_FONTS; i++)
+ if (old->fonts[i])
+ new->fonts[i] = pango_font_description_copy (old->fonts[i]);
+
+ return new;
+}
+
void
xr_fsm_style_unref (struct xr_fsm_style *style)
{
|| a->min_break[H] != b->min_break[H]
|| a->min_break[V] != b->min_break[V]
|| 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++)
return true;
}
\f
+/* 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).
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
: 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
struct xr_fsm *xr = xr_;
int w, h, brk;
- if (!xr->style->transparent)
+ const struct cell_color *bg = &cell->style->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];
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],
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);
+}
\f
static void
xr_clip (struct xr_fsm *xr, int clip[TABLE_N_AXES][2])
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];
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;
PANGO_UNDERLINE_SINGLE));
}
- if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
+ if (cell->n_footnotes || cell->n_subscripts)
{
/* If we haven't already put TEXT into tmp, do it now. */
if (ds_is_empty (&tmp))
ds_put_cstr (&tmp, cell->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++)
{
if (i)
ds_put_byte (&tmp, ',');
- ds_put_cstr (&tmp, cell->footnotes[i]->marker);
+
+ pivot_value_format (cell->footnotes[i]->marker,
+ SETTINGS_VALUE_SHOW_DEFAULT,
+ SETTINGS_VALUE_SHOW_DEFAULT, &tmp);
}
/* Allow footnote markers to occupy the right margin. That way, numbers
subscript_ofs, PANGO_ATTR_INDEX_TO_TEXT_END);
if (cell->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,
+ footnote_ofs - subscript_ofs);
+ if (cell->n_footnotes)
+ add_attr (attrs, pango_attr_rise_new (3000), footnote_ofs,
PANGO_ATTR_INDEX_TO_TEXT_END);
}
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);
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);
}
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)
*brk = bb[V][0];
*height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);
}
-\f
-#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;
-}
-\f
-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;
-}
-\f
#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)
-{
- enum text_item_type type = text_item_get_type (text_item);
-
- switch (type)
- {
- case TEXT_ITEM_PAGE_TITLE:
- break;
-
- case TEXT_ITEM_EJECT_PAGE:
- if (xr->y > 0)
- return xr_render_eject ();
- break;
-
- default:
- return xr_render_table (
- xr, text_item_to_table_item (text_item_ref (text_item)));
- }
-
- return NULL;
-}
-#endif
-
-#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_)
item = table_item_super (
text_item_to_table_item (
text_item_create (TEXT_ITEM_TITLE,
- to_group_open_item (item_)->command_name)));
+ to_group_open_item (item_)->command_name,
+ NULL)));
}
else
NOT_REACHED ();
|| is_chart_item (item)
|| is_page_eject_item (item));
+ size_t *layer_indexes = NULL;
+ if (is_table_item (item))
+ {
+ const struct table_item *table_item = to_table_item (item);
+ layer_indexes = pivot_output_next_layer (table_item->pt, NULL, print);
+ if (!layer_indexes)
+ return NULL;
+ }
+
static const struct render_ops xrr_render_ops = {
.measure_cell_width = xrr_measure_cell_width,
.measure_cell_height = xrr_measure_cell_height,
.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 };
*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,
}
};
- if (is_table_item (item))
- {
- fsm->cairo = cr;
- fsm->p = render_pager_create (&fsm->rp, to_table_item (item));
- fsm->cairo = NULL;
- }
-
for (int i = 0; i < XR_N_FONTS; i++)
{
- PangoLayout *layout = pango_cairo_create_layout (cr);
+ 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->fonts[i]);
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++)
+ for (int a = 0; a < TABLE_N_AXES; a++)
{
- int csj = pango_to_xr (char_size[j]);
- fsm->rp.font_size[j] = MAX (fsm->rp.font_size[j], csj);
+ 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));
}
+ if (is_table_item (item))
+ {
+ struct table_item *table_item = to_table_item (item);
+
+ fsm->cairo = cr;
+ fsm->p = render_pager_create (&fsm->rp, table_item, 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)
{
{
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);
}
}
+\f
+/* 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))
*hp = h;
}
-static int
-xr_fsm_draw_table (struct xr_fsm *fsm, int space)
-{
- int used = 0;
- while (render_pager_has_next (fsm->p))
- {
- int chunk = render_pager_draw_next (fsm->p, space - used);
- if (!chunk)
- return used;
-
- used += chunk;
- cairo_translate (fsm->cairo, 0, chunk);
- }
- return used;
-}
-
-static void
-xr_draw_chart (const struct chart_item *chart_item, cairo_t *cr,
- double width, double height)
-{
- 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);
-}
-
-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);
}
xr_fsm_draw_region (struct xr_fsm *fsm, cairo_t *cr,
int x, int y, int w, int h)
{
+ assert (!fsm->print);
if (is_table_item (fsm->item))
{
fsm->cairo = cr;
else
NOT_REACHED ();
}
+\f
+/* Printing API. */
+
+static int
+xr_fsm_draw_table (struct xr_fsm *fsm, int space)
+{
+ struct table_item *table_item = to_table_item (fsm->item);
+ 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 (table_item->pt,
+ fsm->layer_indexes, true);
+ if (fsm->layer_indexes)
+ {
+ fsm->p = render_pager_create (&fsm->rp, table_item,
+ fsm->layer_indexes);
+ if (table_item->pt->look->paginate_layers)
+ used = space;
+ else
+ used += fsm->style->object_spacing;
+ }
+ else
+ {
+ fsm->p = NULL;
+ fsm->done = true;
+ }
+ }
+ 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 (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;
+}
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)
return 0;
cairo_save (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;
}