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 * SECTION:gtkprintcontext
25 * @Short_description: Encapsulates context for drawing pages
26 * @Title: GtkPrintContext
27 *
28 * A GtkPrintContext encapsulates context information that is required when
29 * drawing pages for printing, such as the cairo context and important
30 * parameters like page size and resolution. It also lets you easily
31 * create #PangoLayout and #PangoContext objects that match the font metrics
32 * of the cairo surface.
33 *
34 * GtkPrintContext objects gets passed to the #GtkPrintOperation::begin-print,
35 * #GtkPrintOperation::end-print, #GtkPrintOperation::request-page-setup and
36 * #GtkPrintOperation::draw-page signals on the #GtkPrintOperation.
37 *
38 * ## Using GtkPrintContext in a #GtkPrintOperation::draw-page callback
39 *
40 * |[<!-- language="C" -->
41 * static void
42 * draw_page (GtkPrintOperation *operation,
43 * GtkPrintContext *context,
44 * int page_nr)
45 * {
46 * cairo_t *cr;
47 * PangoLayout *layout;
48 * PangoFontDescription *desc;
49 *
50 * cr = gtk_print_context_get_cairo_context (context);
51 *
52 * // Draw a red rectangle, as wide as the paper (inside the margins)
53 * cairo_set_source_rgb (cr, 1.0, 0, 0);
54 * cairo_rectangle (cr, 0, 0, gtk_print_context_get_width (context), 50);
55 *
56 * cairo_fill (cr);
57 *
58 * // Draw some lines
59 * cairo_move_to (cr, 20, 10);
60 * cairo_line_to (cr, 40, 20);
61 * cairo_arc (cr, 60, 60, 20, 0, M_PI);
62 * cairo_line_to (cr, 80, 20);
63 *
64 * cairo_set_source_rgb (cr, 0, 0, 0);
65 * cairo_set_line_width (cr, 5);
66 * cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
67 * cairo_set_line_join (cr, CAIRO_LINE_JOIN_ROUND);
68 *
69 * cairo_stroke (cr);
70 *
71 * // Draw some text
72 * layout = gtk_print_context_create_pango_layout (context);
73 * pango_layout_set_text (layout, "Hello World! Printing is easy", -1);
74 * desc = pango_font_description_from_string ("sans 28");
75 * pango_layout_set_font_description (layout, desc);
76 * pango_font_description_free (desc);
77 *
78 * cairo_move_to (cr, 30, 20);
79 * pango_cairo_layout_path (cr, layout);
80 *
81 * // Font Outline
82 * cairo_set_source_rgb (cr, 0.93, 1.0, 0.47);
83 * cairo_set_line_width (cr, 0.5);
84 * cairo_stroke_preserve (cr);
85 *
86 * // Font Fill
87 * cairo_set_source_rgb (cr, 0, 0.0, 1.0);
88 * cairo_fill (cr);
89 *
90 * g_object_unref (layout);
91 * }
92 * ]|
93 *
94 * Printing support was added in GTK+ 2.10.
95 */
96
97
98 typedef 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
107 struct _GtkPrintContext
108 {
109 GObject parent_instance;
110
111 GtkPrintOperation *op;
112 cairo_t *cr;
113 GtkPageSetup *page_setup;
114
115 gdouble surface_dpi_x;
116 gdouble surface_dpi_y;
117
118 gdouble pixels_per_unit_x;
119 gdouble pixels_per_unit_y;
120
121 gboolean has_hard_margins;
122 gdouble hard_margin_top;
123 gdouble hard_margin_bottom;
124 gdouble hard_margin_left;
125 gdouble hard_margin_right;
126
127 };
128
129 struct _GtkPrintContextClass
130 {
131 GObjectClass parent_class;
132 };
133
G_DEFINE_TYPE(GtkPrintContext,gtk_print_context,G_TYPE_OBJECT)134 G_DEFINE_TYPE (GtkPrintContext, gtk_print_context, G_TYPE_OBJECT)
135
136 static void
137 gtk_print_context_finalize (GObject *object)
138 {
139 GtkPrintContext *context = GTK_PRINT_CONTEXT (object);
140
141 if (context->page_setup)
142 g_object_unref (context->page_setup);
143
144 if (context->cr)
145 cairo_destroy (context->cr);
146
147 G_OBJECT_CLASS (gtk_print_context_parent_class)->finalize (object);
148 }
149
150 static void
gtk_print_context_init(GtkPrintContext * context)151 gtk_print_context_init (GtkPrintContext *context)
152 {
153 }
154
155 static void
gtk_print_context_class_init(GtkPrintContextClass * class)156 gtk_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
164 GtkPrintContext *
_gtk_print_context_new(GtkPrintOperation * op)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
178 static PangoFontMap *
_gtk_print_context_get_fontmap(GtkPrintContext * context)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 *
198 * Since: 2.10
199 */
200 void
gtk_print_context_set_cairo_context(GtkPrintContext * context,cairo_t * cr,double dpi_x,double dpi_y)201 gtk_print_context_set_cairo_context (GtkPrintContext *context,
202 cairo_t *cr,
203 double dpi_x,
204 double dpi_y)
205 {
206 if (context->cr)
207 cairo_destroy (context->cr);
208
209 context->cr = cairo_reference (cr);
210 context->surface_dpi_x = dpi_x;
211 context->surface_dpi_y = dpi_y;
212
213 switch (context->op->priv->unit)
214 {
215 default:
216 case GTK_UNIT_NONE:
217 /* Do nothing, this is the cairo default unit */
218 context->pixels_per_unit_x = 1.0;
219 context->pixels_per_unit_y = 1.0;
220 break;
221 case GTK_UNIT_POINTS:
222 context->pixels_per_unit_x = dpi_x / POINTS_PER_INCH;
223 context->pixels_per_unit_y = dpi_y / POINTS_PER_INCH;
224 break;
225 case GTK_UNIT_INCH:
226 context->pixels_per_unit_x = dpi_x;
227 context->pixels_per_unit_y = dpi_y;
228 break;
229 case GTK_UNIT_MM:
230 context->pixels_per_unit_x = dpi_x / MM_PER_INCH;
231 context->pixels_per_unit_y = dpi_y / MM_PER_INCH;
232 break;
233 }
234 cairo_scale (context->cr,
235 context->pixels_per_unit_x,
236 context->pixels_per_unit_y);
237 }
238
239
240 void
_gtk_print_context_rotate_according_to_orientation(GtkPrintContext * context)241 _gtk_print_context_rotate_according_to_orientation (GtkPrintContext *context)
242 {
243 cairo_t *cr = context->cr;
244 cairo_matrix_t matrix;
245 GtkPaperSize *paper_size;
246 gdouble width, height;
247
248 paper_size = gtk_page_setup_get_paper_size (context->page_setup);
249
250 width = gtk_paper_size_get_width (paper_size, GTK_UNIT_INCH);
251 width = width * context->surface_dpi_x / context->pixels_per_unit_x;
252 height = gtk_paper_size_get_height (paper_size, GTK_UNIT_INCH);
253 height = height * context->surface_dpi_y / context->pixels_per_unit_y;
254
255 switch (gtk_page_setup_get_orientation (context->page_setup))
256 {
257 default:
258 case GTK_PAGE_ORIENTATION_PORTRAIT:
259 break;
260 case GTK_PAGE_ORIENTATION_LANDSCAPE:
261 cairo_translate (cr, 0, height);
262 cairo_matrix_init (&matrix,
263 0, -1,
264 1, 0,
265 0, 0);
266 cairo_transform (cr, &matrix);
267 break;
268 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
269 cairo_translate (cr, width, height);
270 cairo_matrix_init (&matrix,
271 -1, 0,
272 0, -1,
273 0, 0);
274 cairo_transform (cr, &matrix);
275 break;
276 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
277 cairo_translate (cr, width, 0);
278 cairo_matrix_init (&matrix,
279 0, 1,
280 -1, 0,
281 0, 0);
282 cairo_transform (cr, &matrix);
283 break;
284 }
285 }
286
287 void
_gtk_print_context_reverse_according_to_orientation(GtkPrintContext * context)288 _gtk_print_context_reverse_according_to_orientation (GtkPrintContext *context)
289 {
290 cairo_t *cr = context->cr;
291 cairo_matrix_t matrix;
292 gdouble width, height;
293
294 width = gtk_page_setup_get_paper_width (context->page_setup, GTK_UNIT_INCH);
295 width = width * context->surface_dpi_x / context->pixels_per_unit_x;
296 height = gtk_page_setup_get_paper_height (context->page_setup, GTK_UNIT_INCH);
297 height = height * context->surface_dpi_y / context->pixels_per_unit_y;
298
299 switch (gtk_page_setup_get_orientation (context->page_setup))
300 {
301 default:
302 case GTK_PAGE_ORIENTATION_PORTRAIT:
303 case GTK_PAGE_ORIENTATION_LANDSCAPE:
304 break;
305 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
306 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
307 cairo_translate (cr, width, height);
308 cairo_matrix_init (&matrix,
309 -1, 0,
310 0, -1,
311 0, 0);
312 cairo_transform (cr, &matrix);
313 break;
314 }
315 }
316
317 void
_gtk_print_context_translate_into_margin(GtkPrintContext * context)318 _gtk_print_context_translate_into_margin (GtkPrintContext *context)
319 {
320 gdouble dx, dy;
321
322 g_return_if_fail (GTK_IS_PRINT_CONTEXT (context));
323
324 /* We do it this way to also handle GTK_UNIT_NONE */
325 switch (gtk_page_setup_get_orientation (context->page_setup))
326 {
327 default:
328 case GTK_PAGE_ORIENTATION_PORTRAIT:
329 dx = gtk_page_setup_get_left_margin (context->page_setup, GTK_UNIT_INCH);
330 dy = gtk_page_setup_get_top_margin (context->page_setup, GTK_UNIT_INCH);
331 break;
332 case GTK_PAGE_ORIENTATION_LANDSCAPE:
333 dx = gtk_page_setup_get_bottom_margin (context->page_setup, GTK_UNIT_INCH);
334 dy = gtk_page_setup_get_left_margin (context->page_setup, GTK_UNIT_INCH);
335 break;
336 case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
337 dx = gtk_page_setup_get_right_margin (context->page_setup, GTK_UNIT_INCH);
338 dy = gtk_page_setup_get_bottom_margin (context->page_setup, GTK_UNIT_INCH);
339 break;
340 case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
341 dx = gtk_page_setup_get_top_margin (context->page_setup, GTK_UNIT_INCH);
342 dy = gtk_page_setup_get_right_margin (context->page_setup, GTK_UNIT_INCH);
343 break;
344 }
345
346 cairo_translate (context->cr,
347 dx * context->surface_dpi_x / context->pixels_per_unit_x,
348 dy * context->surface_dpi_y / context->pixels_per_unit_y);
349 }
350
351 void
_gtk_print_context_set_page_setup(GtkPrintContext * context,GtkPageSetup * page_setup)352 _gtk_print_context_set_page_setup (GtkPrintContext *context,
353 GtkPageSetup *page_setup)
354 {
355 g_return_if_fail (GTK_IS_PRINT_CONTEXT (context));
356 g_return_if_fail (page_setup == NULL ||
357 GTK_IS_PAGE_SETUP (page_setup));
358
359 if (page_setup != NULL)
360 g_object_ref (page_setup);
361
362 if (context->page_setup != NULL)
363 g_object_unref (context->page_setup);
364
365 context->page_setup = page_setup;
366 }
367
368 /**
369 * gtk_print_context_get_cairo_context:
370 * @context: a #GtkPrintContext
371 *
372 * Obtains the cairo context that is associated with the
373 * #GtkPrintContext.
374 *
375 * Returns: (transfer none): the cairo context of @context
376 *
377 * Since: 2.10
378 */
379 cairo_t *
gtk_print_context_get_cairo_context(GtkPrintContext * context)380 gtk_print_context_get_cairo_context (GtkPrintContext *context)
381 {
382 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
383
384 return context->cr;
385 }
386
387 /**
388 * gtk_print_context_get_page_setup:
389 * @context: a #GtkPrintContext
390 *
391 * Obtains the #GtkPageSetup that determines the page
392 * dimensions of the #GtkPrintContext.
393 *
394 * Returns: (transfer none): the page setup of @context
395 *
396 * Since: 2.10
397 */
398 GtkPageSetup *
gtk_print_context_get_page_setup(GtkPrintContext * context)399 gtk_print_context_get_page_setup (GtkPrintContext *context)
400 {
401 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
402
403 return context->page_setup;
404 }
405
406 /**
407 * gtk_print_context_get_width:
408 * @context: a #GtkPrintContext
409 *
410 * Obtains the width of the #GtkPrintContext, in pixels.
411 *
412 * Returns: the width of @context
413 *
414 * Since: 2.10
415 */
416 gdouble
gtk_print_context_get_width(GtkPrintContext * context)417 gtk_print_context_get_width (GtkPrintContext *context)
418 {
419 GtkPrintOperationPrivate *priv;
420 gdouble width;
421
422 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0);
423
424 priv = context->op->priv;
425
426 if (priv->use_full_page)
427 width = gtk_page_setup_get_paper_width (context->page_setup, GTK_UNIT_INCH);
428 else
429 width = gtk_page_setup_get_page_width (context->page_setup, GTK_UNIT_INCH);
430
431 /* Really dpi_x? What about landscape? what does dpi_x mean in that case? */
432 return width * context->surface_dpi_x / context->pixels_per_unit_x;
433 }
434
435 /**
436 * gtk_print_context_get_height:
437 * @context: a #GtkPrintContext
438 *
439 * Obtains the height of the #GtkPrintContext, in pixels.
440 *
441 * Returns: the height of @context
442 *
443 * Since: 2.10
444 */
445 gdouble
gtk_print_context_get_height(GtkPrintContext * context)446 gtk_print_context_get_height (GtkPrintContext *context)
447 {
448 GtkPrintOperationPrivate *priv;
449 gdouble height;
450
451 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0);
452
453 priv = context->op->priv;
454
455 if (priv->use_full_page)
456 height = gtk_page_setup_get_paper_height (context->page_setup, GTK_UNIT_INCH);
457 else
458 height = gtk_page_setup_get_page_height (context->page_setup, GTK_UNIT_INCH);
459
460 /* Really dpi_y? What about landscape? what does dpi_y mean in that case? */
461 return height * context->surface_dpi_y / context->pixels_per_unit_y;
462 }
463
464 /**
465 * gtk_print_context_get_dpi_x:
466 * @context: a #GtkPrintContext
467 *
468 * Obtains the horizontal resolution of the #GtkPrintContext,
469 * in dots per inch.
470 *
471 * Returns: the horizontal resolution of @context
472 *
473 * Since: 2.10
474 */
475 gdouble
gtk_print_context_get_dpi_x(GtkPrintContext * context)476 gtk_print_context_get_dpi_x (GtkPrintContext *context)
477 {
478 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0);
479
480 return context->surface_dpi_x;
481 }
482
483 /**
484 * gtk_print_context_get_dpi_y:
485 * @context: a #GtkPrintContext
486 *
487 * Obtains the vertical resolution of the #GtkPrintContext,
488 * in dots per inch.
489 *
490 * Returns: the vertical resolution of @context
491 *
492 * Since: 2.10
493 */
494 gdouble
gtk_print_context_get_dpi_y(GtkPrintContext * context)495 gtk_print_context_get_dpi_y (GtkPrintContext *context)
496 {
497 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0);
498
499 return context->surface_dpi_y;
500 }
501
502 /**
503 * gtk_print_context_get_hard_margins:
504 * @context: a #GtkPrintContext
505 * @top: (out): top hardware printer margin
506 * @bottom: (out): bottom hardware printer margin
507 * @left: (out): left hardware printer margin
508 * @right: (out): right hardware printer margin
509 *
510 * Obtains the hardware printer margins of the #GtkPrintContext, in units.
511 *
512 * Returns: %TRUE if the hard margins were retrieved
513 *
514 * Since: 2.20
515 */
516 gboolean
gtk_print_context_get_hard_margins(GtkPrintContext * context,gdouble * top,gdouble * bottom,gdouble * left,gdouble * right)517 gtk_print_context_get_hard_margins (GtkPrintContext *context,
518 gdouble *top,
519 gdouble *bottom,
520 gdouble *left,
521 gdouble *right)
522 {
523 if (context->has_hard_margins)
524 {
525 *top = context->hard_margin_top / context->pixels_per_unit_y;
526 *bottom = context->hard_margin_bottom / context->pixels_per_unit_y;
527 *left = context->hard_margin_left / context->pixels_per_unit_x;
528 *right = context->hard_margin_right / context->pixels_per_unit_x;
529 }
530
531 return context->has_hard_margins;
532 }
533
534 /**
535 * gtk_print_context_set_hard_margins:
536 * @context: a #GtkPrintContext
537 * @top: top hardware printer margin
538 * @bottom: bottom hardware printer margin
539 * @left: left hardware printer margin
540 * @right: right hardware printer margin
541 *
542 * set the hard margins in pixel coordinates
543 */
544 void
_gtk_print_context_set_hard_margins(GtkPrintContext * context,gdouble top,gdouble bottom,gdouble left,gdouble right)545 _gtk_print_context_set_hard_margins (GtkPrintContext *context,
546 gdouble top,
547 gdouble bottom,
548 gdouble left,
549 gdouble right)
550 {
551 context->hard_margin_top = top;
552 context->hard_margin_bottom = bottom;
553 context->hard_margin_left = left;
554 context->hard_margin_right = right;
555 context->has_hard_margins = TRUE;
556 }
557
558 /**
559 * gtk_print_context_get_pango_fontmap:
560 * @context: a #GtkPrintContext
561 *
562 * Returns a #PangoFontMap that is suitable for use
563 * with the #GtkPrintContext.
564 *
565 * Returns: (transfer none): the font map of @context
566 *
567 * Since: 2.10
568 */
569 PangoFontMap *
gtk_print_context_get_pango_fontmap(GtkPrintContext * context)570 gtk_print_context_get_pango_fontmap (GtkPrintContext *context)
571 {
572 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
573
574 return _gtk_print_context_get_fontmap (context);
575 }
576
577 /**
578 * gtk_print_context_create_pango_context:
579 * @context: a #GtkPrintContext
580 *
581 * Creates a new #PangoContext that can be used with the
582 * #GtkPrintContext.
583 *
584 * Returns: (transfer full): a new Pango context for @context
585 *
586 * Since: 2.10
587 */
588 PangoContext *
gtk_print_context_create_pango_context(GtkPrintContext * context)589 gtk_print_context_create_pango_context (GtkPrintContext *context)
590 {
591 PangoContext *pango_context;
592 cairo_font_options_t *options;
593
594 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
595
596 pango_context = pango_font_map_create_context (_gtk_print_context_get_fontmap (context));
597
598 options = cairo_font_options_create ();
599 cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
600 pango_cairo_context_set_font_options (pango_context, options);
601 cairo_font_options_destroy (options);
602
603 /* We use the unit-scaled resolution, as we still want
604 * fonts given in points to work
605 */
606 pango_cairo_context_set_resolution (pango_context,
607 context->surface_dpi_y / context->pixels_per_unit_y);
608 return pango_context;
609 }
610
611 /**
612 * gtk_print_context_create_pango_layout:
613 * @context: a #GtkPrintContext
614 *
615 * Creates a new #PangoLayout that is suitable for use
616 * with the #GtkPrintContext.
617 *
618 * Returns: (transfer full): a new Pango layout for @context
619 *
620 * Since: 2.10
621 */
622 PangoLayout *
gtk_print_context_create_pango_layout(GtkPrintContext * context)623 gtk_print_context_create_pango_layout (GtkPrintContext *context)
624 {
625 PangoContext *pango_context;
626 PangoLayout *layout;
627
628 g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
629
630 pango_context = gtk_print_context_create_pango_context (context);
631 layout = pango_layout_new (pango_context);
632
633 pango_cairo_update_context (context->cr, pango_context);
634 g_object_unref (pango_context);
635
636 return layout;
637 }
638