-
- if (!double_vert)
- vert_line (xr, y0, y1, y2, y3, xc, top, bottom, top_color, bottom_color,
- shorten_xc_line);
- else
- {
- vert_line (xr, y0, y1, y2, y3, x1, top, bottom, top_color, bottom_color,
- shorten_x1_lines);
- vert_line (xr, y0, y1, y2, y3, x2, top, bottom, top_color, bottom_color,
- shorten_x2_lines);
- }
-}
-
-static void
-xr_measure_cell_width (void *xr_, const struct table_cell *cell,
- int *min_width, int *max_width)
-{
- struct xr_driver *xr = xr_;
- int bb[TABLE_N_AXES][2];
- int clip[TABLE_N_AXES][2];
- int h;
-
- bb[H][0] = 0;
- bb[H][1] = INT_MAX;
- bb[V][0] = 0;
- bb[V][1] = INT_MAX;
- clip[H][0] = clip[H][1] = clip[V][0] = clip[V][1] = 0;
- xr_layout_cell (xr, cell, bb, clip, max_width, &h, NULL);
-
- bb[H][1] = 1;
- xr_layout_cell (xr, cell, bb, clip, min_width, &h, NULL);
-
- if (*min_width > 0)
- *min_width += px_to_xr (cell->style->cell_style.margin[H][0]
- + cell->style->cell_style.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]);
-}
-
-static int
-xr_measure_cell_height (void *xr_, const struct table_cell *cell, int width)
-{
- struct xr_driver *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;
- 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]);
- return h;
-}
-
-static void xr_clip (struct xr_driver *, int clip[TABLE_N_AXES][2]);
-
-static void
-xr_draw_cell (void *xr_, const struct table_cell *cell, int color_idx,
- int bb[TABLE_N_AXES][2],
- int spill[TABLE_N_AXES][2],
- int clip[TABLE_N_AXES][2])
-{
- struct xr_driver *xr = xr_;
- int w, h, brk;
-
- cairo_save (xr->cairo);
- int bg_clip[TABLE_N_AXES][2];
- for (int axis = 0; axis < TABLE_N_AXES; axis++)
- {
- bg_clip[axis][0] = clip[axis][0];
- if (bb[axis][0] == clip[axis][0])
- bg_clip[axis][0] -= spill[axis][0];
-
- bg_clip[axis][1] = clip[axis][1];
- if (bb[axis][1] == clip[axis][1])
- bg_clip[axis][1] += spill[axis][1];
- }
- xr_clip (xr, bg_clip);
- set_source_rgba (xr->cairo, &cell->style->font_style.bg[color_idx]);
- fill_rectangle (xr,
- bb[H][0] - spill[H][0],
- bb[V][0] - spill[V][0],
- bb[H][1] + spill[H][1],
- bb[V][1] + spill[V][1]);
- cairo_restore (xr->cairo);
-
- cairo_save (xr->cairo);
- set_source_rgba (xr->cairo, &cell->style->font_style.fg[color_idx]);
-
- 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]);
- }
- if (bb[H][0] < bb[H][1] && bb[V][0] < bb[V][1])
- xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
- cairo_restore (xr->cairo);
-}
-
-static int
-xr_adjust_break (void *xr_, const struct table_cell *cell,
- int width, int height)
-{
- struct xr_driver *xr = xr_;
- int bb[TABLE_N_AXES][2];
- int clip[TABLE_N_AXES][2];
- int w, h, brk;
-
- if (xr_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]);
- 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;
- xr_layout_cell (xr, cell, bb, clip, &w, &h, &brk);
- return brk;
-}
-\f
-static void
-xr_clip (struct xr_driver *xr, int clip[TABLE_N_AXES][2])
-{
- if (clip[H][1] != INT_MAX || clip[V][1] != INT_MAX)
- {
- double x0 = xr_to_pt (clip[H][0] + xr->x);
- double y0 = xr_to_pt (clip[V][0] + xr->y);
- double x1 = xr_to_pt (clip[H][1] + xr->x);
- double y1 = xr_to_pt (clip[V][1] + xr->y);
-
- cairo_rectangle (xr->cairo, x0, y0, x1 - x0, y1 - y0);
- cairo_clip (xr->cairo);
- }
-}
-
-static void
-add_attr (PangoAttrList *list, PangoAttribute *attr,
- guint start_index, guint end_index)
-{
- attr->start_index = start_index;
- attr->end_index = end_index;
- pango_attr_list_insert (list, attr);
-}
-
-static void
-markup_escape (struct string *out, unsigned int options,
- const char *in, size_t len)
-{
- if (!(options & TAB_MARKUP))
- {
- ds_put_substring (out, ss_buffer (in, len == -1 ? strlen (in) : len));
- return;
- }
-
- while (len-- > 0)
- {
- int c = *in++;
- switch (c)
- {
- case 0:
- return;
- case '&':
- ds_put_cstr (out, "&");
- break;
- case '<':
- ds_put_cstr (out, "<");
- break;
- case '>':
- ds_put_cstr (out, ">");
- break;
- default:
- ds_put_byte (out, c);
- break;
- }
- }
-}
-
-static int
-get_layout_dimension (PangoLayout *layout, enum table_axis axis)
-{
- int size[TABLE_N_AXES];
- pango_layout_get_size (layout, &size[H], &size[V]);
- return size[axis];
-}
-
-static int
-xr_layout_cell_text (struct xr_driver *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;
- 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;
-
- struct xr_font *font = (options & TAB_FIX ? &xr->fonts[XR_FONT_FIXED]
- : &xr->fonts[XR_FONT_PROPORTIONAL]);
- struct xr_font local_font;
- if (font_style->typeface)
- {
- PangoFontDescription *desc = parse_font (
- font_style->typeface,
- font_style->size ? font_style->size * 1000 * xr->font_scale : 10000,
- font_style->bold, font_style->italic);
- if (desc)
- {
- PangoLayout *layout = pango_cairo_create_layout (xr->cairo);
- pango_layout_set_font_description (layout, desc);
-
- local_font.desc = desc;
- local_font.layout = layout;
- font = &local_font;
- }
- }
-
- const char *text = cell->text;
- enum table_halign halign = table_halign_interpret (
- cell_style->halign, cell->options & TAB_NUMERIC);
- if (cell_style->halign == TABLE_HALIGN_DECIMAL && !(options & TAB_ROTATE))
- {
- int margin_adjustment = -px_to_xr (cell_style->decimal_offset);
-
- const char *decimal = strrchr (text, cell_style->decimal_char);
- if (decimal)
- {
- pango_layout_set_text (font->layout, decimal, strlen (decimal));
- pango_layout_set_width (font->layout, -1);
- margin_adjustment += get_layout_dimension (font->layout, H);
- }
-
- if (margin_adjustment < 0)
- 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
- Pango's implementation of it): it will break after a period or a comma
- that precedes a digit, e.g. in ".000" it will break after the period.
- This code looks for such a situation and inserts a U+2060 WORD JOINER
- to prevent the break.
-
- This isn't necessary when the decimal point is between two digits
- (e.g. "0.000" won't be broken) or when the display width is not limited so
- that word wrapping won't happen.
-
- It isn't necessary to look for more than one period or comma, as would
- 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)
- {
- PangoAttrList *new_attrs;
- char *new_text;
- if (pango_parse_markup (text, -1, 0, &new_attrs, &new_text, NULL, NULL))
- {
- attrs = new_attrs;
- tmp.ss = ss_cstr (new_text);
- tmp.capacity = tmp.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 *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);
- ds_put_unichar (&tmp, 0x2060 /* U+2060 WORD JOINER */);
- markup_escape (&tmp, options, decimal + 1, -1);
- }
- }
-
- if (font_style->underline)
- {
- if (!attrs)
- attrs = pango_attr_list_new ();
- pango_attr_list_insert (attrs, pango_attr_underline_new (
- PANGO_UNDERLINE_SINGLE));
- }
-
- if (cell->n_footnotes || cell->n_subscripts || cell->superscript)
- {
- /* 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++)
- {
- if (i)
- ds_put_byte (&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);
- }
-
- /* 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)
- {
- /* Measure the width of the footnote marker, so we know how much we
- need to make room for. */
- pango_layout_set_text (font->layout, ds_cstr (&tmp) + footnote_ofs,
- ds_length (&tmp) - footnote_ofs);
-
- PangoAttrList *fn_attrs = pango_attr_list_new ();
- pango_attr_list_insert (
- fn_attrs, pango_attr_scale_new (PANGO_SCALE_SMALL));
- pango_attr_list_insert (fn_attrs, pango_attr_rise_new (3000));
- pango_layout_set_attributes (font->layout, fn_attrs);
- pango_attr_list_unref (fn_attrs);
- int footnote_width = get_layout_dimension (font->layout, X);
-
- /* Bound the adjustment by the width of the right margin. */
- int right_margin = px_to_xr (cell_style->margin[X][R]);
- int footnote_adjustment = MIN (footnote_width, right_margin);
-
- /* Adjust the bounding box. */
- if (options & TAB_ROTATE)
- footnote_adjustment = -footnote_adjustment;
- bb[X][R] += footnote_adjustment;
-
- /* Clean up. */
- pango_layout_set_attributes (font->layout, NULL);
- }
-
- /* Set attributes. */
- if (!attrs)
- attrs = pango_attr_list_new ();
- add_attr (attrs, pango_attr_font_desc_new (font->desc), subscript_ofs,
- 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)
- 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);
- }
-
- /* Set the attributes, if any. */
- if (attrs)
- {
- pango_layout_set_attributes (font->layout, attrs);
- pango_attr_list_unref (attrs);
- }
-
- /* Set the text. */
- if (ds_is_empty (&tmp))
- pango_layout_set_text (font->layout, text, -1);
- else
- pango_layout_set_text (font->layout, ds_cstr (&tmp), ds_length (&tmp));
- ds_destroy (&tmp);
-
- pango_layout_set_alignment (font->layout,
- (halign == TABLE_HALIGN_RIGHT ? PANGO_ALIGN_RIGHT
- : halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
- : PANGO_ALIGN_CENTER));
- pango_layout_set_width (
- font->layout,
- bb[X][1] == INT_MAX ? -1 : xr_to_pango (bb[X][1] - bb[X][0]));
- pango_layout_set_wrap (font->layout, PANGO_WRAP_WORD);
-
- if (clip[H][0] != clip[H][1])
- {
- cairo_save (xr->cairo);
- if (!(options & TAB_ROTATE))
- xr_clip (xr, clip);
- if (options & TAB_ROTATE)
- {
- cairo_translate (xr->cairo,
- xr_to_pt (bb[H][0] + xr->x),
- xr_to_pt (bb[V][1] + xr->y));
- cairo_rotate (xr->cairo, -M_PI_2);
- }
- else
- cairo_translate (xr->cairo,
- xr_to_pt (bb[H][0] + xr->x),
- xr_to_pt (bb[V][0] + xr->y));
- pango_cairo_show_layout (xr->cairo, font->layout);
-
- /* If enabled, this draws a blue rectangle around the extents of each
- line of text, which can be rather useful for debugging layout
- issues. */
- if (0)
- {
- PangoLayoutIter *iter;
- iter = pango_layout_get_iter (font->layout);
- do
- {
- PangoRectangle extents;
-
- pango_layout_iter_get_line_extents (iter, &extents, NULL);
- cairo_save (xr->cairo);
- cairo_set_source_rgb (xr->cairo, 1, 0, 0);
- dump_rectangle (xr,
- pango_to_xr (extents.x) - xr->x,
- pango_to_xr (extents.y) - xr->y,
- pango_to_xr (extents.x + extents.width) - xr->x,
- pango_to_xr (extents.y + extents.height) - xr->y);
- cairo_restore (xr->cairo);
- }
- while (pango_layout_iter_next_line (iter));
- pango_layout_iter_free (iter);
- }
-
- cairo_restore (xr->cairo);
- }
-
- int size[TABLE_N_AXES];
- pango_layout_get_size (font->layout, &size[H], &size[V]);
- int w = pango_to_xr (size[X]);
- int h = pango_to_xr (size[Y]);
- if (w > *widthp)
- *widthp = w;
- if (bb[V][0] + h >= bb[V][1] && !(options & TAB_ROTATE))
- {
- PangoLayoutIter *iter;
- int best = 0;
-
- /* Choose a breakpoint between lines instead of in the middle of one. */
- iter = pango_layout_get_iter (font->layout);
- do
- {
- PangoRectangle extents;
- int y0, y1;
- int bottom;
-
- pango_layout_iter_get_line_extents (iter, NULL, &extents);
- pango_layout_iter_get_line_yrange (iter, &y0, &y1);
- extents.x = pango_to_xr (extents.x);
- extents.y = pango_to_xr (y0);
- extents.width = pango_to_xr (extents.width);
- extents.height = pango_to_xr (y1 - y0);
- bottom = bb[V][0] + extents.y + extents.height;
- if (bottom < bb[V][1])
- {
- if (brk && clip[H][0] != clip[H][1])
- best = bottom;
- if (brk)
- *brk = bottom;
- }
- else
- break;
- }
- while (pango_layout_iter_next_line (iter));
- pango_layout_iter_free (iter);
-
- /* If enabled, draws a green line across the chosen breakpoint, which can
- be useful for debugging issues with breaking. */
- if (0)
- {
- if (best && !xr->nest)
- dump_line (xr, -xr->left_margin, best,
- xr->width + xr->right_margin, best,
- RENDER_LINE_SINGLE,
- &(struct cell_color) CELL_COLOR (0, 255, 0));
- }
- }
-
- pango_layout_set_attributes (font->layout, NULL);
-
- if (font == &local_font)
- {
- g_object_unref (G_OBJECT (font->layout));
- pango_font_description_free (font->desc);
- }
-
- return h;
-}
-
-static void
-xr_layout_cell (struct xr_driver *xr, const struct table_cell *cell,
- int bb[TABLE_N_AXES][2], int clip[TABLE_N_AXES][2],
- int *width, int *height, int *brk)
-{
- *width = 0;
- *height = 0;
-
- /* If enabled, draws a blue rectangle around the cell extents, which can be
- useful for debugging layout. */
- if (0)
- {
- if (clip[H][0] != clip[H][1])
- {
- int offset = (xr->nest) * XR_POINT;
-
- cairo_save (xr->cairo);
- cairo_set_source_rgb (xr->cairo, 0, 0, 1);
- dump_rectangle (xr,
- bb[H][0] + offset, bb[V][0] + offset,
- bb[H][1] - offset, bb[V][1] - offset);
- cairo_restore (xr->cairo);
- }
- }
-
- if (brk)
- *brk = bb[V][0];
- *height = xr_layout_cell_text (xr, cell, bb, clip, width, brk);