#include "gettext.h"
#define _(msgid) gettext (msgid)
+
+/* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
+#define H TABLE_HORZ
+#define V TABLE_VERT
\f
struct xr_page_style *
xr_page_style_ref (const struct xr_page_style *ps_)
return ps;
}
+struct xr_page_style *
+xr_page_style_unshare (struct xr_page_style *old)
+{
+ assert (old->ref_cnt > 0);
+ if (old->ref_cnt == 1)
+ return old;
+
+ xr_page_style_unref (old);
+
+ struct xr_page_style *new = xmemdup (old, sizeof *old);
+ new->ref_cnt = 1;
+ for (int i = 0; i < 2; i++)
+ page_heading_copy (&new->headings[i], &old->headings[i]);
+
+ return new;
+}
+
void
xr_page_style_unref (struct xr_page_style *ps)
{
{
for (int i = 0; i < 2; i++)
page_heading_uninit (&ps->headings[i]);
- if (ps->font)
- pango_font_description_free (ps->font);
free (ps);
}
}
}
-static bool
-pfd_equals (const PangoFontDescription *a,
- const PangoFontDescription *b)
-{
- return a && b ? pango_font_description_equal (a, b) : !a && !b;
-}
-
bool
xr_page_style_equals (const struct xr_page_style *a,
const struct xr_page_style *b)
{
for (int i = 0; i < TABLE_N_AXES; i++)
- {
- if (a->size[i] != b->size[i])
+ for (int j = 0; j < 2; j++)
+ if (a->margins[i][j] != b->margins[i][j])
return false;
- for (int j = 0; j < 2; j++)
- if (a->margins[i][j] != b->margins[i][j])
- return false;
- }
-
for (int i = 0; i < 2; i++)
if (!page_heading_equals (&a->headings[i], &b->headings[i]))
return false;
- return (pfd_equals (a->font, b->font)
- && a->font_scale == b->font_scale
- && a->initial_page_number == b->initial_page_number
+ return (a->initial_page_number == b->initial_page_number
&& a->object_spacing == b->object_spacing);
}
\f
struct xr_pager
{
struct xr_page_style *page_style;
+ struct xr_fsm_style *fsm_style;
int page_index;
int heading_heights[2];
/* Current output item. */
struct xr_fsm *fsm;
- struct xr_fsm_style *fsm_style;
struct output_item *item;
/* Current output page. */
static int
xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
const struct page_heading *ph, int page_number,
- int width, bool draw, int base_y)
+ int width, int base_y, double font_resolution)
{
- PangoLayout *layout = pango_cairo_create_layout (cairo);
+ PangoContext *context = pango_cairo_create_context (cairo);
+ pango_cairo_context_set_resolution (context, font_resolution);
+ PangoLayout *layout = pango_layout_new (context);
+ g_object_unref (context);
+
pango_layout_set_font_description (layout, font);
int y = 0;
: pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
: PANGO_ALIGN_RIGHT));
pango_layout_set_width (layout, xr_to_pango (width));
- if (draw)
- {
- cairo_save (cairo);
- cairo_translate (cairo, 0, xr_to_pt (y + base_y));
- pango_cairo_show_layout (cairo, layout);
- cairo_restore (cairo);
- }
+
+ cairo_save (cairo);
+ cairo_translate (cairo, 0, xr_to_pt (y + base_y));
+ pango_cairo_show_layout (cairo, layout);
+ cairo_restore (cairo);
y += pango_to_xr (get_layout_height (layout));
}
}
static void
-xr_measure_headings (struct xr_pager *p)
+xr_measure_headings (const struct xr_page_style *ps,
+ const struct xr_fsm_style *fs,
+ int heading_heights[2])
{
- const struct xr_page_style *ps = p->page_style;
-
cairo_surface_t *surface = cairo_recording_surface_create (
CAIRO_CONTENT_COLOR, NULL);
cairo_t *cairo = cairo_create (surface);
for (int i = 0; i < 2; i++)
{
- int h = xr_render_page_heading (cairo, ps->font, &ps->headings[i], -1,
- ps->size[TABLE_HORZ], false, 0);
-
- /* If the heading is nonempty, add a margin above or below it. */
- if (h)
- h += ps->object_spacing;
-
- p->heading_heights[i] = h;
+ int *h = &heading_heights[i];
+ *h = xr_render_page_heading (cairo, fs->fonts[XR_FONT_PROPORTIONAL],
+ &ps->headings[i], -1, fs->size[H], 0,
+ fs->font_resolution);
+ if (*h)
+ *h += ps->object_spacing;
}
cairo_destroy (cairo);
cairo_surface_destroy (surface);
-
- int total = p->heading_heights[0] + p->heading_heights[1];
- if (total >= ps->size[TABLE_VERT])
- p->heading_heights[0] = p->heading_heights[1] = 0;
}
struct xr_pager *
-xr_pager_create (const struct xr_page_style *ps)
+xr_pager_create (const struct xr_page_style *ps_,
+ const struct xr_fsm_style *fs_)
{
- struct xr_pager *p = xmalloc (sizeof *p);
- *p = (struct xr_pager) { .page_style = xr_page_style_ref (ps) };
+ struct xr_page_style *ps = xr_page_style_ref (ps_);
+ struct xr_fsm_style *fs = xr_fsm_style_ref (fs_);
- xr_measure_headings (p);
+ int heading_heights[2];
+ xr_measure_headings (ps, fs, heading_heights);
+ int total = heading_heights[0] + heading_heights[1];
+ if (total > 0 && total < fs->size[V])
+ {
+ fs = xr_fsm_style_unshare (fs);
+ ps = xr_page_style_unshare (ps);
+
+ for (int i = 0; i < 2; i++)
+ ps->margins[V][i] += heading_heights[i];
+ fs->size[V] -= total;
+ }
+ struct xr_pager *p = xmalloc (sizeof *p);
+ *p = (struct xr_pager) { .page_style = ps, .fsm_style = fs };
return p;
}
{
if (p)
{
- xr_fsm_destroy (p->fsm);
+ xr_page_style_unref (p->page_style);
xr_fsm_style_unref (p->fsm_style);
+
+ xr_fsm_destroy (p->fsm);
output_item_unref (p->item);
- xr_page_style_unref (p->page_style);
if (p->cr)
- cairo_destroy (p->cr);
+ {
+ cairo_restore (p->cr);
+ cairo_destroy (p->cr);
+ }
free (p);
}
}
}
void
-xr_pager_add_item (struct xr_pager *p, const struct xr_fsm_style *fsm_style,
- const struct output_item *item)
+xr_pager_add_item (struct xr_pager *p, const struct output_item *item)
{
assert (!p->item);
p->item = output_item_ref (item);
- p->fsm_style = xr_fsm_style_ref (fsm_style);
xr_pager_run (p);
}
xr_pager_add_page (struct xr_pager *p, cairo_t *cr)
{
assert (!p->cr);
+ cairo_save (cr);
p->cr = cr;
p->y = 0;
+ const struct xr_fsm_style *fs = p->fsm_style;
const struct xr_page_style *ps = p->page_style;
const struct cell_color *bg = &ps->bg;
if (bg->alpha)
cairo_save (cr);
cairo_set_source_rgba (cr, bg->r / 255.0, bg->g / 255.0,
bg->b / 255.0, bg->alpha / 255.0);
- cairo_rectangle (cr, 0, 0, ps->size[TABLE_HORZ], ps->size[TABLE_VERT]);
+ cairo_rectangle (cr, 0, 0, fs->size[H], fs->size[V]);
cairo_fill (cr);
cairo_restore (cr);
}
cairo_translate (cr,
- xr_to_pt (ps->margins[TABLE_HORZ][0]),
- xr_to_pt (ps->margins[TABLE_VERT][0]));
+ xr_to_pt (ps->margins[H][0]),
+ xr_to_pt (ps->margins[V][0]));
+ const PangoFontDescription *font = fs->fonts[XR_FONT_PROPORTIONAL];
int page_number = p->page_index++ + ps->initial_page_number;
if (p->heading_heights[0])
- xr_render_page_heading (cr, ps->font, &ps->headings[0], page_number,
- ps->size[TABLE_HORZ], true, 0);
+ xr_render_page_heading (cr, font, &ps->headings[0], page_number,
+ fs->size[H], -p->heading_heights[0],
+ fs->font_resolution);
if (p->heading_heights[1])
- xr_render_page_heading (
- cr, ps->font, &ps->headings[1], page_number,
- ps->size[TABLE_HORZ], true,
- ps->size[TABLE_VERT] - p->heading_heights[1] + ps->object_spacing);
-
- cairo_translate (cr, 0, xr_to_pt (p->heading_heights[0]));
+ xr_render_page_heading (cr, font, &ps->headings[1], page_number,
+ fs->size[H], fs->size[V] + ps->object_spacing,
+ fs->font_resolution);
xr_pager_run (p);
}
bool
xr_pager_needs_new_page (struct xr_pager *p)
{
- int vsize = (p->page_style->size[TABLE_VERT]
- - p->heading_heights[0] - p->heading_heights[1]);
- if (p->item && p->y >= vsize)
+ if (p->item && (!p->cr || p->y >= p->fsm_style->size[V]))
{
- cairo_destroy (p->cr);
- p->cr = NULL;
+ if (p->cr)
+ {
+ cairo_restore (p->cr);
+ cairo_destroy (p->cr);
+ p->cr = NULL;
+ }
return true;
}
else
static void
xr_pager_run (struct xr_pager *p)
{
- int vsize = (p->page_style->size[TABLE_VERT]
- - p->heading_heights[0] - p->heading_heights[1]);
- if (p->item && p->cr && p->y < vsize)
+ if (p->item && p->cr && p->y < p->fsm_style->size[V])
{
if (!p->fsm)
{
- /* XXX fsm_style needs to account for heading_heights */
p->fsm = xr_fsm_create (p->item, p->fsm_style, p->cr);
if (!p->fsm)
{
- xr_fsm_style_unref (p->fsm_style);
- p->fsm_style = NULL;
output_item_unref (p->item);
p->item = NULL;
for (;;)
{
int spacing = p->page_style->object_spacing;
- int chunk = xr_fsm_draw_slice (p->fsm, p->cr, vsize - p->y);
+ int chunk = xr_fsm_draw_slice (p->fsm, p->cr,
+ p->fsm_style->size[V] - p->y);
p->y += chunk + spacing;
cairo_translate (p->cr, 0, xr_to_pt (chunk + spacing));
{
xr_fsm_destroy (p->fsm);
p->fsm = NULL;
- xr_fsm_style_unref (p->fsm_style);
- p->fsm_style = NULL;
output_item_unref (p->item);
p->item = NULL;
return;