1/* GTK - The GIMP Toolkit
2 * gtkprintcontext.c: Print Context
3 * Copyright (C) 2006, Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "config.h"
20#include "gtkprintoperation-private.h"
21
22
23/**
24 * GtkPrintContext:
25 *
26 * A `GtkPrintContext` encapsulates context information that is required when
27 * drawing pages for printing.
28 *
29 * This includes the cairo context and important parameters like page size
30 * and resolution. It also lets you easily create [class@Pango.Layout] and
31 * [class@Pango.Context] objects that match the font metrics of the cairo surface.
32 *
33 * `GtkPrintContext` objects get passed to the
34 * [signal@Gtk.PrintOperation::begin-print],
35 * [signal@Gtk.PrintOperation::end-print],
36 * [signal@Gtk.PrintOperation::request-page-setup] and
37 * [signal@Gtk.PrintOperation::draw-page] signals on the
38 * [class@Gtk.PrintOperation] object.
39 *
40 * ## Using GtkPrintContext in a ::draw-page callback
41 *
42 * ```c
43 * static void
44 * draw_page (GtkPrintOperation *operation,
45 * GtkPrintContext *context,
46 * int page_nr)
47 * {
48 * cairo_t *cr;
49 * PangoLayout *layout;
50 * PangoFontDescription *desc;
51 *
52 * cr = gtk_print_context_get_cairo_context (context);
53 *
54 * // Draw a red rectangle, as wide as the paper (inside the margins)
55 * cairo_set_source_rgb (cr, 1.0, 0, 0);
56 * cairo_rectangle (cr, 0, 0, gtk_print_context_get_width (context), 50);
57 *
58 * cairo_fill (cr);
59 *
60 * // Draw some lines
61 * cairo_move_to (cr, 20, 10);
62 * cairo_line_to (cr, 40, 20);
63 * cairo_arc (cr, 60, 60, 20, 0, M_PI);
64 * cairo_line_to (cr, 80, 20);
65 *
66 * cairo_set_source_rgb (cr, 0, 0, 0);
67 * cairo_set_line_width (cr, 5);
68 * cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
69 * cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
70 *
71 * cairo_stroke (cr);
72 *
73 * // Draw some text
74 * layout = gtk_print_context_create_pango_layout (context);
75 * pango_layout_set_text (layout, "Hello World! Printing is easy", -1);
76 * desc = pango_font_description_from_string ("sans 28");
77 * pango_layout_set_font_description (layout, desc);
78 * pango_font_description_free (desc);
79 *
80 * cairo_move_to (cr, 30, 20);
81 * pango_cairo_layout_path (cr, layout);
82 *
83 * // Font Outline
84 * cairo_set_source_rgb (cr, 0.93, 1.0, 0.47);
85 * cairo_set_line_width (cr, 0.5);
86 * cairo_stroke_preserve (cr);
87 *
88 * // Font Fill
89 * cairo_set_source_rgb (cr, 0, 0.0, 1.0);
90 * cairo_fill (cr);
91 *
92 * g_object_unref (layout);
93 * }
94 * ```
95 */
96
97
98typedef struct _GtkPrintContextClass GtkPrintContextClass;
99
100#define GTK_IS_PRINT_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_CONTEXT))
101#define GTK_PRINT_CONTEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_CONTEXT, GtkPrintContextClass))
102#define GTK_PRINT_CONTEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_CONTEXT, GtkPrintContextClass))
103
104#define MM_PER_INCH 25.4
105#define POINTS_PER_INCH 72
106
107struct _GtkPrintContext
108{
109 GObject parent_instance;
110
111 GtkPrintOperation *op;
112 cairo_t *cr;
113 GtkPageSetup *page_setup;
114
115 double surface_dpi_x;
116 double surface_dpi_y;
117
118 double pixels_per_unit_x;
119 double pixels_per_unit_y;
120
121 gboolean has_hard_margins;
122 double hard_margin_top;
123 double hard_margin_bottom;
124 double hard_margin_left;
125 double hard_margin_right;
126
127};
128
129struct _GtkPrintContextClass
130{
131 GObjectClass parent_class;
132};
133
134G_DEFINE_TYPE (GtkPrintContext, gtk_print_context, G_TYPE_OBJECT)
135
136static void
137gtk_print_context_finalize (GObject *object)
138{
139 GtkPrintContext *context = GTK_PRINT_CONTEXT (object);
140
141 if (context->page_setup)
142 g_object_unref (object: context->page_setup);
143
144 if (context->cr)
145 cairo_destroy (cr: context->cr);
146
147 G_OBJECT_CLASS (gtk_print_context_parent_class)->finalize (object);
148}
149
150static void
151gtk_print_context_init (GtkPrintContext *context)
152{
153}
154
155static void
156gtk_print_context_class_init (GtkPrintContextClass *class)
157{
158 GObjectClass *gobject_class = (GObjectClass *)class;
159
160 gobject_class->finalize = gtk_print_context_finalize;
161}
162
163
164GtkPrintContext *
165_gtk_print_context_new (GtkPrintOperation *op)
166{
167 GtkPrintContext *context;
168
169 context = g_object_new (GTK_TYPE_PRINT_CONTEXT, NULL);
170
171 context->op = op;
172 context->cr = NULL;
173 context->has_hard_margins = FALSE;
174
175 return context;
176}
177
178static PangoFontMap *
179_gtk_print_context_get_fontmap (GtkPrintContext *context)
180{
181 return pango_cairo_font_map_get_default ();
182}
183
184/**
185 * gtk_print_context_set_cairo_context:
186 * @context: a `GtkPrintContext`
187 * @cr: the cairo context
188 * @dpi_x: the horizontal resolution to use with @cr
189 * @dpi_y: the vertical resolution to use with @cr
190 *
191 * Sets a new cairo context on a print context.
192 *
193 * This function is intended to be used when implementing
194 * an internal print preview, it is not needed for printing,
195 * since GTK itself creates a suitable cairo context in that
196 * case.
197 */
198void
199gtk_print_context_set_cairo_context (GtkPrintContext *context,
200 cairo_t *cr,
201 double dpi_x,
202 double dpi_y)
203{
204 if (context->cr)
205 cairo_destroy (cr: context->cr);
206
207 context->cr = cairo_reference (cr);
208 context->surface_dpi_x = dpi_x;
209 context->surface_dpi_y = dpi_y;
210
211 switch (context->op->priv->unit)
212 {
213 default:
214 case GTK_UNIT_NONE:
215 /* Do nothing, this is the cairo default unit */
216 context->pixels_per_unit_x = 1.0;
217 context->pixels_per_unit_y = 1.0;
218 break;
219 case GTK_UNIT_POINTS:
220 context->pixels_per_unit_x = dpi_x / POINTS_PER_INCH;
221 context->pixels_per_unit_y = dpi_y / POINTS_PER_INCH;
222 break;
223 case GTK_UNIT_INCH:
224 context->pixels_per_unit_x = dpi_x;
225 context->pixels_per_unit_y = dpi_y;
226 break;
227 case GTK_UNIT_MM:
228 context->pixels_per_unit_x = dpi_x / MM_PER_INCH;
229 context->pixels_per_unit_y = dpi_y / MM_PER_INCH;
230 break;
231 }
232 cairo_scale (cr: context->cr,
233 sx: context->pixels_per_unit_x,
234 sy: context->pixels_per_unit_y);
235}
236
237
238void
239_gtk_print_context_rotate_according_to_orientation (GtkPrintContext *context)
240{
241 cairo_t *cr = context->cr;
242 cairo_matrix_t matrix;
243 GtkPaperSize *paper_size;
244 double width, height;
245
246 paper_size = gtk_page_setup_get_paper_size (setup: context->page_setup);
247
248 width = gtk_paper_size_get_width (size: paper_size, unit: GTK_UNIT_INCH);
249 width = width * context->surface_dpi_x / context->pixels_per_unit_x;
250 height = gtk_paper_size_get_height (size: paper_size, unit: GTK_UNIT_INCH);
251 height = height * context->surface_dpi_y / context->pixels_per_unit_y;
252
253 switch (gtk_page_setup_get_orientation (setup: context->page_setup))
254 {
255 default:
256 case GTK_PAGE_ORIENTATION_PORTRAIT:
257 break;
258 case GTK_PAGE_ORIENTATION_LANDSCAPE:
259 cairo_translate (cr, tx: 0, ty: height);
260 cairo_matrix_init (matrix: &matrix,
261 xx: 0, yx: -1,
262 xy: 1, yy: 0,
263 x0: 0, y0: 0);
264 cairo_transform (cr, matrix: &matrix);
265 break;
266 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
267 cairo_translate (cr, tx: width, ty: height);
268 cairo_matrix_init (matrix: &matrix,
269 xx: -1, yx: 0,
270 xy: 0, yy: -1,
271 x0: 0, y0: 0);
272 cairo_transform (cr, matrix: &matrix);
273 break;
274 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
275 cairo_translate (cr, tx: width, ty: 0);
276 cairo_matrix_init (matrix: &matrix,
277 xx: 0, yx: 1,
278 xy: -1, yy: 0,
279 x0: 0, y0: 0);
280 cairo_transform (cr, matrix: &matrix);
281 break;
282 }
283}
284
285void
286_gtk_print_context_reverse_according_to_orientation (GtkPrintContext *context)
287{
288 cairo_t *cr = context->cr;
289 cairo_matrix_t matrix;
290 double width, height;
291
292 width = gtk_page_setup_get_paper_width (setup: context->page_setup, unit: GTK_UNIT_INCH);
293 width = width * context->surface_dpi_x / context->pixels_per_unit_x;
294 height = gtk_page_setup_get_paper_height (setup: context->page_setup, unit: GTK_UNIT_INCH);
295 height = height * context->surface_dpi_y / context->pixels_per_unit_y;
296
297 switch (gtk_page_setup_get_orientation (setup: context->page_setup))
298 {
299 default:
300 case GTK_PAGE_ORIENTATION_PORTRAIT:
301 case GTK_PAGE_ORIENTATION_LANDSCAPE:
302 break;
303 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
304 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
305 cairo_translate (cr, tx: width, ty: height);
306 cairo_matrix_init (matrix: &matrix,
307 xx: -1, yx: 0,
308 xy: 0, yy: -1,
309 x0: 0, y0: 0);
310 cairo_transform (cr, matrix: &matrix);
311 break;
312 }
313}
314
315void
316_gtk_print_context_translate_into_margin (GtkPrintContext *context)
317{
318 double dx, dy;
319
320 g_return_if_fail (GTK_IS_PRINT_CONTEXT (context));
321
322 /* We do it this way to also handle GTK_UNIT_NONE */
323 switch (gtk_page_setup_get_orientation (setup: context->page_setup))
324 {
325 default:
326 case GTK_PAGE_ORIENTATION_PORTRAIT:
327 dx = gtk_page_setup_get_left_margin (setup: context->page_setup, unit: GTK_UNIT_INCH);
328 dy = gtk_page_setup_get_top_margin (setup: context->page_setup, unit: GTK_UNIT_INCH);
329 break;
330 case GTK_PAGE_ORIENTATION_LANDSCAPE:
331 dx = gtk_page_setup_get_bottom_margin (setup: context->page_setup, unit: GTK_UNIT_INCH);
332 dy = gtk_page_setup_get_left_margin (setup: context->page_setup, unit: GTK_UNIT_INCH);
333 break;
334 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
335 dx = gtk_page_setup_get_right_margin (setup: context->page_setup, unit: GTK_UNIT_INCH);
336 dy = gtk_page_setup_get_bottom_margin (setup: context->page_setup, unit: GTK_UNIT_INCH);
337 break;
338 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
339 dx = gtk_page_setup_get_top_margin (setup: context->page_setup, unit: GTK_UNIT_INCH);
340 dy = gtk_page_setup_get_right_margin (setup: context->page_setup, unit: GTK_UNIT_INCH);
341 break;
342 }
343
344 cairo_translate (cr: context->cr,
345 tx: dx * context->surface_dpi_x / context->pixels_per_unit_x,
346 ty: dy * context->surface_dpi_y / context->pixels_per_unit_y);
347}
348
349void
350_gtk_print_context_set_page_setup (GtkPrintContext *context,
351 GtkPageSetup *page_setup)
352{
353 g_return_if_fail (GTK_IS_PRINT_CONTEXT (context));
354 g_return_if_fail (page_setup == NULL ||
355 GTK_IS_PAGE_SETUP (page_setup));
356
357 if (page_setup != NULL)
358 g_object_ref (page_setup);
359
360 if (context->page_setup != NULL)
361 g_object_unref (object: context->page_setup);
362
363 context->page_setup = page_setup;
364}
365
366/**
367 * gtk_print_context_get_cairo_context:
368 * @context: a `GtkPrintContext`
369 *
370 * Obtains the cairo context that is associated with the
371 * `GtkPrintContext`.
372 *
373 * Returns: (transfer none): the cairo context of @context
374 */
375cairo_t *
376gtk_print_context_get_cairo_context (GtkPrintContext *context)
377{
378 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
379
380 return context->cr;
381}
382
383/**
384 * gtk_print_context_get_page_setup:
385 * @context: a `GtkPrintContext`
386 *
387 * Obtains the `GtkPageSetup` that determines the page
388 * dimensions of the `GtkPrintContext`.
389 *
390 * Returns: (transfer none): the page setup of @context
391 */
392GtkPageSetup *
393gtk_print_context_get_page_setup (GtkPrintContext *context)
394{
395 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
396
397 return context->page_setup;
398}
399
400/**
401 * gtk_print_context_get_width:
402 * @context: a `GtkPrintContext`
403 *
404 * Obtains the width of the `GtkPrintContext`, in pixels.
405 *
406 * Returns: the width of @context
407 */
408double
409gtk_print_context_get_width (GtkPrintContext *context)
410{
411 GtkPrintOperationPrivate *priv;
412 double width;
413
414 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0);
415
416 priv = context->op->priv;
417
418 if (priv->use_full_page)
419 width = gtk_page_setup_get_paper_width (setup: context->page_setup, unit: GTK_UNIT_INCH);
420 else
421 width = gtk_page_setup_get_page_width (setup: context->page_setup, unit: GTK_UNIT_INCH);
422
423 /* Really dpi_x? What about landscape? what does dpi_x mean in that case? */
424 return width * context->surface_dpi_x / context->pixels_per_unit_x;
425}
426
427/**
428 * gtk_print_context_get_height:
429 * @context: a `GtkPrintContext`
430 *
431 * Obtains the height of the `GtkPrintContext`, in pixels.
432 *
433 * Returns: the height of @context
434 */
435double
436gtk_print_context_get_height (GtkPrintContext *context)
437{
438 GtkPrintOperationPrivate *priv;
439 double height;
440
441 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0);
442
443 priv = context->op->priv;
444
445 if (priv->use_full_page)
446 height = gtk_page_setup_get_paper_height (setup: context->page_setup, unit: GTK_UNIT_INCH);
447 else
448 height = gtk_page_setup_get_page_height (setup: context->page_setup, unit: GTK_UNIT_INCH);
449
450 /* Really dpi_y? What about landscape? what does dpi_y mean in that case? */
451 return height * context->surface_dpi_y / context->pixels_per_unit_y;
452}
453
454/**
455 * gtk_print_context_get_dpi_x:
456 * @context: a `GtkPrintContext`
457 *
458 * Obtains the horizontal resolution of the `GtkPrintContext`,
459 * in dots per inch.
460 *
461 * Returns: the horizontal resolution of @context
462 */
463double
464gtk_print_context_get_dpi_x (GtkPrintContext *context)
465{
466 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0);
467
468 return context->surface_dpi_x;
469}
470
471/**
472 * gtk_print_context_get_dpi_y:
473 * @context: a `GtkPrintContext`
474 *
475 * Obtains the vertical resolution of the `GtkPrintContext`,
476 * in dots per inch.
477 *
478 * Returns: the vertical resolution of @context
479 */
480double
481gtk_print_context_get_dpi_y (GtkPrintContext *context)
482{
483 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0);
484
485 return context->surface_dpi_y;
486}
487
488/**
489 * gtk_print_context_get_hard_margins:
490 * @context: a `GtkPrintContext`
491 * @top: (out): top hardware printer margin
492 * @bottom: (out): bottom hardware printer margin
493 * @left: (out): left hardware printer margin
494 * @right: (out): right hardware printer margin
495 *
496 * Obtains the hardware printer margins of the `GtkPrintContext`,
497 * in units.
498 *
499 * Returns: %TRUE if the hard margins were retrieved
500 */
501gboolean
502gtk_print_context_get_hard_margins (GtkPrintContext *context,
503 double *top,
504 double *bottom,
505 double *left,
506 double *right)
507{
508 if (context->has_hard_margins)
509 {
510 *top = context->hard_margin_top / context->pixels_per_unit_y;
511 *bottom = context->hard_margin_bottom / context->pixels_per_unit_y;
512 *left = context->hard_margin_left / context->pixels_per_unit_x;
513 *right = context->hard_margin_right / context->pixels_per_unit_x;
514 }
515
516 return context->has_hard_margins;
517}
518
519/**
520 * gtk_print_context_set_hard_margins:
521 * @context: a `GtkPrintContext`
522 * @top: top hardware printer margin
523 * @bottom: bottom hardware printer margin
524 * @left: left hardware printer margin
525 * @right: right hardware printer margin
526 *
527 * Sets the hard margins in pixels.
528 */
529void
530_gtk_print_context_set_hard_margins (GtkPrintContext *context,
531 double top,
532 double bottom,
533 double left,
534 double right)
535{
536 context->hard_margin_top = top;
537 context->hard_margin_bottom = bottom;
538 context->hard_margin_left = left;
539 context->hard_margin_right = right;
540 context->has_hard_margins = TRUE;
541}
542
543/**
544 * gtk_print_context_get_pango_fontmap:
545 * @context: a `GtkPrintContext`
546 *
547 * Returns a `PangoFontMap` that is suitable for use
548 * with the `GtkPrintContext`.
549 *
550 * Returns: (transfer none): the font map of @context
551 */
552PangoFontMap *
553gtk_print_context_get_pango_fontmap (GtkPrintContext *context)
554{
555 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
556
557 return _gtk_print_context_get_fontmap (context);
558}
559
560/**
561 * gtk_print_context_create_pango_context:
562 * @context: a `GtkPrintContext`
563 *
564 * Creates a new `PangoContext` that can be used with the
565 * `GtkPrintContext`.
566 *
567 * Returns: (transfer full): a new Pango context for @context
568 */
569PangoContext *
570gtk_print_context_create_pango_context (GtkPrintContext *context)
571{
572 PangoContext *pango_context;
573 cairo_font_options_t *options;
574
575 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
576
577 pango_context = pango_font_map_create_context (fontmap: _gtk_print_context_get_fontmap (context));
578
579 options = cairo_font_options_create ();
580 cairo_font_options_set_hint_metrics (options, hint_metrics: CAIRO_HINT_METRICS_OFF);
581 pango_cairo_context_set_font_options (context: pango_context, options);
582 cairo_font_options_destroy (options);
583
584 /* We use the unit-scaled resolution, as we still want
585 * fonts given in points to work
586 */
587 pango_cairo_context_set_resolution (context: pango_context,
588 dpi: context->surface_dpi_y / context->pixels_per_unit_y);
589 return pango_context;
590}
591
592/**
593 * gtk_print_context_create_pango_layout:
594 * @context: a `GtkPrintContext`
595 *
596 * Creates a new `PangoLayout` that is suitable for use
597 * with the `GtkPrintContext`.
598 *
599 * Returns: (transfer full): a new Pango layout for @context
600 */
601PangoLayout *
602gtk_print_context_create_pango_layout (GtkPrintContext *context)
603{
604 PangoContext *pango_context;
605 PangoLayout *layout;
606
607 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
608
609 pango_context = gtk_print_context_create_pango_context (context);
610 layout = pango_layout_new (context: pango_context);
611
612 pango_cairo_update_context (cr: context->cr, context: pango_context);
613 g_object_unref (object: pango_context);
614
615 return layout;
616}
617

source code of gtk/gtk/gtkprintcontext.c