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