1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2009, 2010, 2014, 2020 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "output/cairo-pager.h"
22 #include <cairo/cairo-pdf.h>
23 #include <pango/pango-layout.h>
24 #include <pango/pangocairo.h>
26 #include "output/chart-item.h"
27 #include "output/driver-provider.h"
28 #include "output/group-item.h"
29 #include "output/message-item.h"
30 #include "output/page-setup-item.h"
31 #include "output/table-item.h"
32 #include "output/text-item.h"
34 #include "gl/xalloc.h"
37 #define _(msgid) gettext (msgid)
39 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
43 struct xr_page_style *
44 xr_page_style_ref (const struct xr_page_style *ps_)
46 struct xr_page_style *ps = CONST_CAST (struct xr_page_style *, ps_);
47 assert (ps->ref_cnt > 0);
52 struct xr_page_style *
53 xr_page_style_unshare (struct xr_page_style *old)
55 assert (old->ref_cnt > 0);
56 if (old->ref_cnt == 1)
59 xr_page_style_unref (old);
61 struct xr_page_style *new = xmemdup (old, sizeof *old);
63 for (int i = 0; i < 2; i++)
64 page_heading_copy (&new->headings[i], &old->headings[i]);
70 xr_page_style_unref (struct xr_page_style *ps)
74 assert (ps->ref_cnt > 0);
77 for (int i = 0; i < 2; i++)
78 page_heading_uninit (&ps->headings[i]);
85 xr_page_style_equals (const struct xr_page_style *a,
86 const struct xr_page_style *b)
88 for (int i = 0; i < TABLE_N_AXES; i++)
89 for (int j = 0; j < 2; j++)
90 if (a->margins[i][j] != b->margins[i][j])
93 for (int i = 0; i < 2; i++)
94 if (!page_heading_equals (&a->headings[i], &b->headings[i]))
97 return a->initial_page_number == b->initial_page_number;
102 struct xr_page_style *page_style;
103 struct xr_fsm_style *fsm_style;
105 int heading_heights[2];
107 /* Current output item. */
109 struct output_item *item;
112 /* Grouping, for constructing the outline for PDFs.
114 The 'group_ids' were returned by cairo_pdf_surface_add_outline() and
115 represent the groups within which upcoming output is nested. The
116 'group_opens' will be passed to cairo_pdf_surface_add_outline() when the
117 next item is rendered (we defer it so that the location associated with
118 the outline item can be the first object actually output in it). */
120 size_t n_group_ids, allocated_group_ids;
121 struct group_open_item **group_opens;
122 size_t n_opens, allocated_opens;
124 /* Current output page. */
129 static void xr_pager_run (struct xr_pager *);
131 /* Conversions to and from points. */
135 return x / (double) XR_POINT;
139 pango_to_xr (int pango)
141 return (XR_POINT != PANGO_SCALE
142 ? ceil (pango * (1. * XR_POINT / PANGO_SCALE))
149 return (XR_POINT != PANGO_SCALE
150 ? ceil (xr * (1. / XR_POINT * PANGO_SCALE))
155 get_layout_height (PangoLayout *layout)
158 pango_layout_get_size (layout, &w, &h);
163 xr_render_page_heading (cairo_t *cairo, const PangoFontDescription *font,
164 const struct page_heading *ph, int page_number,
165 int width, int base_y, double font_resolution)
167 PangoContext *context = pango_cairo_create_context (cairo);
168 pango_cairo_context_set_resolution (context, font_resolution);
169 PangoLayout *layout = pango_layout_new (context);
170 g_object_unref (context);
172 pango_layout_set_font_description (layout, font);
175 for (size_t i = 0; i < ph->n; i++)
177 const struct page_paragraph *pp = &ph->paragraphs[i];
179 char *markup = output_driver_substitute_heading_vars (pp->markup,
181 pango_layout_set_markup (layout, markup, -1);
184 pango_layout_set_alignment (
186 (pp->halign == TABLE_HALIGN_LEFT ? PANGO_ALIGN_LEFT
187 : pp->halign == TABLE_HALIGN_CENTER ? PANGO_ALIGN_CENTER
188 : pp->halign == TABLE_HALIGN_MIXED ? PANGO_ALIGN_LEFT
189 : PANGO_ALIGN_RIGHT));
190 pango_layout_set_width (layout, xr_to_pango (width));
193 cairo_translate (cairo, 0, xr_to_pt (y + base_y));
194 pango_cairo_show_layout (cairo, layout);
195 cairo_restore (cairo);
197 y += pango_to_xr (get_layout_height (layout));
200 g_object_unref (G_OBJECT (layout));
206 xr_measure_headings (const struct xr_page_style *ps,
207 const struct xr_fsm_style *fs,
208 int heading_heights[2])
210 cairo_surface_t *surface = cairo_recording_surface_create (
211 CAIRO_CONTENT_COLOR, NULL);
212 cairo_t *cairo = cairo_create (surface);
213 for (int i = 0; i < 2; i++)
215 int *h = &heading_heights[i];
216 *h = xr_render_page_heading (cairo, fs->font,
217 &ps->headings[i], -1, fs->size[H], 0,
218 fs->font_resolution);
220 *h += fs->object_spacing;
222 cairo_destroy (cairo);
223 cairo_surface_destroy (surface);
227 xr_pager_create (const struct xr_page_style *ps_,
228 const struct xr_fsm_style *fs_)
230 struct xr_page_style *ps = xr_page_style_ref (ps_);
231 struct xr_fsm_style *fs = xr_fsm_style_ref (fs_);
233 int heading_heights[2];
234 xr_measure_headings (ps, fs, heading_heights);
235 int total = heading_heights[0] + heading_heights[1];
236 if (total > 0 && total < fs->size[V])
238 fs = xr_fsm_style_unshare (fs);
239 ps = xr_page_style_unshare (ps);
241 for (int i = 0; i < 2; i++)
242 ps->margins[V][i] += heading_heights[i];
243 fs->size[V] -= total;
246 struct xr_pager *p = xmalloc (sizeof *p);
247 *p = (struct xr_pager) { .page_style = ps, .fsm_style = fs };
252 xr_pager_destroy (struct xr_pager *p)
257 for (size_t i = 0; i < p->n_opens; i++)
258 group_open_item_unref (p->group_opens[i]);
259 free (p->group_opens);
261 xr_page_style_unref (p->page_style);
262 xr_fsm_style_unref (p->fsm_style);
264 xr_fsm_destroy (p->fsm);
265 output_item_unref (p->item);
269 cairo_restore (p->cr);
270 cairo_destroy (p->cr);
277 xr_pager_has_item (const struct xr_pager *p)
279 return p->item != NULL;
283 xr_pager_add_item (struct xr_pager *p, const struct output_item *item)
286 p->item = output_item_ref (item);
292 xr_pager_has_page (const struct xr_pager *p)
298 xr_pager_add_page (struct xr_pager *p, cairo_t *cr)
305 const struct xr_fsm_style *fs = p->fsm_style;
306 const struct xr_page_style *ps = p->page_style;
308 xr_to_pt (ps->margins[H][0]),
309 xr_to_pt (ps->margins[V][0]));
311 int page_number = p->page_index++ + ps->initial_page_number;
312 if (p->heading_heights[0])
313 xr_render_page_heading (cr, fs->font, &ps->headings[0], page_number,
314 fs->size[H], -p->heading_heights[0],
315 fs->font_resolution);
317 if (p->heading_heights[1])
318 xr_render_page_heading (cr, fs->font, &ps->headings[1], page_number,
319 fs->size[H], fs->size[V] + fs->object_spacing,
320 fs->font_resolution);
322 cairo_surface_t *surface = cairo_get_target (cr);
323 if (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_PDF)
325 char *page_label = xasprintf ("%d", page_number);
326 cairo_pdf_surface_set_page_label (surface, page_label);
334 xr_pager_finish_page (struct xr_pager *p)
338 cairo_restore (p->cr);
339 cairo_destroy (p->cr);
345 xr_pager_needs_new_page (struct xr_pager *p)
347 if (p->item && (!p->cr || p->y >= p->fsm_style->size[V]))
349 xr_pager_finish_page (p);
357 add_outline (cairo_t *cr, int parent_id,
358 const char *utf8, const char *link_attribs,
359 cairo_pdf_outline_flags_t flags)
361 cairo_surface_t *surface = cairo_get_target (cr);
362 return (cairo_surface_get_type (surface) == CAIRO_SURFACE_TYPE_PDF
363 ? cairo_pdf_surface_add_outline (surface, parent_id,
364 utf8, link_attribs, flags)
369 xr_pager_run (struct xr_pager *p)
371 if (p->item && p->cr && p->y < p->fsm_style->size[V])
375 if (is_group_open_item (p->item))
377 if (p->n_opens >= p->allocated_opens)
378 p->group_opens = x2nrealloc (p->group_opens,
380 sizeof p->group_opens);
381 p->group_opens[p->n_opens++] = group_open_item_ref (
382 to_group_open_item (p->item));
384 else if (is_group_close_item (p->item))
387 group_open_item_unref (p->group_opens[--p->n_opens]);
388 else if (p->n_group_ids)
392 /* Something wrong! */
396 p->fsm = xr_fsm_create_for_printing (p->item, p->fsm_style, p->cr);
399 output_item_unref (p->item);
408 char *dest_name = NULL;
409 if (p->page_style->include_outline)
411 static int counter = 0;
412 dest_name = xasprintf ("dest%d", counter++);
413 char *attrs = xasprintf ("name='%s'", dest_name);
414 cairo_tag_begin (p->cr, CAIRO_TAG_DEST, attrs);
418 int spacing = p->fsm_style->object_spacing;
419 int chunk = xr_fsm_draw_slice (p->fsm, p->cr,
420 p->fsm_style->size[V] - p->y);
421 p->y += chunk + spacing;
422 cairo_translate (p->cr, 0, xr_to_pt (chunk + spacing));
424 if (p->page_style->include_outline)
426 cairo_tag_end (p->cr, CAIRO_TAG_DEST);
428 if (chunk && p->slice_idx++ == 0)
430 char *attrs = xasprintf ("dest='%s'", dest_name);
432 int parent_group_id = (p->n_group_ids
433 ? p->group_ids[p->n_group_ids - 1]
434 : CAIRO_PDF_OUTLINE_ROOT);
435 for (size_t i = 0; i < p->n_opens; i++)
437 parent_group_id = add_outline (
438 p->cr, parent_group_id,
439 output_item_get_label (&p->group_opens[i]->output_item),
440 attrs, CAIRO_PDF_OUTLINE_FLAG_OPEN);
441 group_open_item_unref (p->group_opens[i]);
443 if (p->n_group_ids >= p->allocated_group_ids)
444 p->group_ids = x2nrealloc (p->group_ids,
445 &p->allocated_group_ids,
446 sizeof *p->group_ids);
447 p->group_ids[p->n_group_ids++] = parent_group_id;
451 add_outline (p->cr, parent_group_id,
452 output_item_get_label (p->item), attrs, 0);
458 if (xr_fsm_is_empty (p->fsm))
460 xr_fsm_destroy (p->fsm);
462 output_item_unref (p->item);