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-eject-item.h"
31 #include "output/page-setup-item.h"
32 #include "output/table-item.h"
33 #include "output/text-item.h"
35 #include "gl/xalloc.h"
38 #define _(msgid) gettext (msgid)
40 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
44 struct xr_page_style
*
45 xr_page_style_ref (const struct xr_page_style
*ps_
)
47 struct xr_page_style
*ps
= CONST_CAST (struct xr_page_style
*, ps_
);
48 assert (ps
->ref_cnt
> 0);
53 struct xr_page_style
*
54 xr_page_style_unshare (struct xr_page_style
*old
)
56 assert (old
->ref_cnt
> 0);
57 if (old
->ref_cnt
== 1)
60 xr_page_style_unref (old
);
62 struct xr_page_style
*new = xmemdup (old
, sizeof *old
);
64 for (int i
= 0; i
< 2; i
++)
65 page_heading_copy (&new->headings
[i
], &old
->headings
[i
]);
71 xr_page_style_unref (struct xr_page_style
*ps
)
75 assert (ps
->ref_cnt
> 0);
78 for (int i
= 0; i
< 2; i
++)
79 page_heading_uninit (&ps
->headings
[i
]);
86 xr_page_style_equals (const struct xr_page_style
*a
,
87 const struct xr_page_style
*b
)
89 for (int i
= 0; i
< TABLE_N_AXES
; i
++)
90 for (int j
= 0; j
< 2; j
++)
91 if (a
->margins
[i
][j
] != b
->margins
[i
][j
])
94 for (int i
= 0; i
< 2; i
++)
95 if (!page_heading_equals (&a
->headings
[i
], &b
->headings
[i
]))
98 return (a
->initial_page_number
== b
->initial_page_number
99 && a
->object_spacing
== b
->object_spacing
);
104 struct xr_page_style
*page_style
;
105 struct xr_fsm_style
*fsm_style
;
107 int heading_heights
[2];
109 /* Current output item. */
111 struct output_item
*item
;
114 /* Grouping, for constructing the outline for PDFs.
116 The 'group_ids' were returned by cairo_pdf_surface_add_outline() and
117 represent the groups within which upcoming output is nested. The
118 'group_opens' will be passed to cairo_pdf_surface_add_outline() when the
119 next item is rendered (we defer it so that the location associated with
120 the outline item can be the first object actually output in it). */
122 size_t n_group_ids
, allocated_group_ids
;
123 struct group_open_item
**group_opens
;
124 size_t n_opens
, allocated_opens
;
126 /* Current output page. */
131 static void xr_pager_run (struct xr_pager
*);
133 /* Conversions to and from points. */
137 return x
/ (double) XR_POINT
;
141 pango_to_xr (int pango
)
143 return (XR_POINT
!= PANGO_SCALE
144 ? ceil (pango
* (1. * XR_POINT
/ PANGO_SCALE
))
151 return (XR_POINT
!= PANGO_SCALE
152 ? ceil (xr
* (1. / XR_POINT
* PANGO_SCALE
))
157 get_layout_height (PangoLayout
*layout
)
160 pango_layout_get_size (layout
, &w
, &h
);
165 xr_render_page_heading (cairo_t
*cairo
, const PangoFontDescription
*font
,
166 const struct page_heading
*ph
, int page_number
,
167 int width
, int base_y
, double font_resolution
)
169 PangoContext
*context
= pango_cairo_create_context (cairo
);
170 pango_cairo_context_set_resolution (context
, font_resolution
);
171 PangoLayout
*layout
= pango_layout_new (context
);
172 g_object_unref (context
);
174 pango_layout_set_font_description (layout
, font
);
177 for (size_t i
= 0; i
< ph
->n
; i
++)
179 const struct page_paragraph
*pp
= &ph
->paragraphs
[i
];
181 char *markup
= output_driver_substitute_heading_vars (pp
->markup
,
183 pango_layout_set_markup (layout
, markup
, -1);
186 pango_layout_set_alignment (
188 (pp
->halign
== TABLE_HALIGN_LEFT
? PANGO_ALIGN_LEFT
189 : pp
->halign
== TABLE_HALIGN_CENTER
? PANGO_ALIGN_CENTER
190 : pp
->halign
== TABLE_HALIGN_MIXED
? PANGO_ALIGN_LEFT
191 : PANGO_ALIGN_RIGHT
));
192 pango_layout_set_width (layout
, xr_to_pango (width
));
195 cairo_translate (cairo
, 0, xr_to_pt (y
+ base_y
));
196 pango_cairo_show_layout (cairo
, layout
);
197 cairo_restore (cairo
);
199 y
+= pango_to_xr (get_layout_height (layout
));
202 g_object_unref (G_OBJECT (layout
));
208 xr_measure_headings (const struct xr_page_style
*ps
,
209 const struct xr_fsm_style
*fs
,
210 int heading_heights
[2])
212 cairo_surface_t
*surface
= cairo_recording_surface_create (
213 CAIRO_CONTENT_COLOR
, NULL
);
214 cairo_t
*cairo
= cairo_create (surface
);
215 for (int i
= 0; i
< 2; i
++)
217 int *h
= &heading_heights
[i
];
218 *h
= xr_render_page_heading (cairo
, fs
->fonts
[XR_FONT_PROPORTIONAL
],
219 &ps
->headings
[i
], -1, fs
->size
[H
], 0,
220 fs
->font_resolution
);
222 *h
+= ps
->object_spacing
;
224 cairo_destroy (cairo
);
225 cairo_surface_destroy (surface
);
229 xr_pager_create (const struct xr_page_style
*ps_
,
230 const struct xr_fsm_style
*fs_
)
232 struct xr_page_style
*ps
= xr_page_style_ref (ps_
);
233 struct xr_fsm_style
*fs
= xr_fsm_style_ref (fs_
);
235 int heading_heights
[2];
236 xr_measure_headings (ps
, fs
, heading_heights
);
237 int total
= heading_heights
[0] + heading_heights
[1];
238 if (total
> 0 && total
< fs
->size
[V
])
240 fs
= xr_fsm_style_unshare (fs
);
241 ps
= xr_page_style_unshare (ps
);
243 for (int i
= 0; i
< 2; i
++)
244 ps
->margins
[V
][i
] += heading_heights
[i
];
245 fs
->size
[V
] -= total
;
248 struct xr_pager
*p
= xmalloc (sizeof *p
);
249 *p
= (struct xr_pager
) { .page_style
= ps
, .fsm_style
= fs
};
254 xr_pager_destroy (struct xr_pager
*p
)
259 for (size_t i
= 0; i
< p
->n_opens
; i
++)
260 group_open_item_unref (p
->group_opens
[i
]);
261 free (p
->group_opens
);
263 xr_page_style_unref (p
->page_style
);
264 xr_fsm_style_unref (p
->fsm_style
);
266 xr_fsm_destroy (p
->fsm
);
267 output_item_unref (p
->item
);
271 cairo_restore (p
->cr
);
272 cairo_destroy (p
->cr
);
279 xr_pager_has_item (const struct xr_pager
*p
)
281 return p
->item
!= NULL
;
285 xr_pager_add_item (struct xr_pager
*p
, const struct output_item
*item
)
288 p
->item
= output_item_ref (item
);
294 xr_pager_has_page (const struct xr_pager
*p
)
300 xr_pager_add_page (struct xr_pager
*p
, cairo_t
*cr
)
307 const struct xr_fsm_style
*fs
= p
->fsm_style
;
308 const struct xr_page_style
*ps
= p
->page_style
;
310 xr_to_pt (ps
->margins
[H
][0]),
311 xr_to_pt (ps
->margins
[V
][0]));
313 const PangoFontDescription
*font
= fs
->fonts
[XR_FONT_PROPORTIONAL
];
314 int page_number
= p
->page_index
++ + ps
->initial_page_number
;
315 if (p
->heading_heights
[0])
316 xr_render_page_heading (cr
, font
, &ps
->headings
[0], page_number
,
317 fs
->size
[H
], -p
->heading_heights
[0],
318 fs
->font_resolution
);
320 if (p
->heading_heights
[1])
321 xr_render_page_heading (cr
, font
, &ps
->headings
[1], page_number
,
322 fs
->size
[H
], fs
->size
[V
] + ps
->object_spacing
,
323 fs
->font_resolution
);
325 cairo_surface_t
*surface
= cairo_get_target (cr
);
326 if (cairo_surface_get_type (surface
) == CAIRO_SURFACE_TYPE_PDF
)
328 char *page_label
= xasprintf ("%d", page_number
);
329 cairo_pdf_surface_set_page_label (surface
, page_label
);
337 xr_pager_finish_page (struct xr_pager
*p
)
341 cairo_restore (p
->cr
);
342 cairo_destroy (p
->cr
);
348 xr_pager_needs_new_page (struct xr_pager
*p
)
350 if (p
->item
&& (!p
->cr
|| p
->y
>= p
->fsm_style
->size
[V
]))
352 xr_pager_finish_page (p
);
360 add_outline (cairo_t
*cr
, int parent_id
,
361 const char *utf8
, const char *link_attribs
,
362 cairo_pdf_outline_flags_t flags
)
364 cairo_surface_t
*surface
= cairo_get_target (cr
);
365 return (cairo_surface_get_type (surface
) == CAIRO_SURFACE_TYPE_PDF
366 ? cairo_pdf_surface_add_outline (surface
, parent_id
,
367 utf8
, link_attribs
, flags
)
372 xr_pager_run (struct xr_pager
*p
)
374 if (p
->item
&& p
->cr
&& p
->y
< p
->fsm_style
->size
[V
])
378 if (is_group_open_item (p
->item
))
380 if (p
->n_opens
>= p
->allocated_opens
)
381 p
->group_opens
= x2nrealloc (p
->group_opens
,
383 sizeof p
->group_opens
);
384 p
->group_opens
[p
->n_opens
++] = group_open_item_ref (
385 to_group_open_item (p
->item
));
387 else if (is_group_close_item (p
->item
))
390 group_open_item_unref (p
->group_opens
[--p
->n_opens
]);
391 else if (p
->n_group_ids
)
395 /* Something wrong! */
399 p
->fsm
= xr_fsm_create (p
->item
, p
->fsm_style
, p
->cr
);
402 output_item_unref (p
->item
);
411 char *dest_name
= NULL
;
412 if (p
->page_style
->include_outline
)
414 static int counter
= 0;
415 dest_name
= xasprintf ("dest%d", counter
++);
416 char *attrs
= xasprintf ("name='%s'", dest_name
);
417 cairo_tag_begin (p
->cr
, CAIRO_TAG_DEST
, attrs
);
421 int spacing
= p
->page_style
->object_spacing
;
422 int chunk
= xr_fsm_draw_slice (p
->fsm
, p
->cr
,
423 p
->fsm_style
->size
[V
] - p
->y
);
424 p
->y
+= chunk
+ spacing
;
425 cairo_translate (p
->cr
, 0, xr_to_pt (chunk
+ spacing
));
427 if (p
->page_style
->include_outline
)
429 cairo_tag_end (p
->cr
, CAIRO_TAG_DEST
);
431 if (chunk
&& p
->slice_idx
++ == 0)
433 char *attrs
= xasprintf ("dest='%s'", dest_name
);
435 int parent_group_id
= (p
->n_group_ids
436 ? p
->group_ids
[p
->n_group_ids
- 1]
437 : CAIRO_PDF_OUTLINE_ROOT
);
438 for (size_t i
= 0; i
< p
->n_opens
; i
++)
440 parent_group_id
= add_outline (
441 p
->cr
, parent_group_id
,
442 p
->group_opens
[i
]->command_name
, attrs
,
443 CAIRO_PDF_OUTLINE_FLAG_OPEN
);
444 group_open_item_unref (p
->group_opens
[i
]);
446 if (p
->n_group_ids
>= p
->allocated_group_ids
)
447 p
->group_ids
= x2nrealloc (p
->group_ids
,
448 &p
->allocated_group_ids
,
449 sizeof *p
->group_ids
);
450 p
->group_ids
[p
->n_group_ids
++] = parent_group_id
;
454 add_outline (p
->cr
, parent_group_id
,
455 output_item_get_label (p
->item
), attrs
, 0);
461 if (xr_fsm_is_empty (p
->fsm
))
463 xr_fsm_destroy (p
->fsm
);
465 output_item_unref (p
->item
);