]> Pileus Git - ~andy/gtk/blob - gtk/gtkprintcontext.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkprintcontext.c
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  * <example>
39  * <title>Using GtkPrintContext in a #GtkPrintOperation::draw-page callback</title>
40  * <programlisting>
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_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  * </programlisting>
93  * </example>
94  *
95  * Printing support was added in GTK+ 2.10.
96  */
97
98
99 typedef struct _GtkPrintContextClass GtkPrintContextClass;
100
101 #define GTK_IS_PRINT_CONTEXT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_PRINT_CONTEXT))
102 #define GTK_PRINT_CONTEXT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_PRINT_CONTEXT, GtkPrintContextClass))
103 #define GTK_PRINT_CONTEXT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_PRINT_CONTEXT, GtkPrintContextClass))
104
105 #define MM_PER_INCH 25.4
106 #define POINTS_PER_INCH 72
107
108 struct _GtkPrintContext
109 {
110   GObject parent_instance;
111
112   GtkPrintOperation *op;
113   cairo_t *cr;
114   GtkPageSetup *page_setup;
115
116   gdouble surface_dpi_x;
117   gdouble surface_dpi_y;
118   
119   gdouble pixels_per_unit_x;
120   gdouble pixels_per_unit_y;
121
122   gboolean has_hard_margins;
123   gdouble hard_margin_top;
124   gdouble hard_margin_bottom;
125   gdouble hard_margin_left;
126   gdouble hard_margin_right;
127
128 };
129
130 struct _GtkPrintContextClass
131 {
132   GObjectClass parent_class;
133 };
134
135 G_DEFINE_TYPE (GtkPrintContext, gtk_print_context, G_TYPE_OBJECT)
136
137 static void
138 gtk_print_context_finalize (GObject *object)
139 {
140   GtkPrintContext *context = GTK_PRINT_CONTEXT (object);
141
142   if (context->page_setup)
143     g_object_unref (context->page_setup);
144
145   if (context->cr)
146     cairo_destroy (context->cr);
147   
148   G_OBJECT_CLASS (gtk_print_context_parent_class)->finalize (object);
149 }
150
151 static void
152 gtk_print_context_init (GtkPrintContext *context)
153 {
154 }
155
156 static void
157 gtk_print_context_class_init (GtkPrintContextClass *class)
158 {
159   GObjectClass *gobject_class = (GObjectClass *)class;
160
161   gobject_class->finalize = gtk_print_context_finalize;
162 }
163
164
165 GtkPrintContext *
166 _gtk_print_context_new (GtkPrintOperation *op)
167 {
168   GtkPrintContext *context;
169
170   context = g_object_new (GTK_TYPE_PRINT_CONTEXT, NULL);
171
172   context->op = op;
173   context->cr = NULL;
174   context->has_hard_margins = FALSE;
175   
176   return context;
177 }
178
179 static PangoFontMap *
180 _gtk_print_context_get_fontmap (GtkPrintContext *context)
181 {
182   return pango_cairo_font_map_get_default ();
183 }
184
185 /**
186  * gtk_print_context_set_cairo_context:
187  * @context: a #GtkPrintContext
188  * @cr: the cairo context
189  * @dpi_x: the horizontal resolution to use with @cr
190  * @dpi_y: the vertical resolution to use with @cr
191  *
192  * Sets a new cairo context on a print context. 
193  * 
194  * This function is intended to be used when implementing
195  * an internal print preview, it is not needed for printing,
196  * since GTK+ itself creates a suitable cairo context in that
197  * case.
198  *
199  * Since: 2.10 
200  */
201 void
202 gtk_print_context_set_cairo_context (GtkPrintContext *context,
203                                      cairo_t         *cr,
204                                      double           dpi_x,
205                                      double           dpi_y)
206 {
207   if (context->cr)
208     cairo_destroy (context->cr);
209
210   context->cr = cairo_reference (cr);
211   context->surface_dpi_x = dpi_x;
212   context->surface_dpi_y = dpi_y;
213
214   switch (context->op->priv->unit)
215     {
216     default:
217     case GTK_UNIT_NONE:
218       /* Do nothing, this is the cairo default unit */
219       context->pixels_per_unit_x = 1.0;
220       context->pixels_per_unit_y = 1.0;
221       break;
222     case GTK_UNIT_POINTS:
223       context->pixels_per_unit_x = dpi_x / POINTS_PER_INCH;
224       context->pixels_per_unit_y = dpi_y / POINTS_PER_INCH;
225       break;
226     case GTK_UNIT_INCH:
227       context->pixels_per_unit_x = dpi_x;
228       context->pixels_per_unit_y = dpi_y;
229       break;
230     case GTK_UNIT_MM:
231       context->pixels_per_unit_x = dpi_x / MM_PER_INCH;
232       context->pixels_per_unit_y = dpi_y / MM_PER_INCH;
233       break;
234     }
235   cairo_scale (context->cr,
236                context->pixels_per_unit_x,
237                context->pixels_per_unit_y);
238 }
239
240
241 void
242 _gtk_print_context_rotate_according_to_orientation (GtkPrintContext *context)
243 {
244   cairo_t *cr = context->cr;
245   cairo_matrix_t matrix;
246   GtkPaperSize *paper_size;
247   gdouble width, height;
248
249   paper_size = gtk_page_setup_get_paper_size (context->page_setup);
250
251   width = gtk_paper_size_get_width (paper_size, GTK_UNIT_INCH);
252   width = width * context->surface_dpi_x / context->pixels_per_unit_x;
253   height = gtk_paper_size_get_height (paper_size, GTK_UNIT_INCH);
254   height = height * context->surface_dpi_y / context->pixels_per_unit_y;
255   
256   switch (gtk_page_setup_get_orientation (context->page_setup))
257     {
258     default:
259     case GTK_PAGE_ORIENTATION_PORTRAIT:
260       break;
261     case GTK_PAGE_ORIENTATION_LANDSCAPE:
262       cairo_translate (cr, 0, height);
263       cairo_matrix_init (&matrix,
264                          0, -1,
265                          1,  0,
266                          0,  0);
267       cairo_transform (cr, &matrix);
268       break;
269     case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
270       cairo_translate (cr, width, height);
271       cairo_matrix_init (&matrix,
272                          -1,  0,
273                           0, -1,
274                           0,  0);
275       cairo_transform (cr, &matrix);
276       break;
277     case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
278       cairo_translate (cr, width, 0);
279       cairo_matrix_init (&matrix,
280                           0,  1,
281                          -1,  0,
282                           0,  0);
283       cairo_transform (cr, &matrix);
284       break;
285     }
286 }
287
288 void
289 _gtk_print_context_reverse_according_to_orientation (GtkPrintContext *context)
290 {
291   cairo_t *cr = context->cr;
292   cairo_matrix_t matrix;
293   gdouble width, height;
294
295   width = gtk_page_setup_get_paper_width (context->page_setup, GTK_UNIT_INCH);
296   width = width * context->surface_dpi_x / context->pixels_per_unit_x;
297   height = gtk_page_setup_get_paper_height (context->page_setup, GTK_UNIT_INCH);
298   height = height * context->surface_dpi_y / context->pixels_per_unit_y;
299
300   switch (gtk_page_setup_get_orientation (context->page_setup))
301     {
302     default:
303     case GTK_PAGE_ORIENTATION_PORTRAIT:
304     case GTK_PAGE_ORIENTATION_LANDSCAPE:
305       break;
306     case GTK_PAGE_ORIENTATION_REVERSE_PORTRAIT:
307     case GTK_PAGE_ORIENTATION_REVERSE_LANDSCAPE:
308       cairo_translate (cr, width, height);
309       cairo_matrix_init (&matrix,
310                          -1,  0,
311                           0, -1,
312                           0,  0);
313       cairo_transform (cr, &matrix);
314       break;
315     }
316 }
317
318 void
319 _gtk_print_context_translate_into_margin (GtkPrintContext *context)
320 {
321   gdouble left, top;
322
323   g_return_if_fail (GTK_IS_PRINT_CONTEXT (context));
324
325   /* We do it this way to also handle GTK_UNIT_NONE */
326   left = gtk_page_setup_get_left_margin (context->page_setup, GTK_UNIT_INCH);
327   top = gtk_page_setup_get_top_margin (context->page_setup, GTK_UNIT_INCH);
328
329   cairo_translate (context->cr,
330                    left * context->surface_dpi_x / context->pixels_per_unit_x,
331                    top * context->surface_dpi_y / context->pixels_per_unit_y);
332 }
333
334 void
335 _gtk_print_context_set_page_setup (GtkPrintContext *context,
336                                    GtkPageSetup    *page_setup)
337 {
338   g_return_if_fail (GTK_IS_PRINT_CONTEXT (context));
339   g_return_if_fail (page_setup == NULL ||
340                     GTK_IS_PAGE_SETUP (page_setup));
341   
342   g_object_ref (page_setup);
343
344   if (context->page_setup != NULL)
345     g_object_unref (context->page_setup);
346
347   context->page_setup = page_setup;
348 }
349
350 /**
351  * gtk_print_context_get_cairo_context:
352  * @context: a #GtkPrintContext
353  *
354  * Obtains the cairo context that is associated with the
355  * #GtkPrintContext.
356  *
357  * Return value: (transfer none): the cairo context of @context
358  *
359  * Since: 2.10
360  */
361 cairo_t *
362 gtk_print_context_get_cairo_context (GtkPrintContext *context)
363 {
364   g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
365
366   return context->cr;
367 }
368
369 /**
370  * gtk_print_context_get_page_setup:
371  * @context: a #GtkPrintContext
372  *
373  * Obtains the #GtkPageSetup that determines the page
374  * dimensions of the #GtkPrintContext.
375  *
376  * Return value: (transfer none): the page setup of @context
377  *
378  * Since: 2.10
379  */
380 GtkPageSetup *
381 gtk_print_context_get_page_setup (GtkPrintContext *context)
382 {
383   g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
384
385   return context->page_setup;
386 }
387
388 /**
389  * gtk_print_context_get_width:
390  * @context: a #GtkPrintContext
391  *
392  * Obtains the width of the #GtkPrintContext, in pixels.
393  *
394  * Return value: the width of @context
395  *
396  * Since: 2.10 
397  */
398 gdouble
399 gtk_print_context_get_width (GtkPrintContext *context)
400 {
401   GtkPrintOperationPrivate *priv;
402   gdouble width;
403
404   g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0);
405
406   priv = context->op->priv;
407
408   if (priv->use_full_page)
409     width = gtk_page_setup_get_paper_width (context->page_setup, GTK_UNIT_INCH);
410   else
411     width = gtk_page_setup_get_page_width (context->page_setup, GTK_UNIT_INCH);
412
413   /* Really dpi_x? What about landscape? what does dpi_x mean in that case? */
414   return width * context->surface_dpi_x / context->pixels_per_unit_x;
415 }
416
417 /**
418  * gtk_print_context_get_height:
419  * @context: a #GtkPrintContext
420  * 
421  * Obtains the height of the #GtkPrintContext, in pixels.
422  *
423  * Return value: the height of @context
424  *
425  * Since: 2.10
426  */
427 gdouble
428 gtk_print_context_get_height (GtkPrintContext *context)
429 {
430   GtkPrintOperationPrivate *priv;
431   gdouble height;
432
433   g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0);
434
435   priv = context->op->priv;
436
437   if (priv->use_full_page)
438     height = gtk_page_setup_get_paper_height (context->page_setup, GTK_UNIT_INCH);
439   else
440     height = gtk_page_setup_get_page_height (context->page_setup, GTK_UNIT_INCH);
441
442   /* Really dpi_y? What about landscape? what does dpi_y mean in that case? */
443   return height * context->surface_dpi_y / context->pixels_per_unit_y;
444 }
445
446 /**
447  * gtk_print_context_get_dpi_x:
448  * @context: a #GtkPrintContext
449  * 
450  * Obtains the horizontal resolution of the #GtkPrintContext,
451  * in dots per inch.
452  *
453  * Return value: the horizontal resolution of @context
454  *
455  * Since: 2.10
456  */
457 gdouble
458 gtk_print_context_get_dpi_x (GtkPrintContext *context)
459 {
460   g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0);
461
462   return context->surface_dpi_x;
463 }
464
465 /**
466  * gtk_print_context_get_dpi_y:
467  * @context: a #GtkPrintContext
468  * 
469  * Obtains the vertical resolution of the #GtkPrintContext,
470  * in dots per inch.
471  *
472  * Return value: the vertical resolution of @context
473  *
474  * Since: 2.10
475  */
476 gdouble
477 gtk_print_context_get_dpi_y (GtkPrintContext *context)
478 {
479   g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), 0);
480
481   return context->surface_dpi_y;
482 }
483
484 /**
485  * gtk_print_context_get_hard_margins:
486  * @context: a #GtkPrintContext
487  * @top: (out): top hardware printer margin
488  * @bottom: (out): bottom hardware printer margin
489  * @left: (out): left hardware printer margin
490  * @right: (out): right hardware printer margin
491  *
492  * Obtains the hardware printer margins of the #GtkPrintContext, in units.
493  *
494  * Return value: %TRUE if the hard margins were retrieved
495  *
496  * Since: 2.20
497  */
498 gboolean
499 gtk_print_context_get_hard_margins (GtkPrintContext *context,
500                                     gdouble         *top,
501                                     gdouble         *bottom,
502                                     gdouble         *left,
503                                     gdouble         *right)
504 {
505   if (context->has_hard_margins)
506     {
507       *top    = context->hard_margin_top / context->pixels_per_unit_y;
508       *bottom = context->hard_margin_bottom / context->pixels_per_unit_y;
509       *left   = context->hard_margin_left / context->pixels_per_unit_x;
510       *right  = context->hard_margin_right / context->pixels_per_unit_x;
511     }
512
513   return context->has_hard_margins;
514 }
515
516 /**
517  * gtk_print_context_set_hard_margins:
518  * @context: a #GtkPrintContext
519  * @top: top hardware printer margin
520  * @bottom: bottom hardware printer margin
521  * @left: left hardware printer margin
522  * @right: right hardware printer margin
523  *
524  * set the hard margins in pixel coordinates
525  */
526 void
527 _gtk_print_context_set_hard_margins (GtkPrintContext *context,
528                                      gdouble          top,
529                                      gdouble          bottom,
530                                      gdouble          left,
531                                      gdouble          right)
532 {
533   context->hard_margin_top    = top;
534   context->hard_margin_bottom = bottom;
535   context->hard_margin_left   = left;
536   context->hard_margin_right  = right;
537   context->has_hard_margins   = TRUE;
538 }
539
540 /**
541  * gtk_print_context_get_pango_fontmap:
542  * @context: a #GtkPrintContext
543  *
544  * Returns a #PangoFontMap that is suitable for use
545  * with the #GtkPrintContext.
546  *
547  * Return value: (transfer none): the font map of @context
548  *
549  * Since: 2.10
550  */
551 PangoFontMap *
552 gtk_print_context_get_pango_fontmap (GtkPrintContext *context)
553 {
554   g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
555
556   return _gtk_print_context_get_fontmap (context);
557 }
558
559 /**
560  * gtk_print_context_create_pango_context:
561  * @context: a #GtkPrintContext
562  *
563  * Creates a new #PangoContext that can be used with the
564  * #GtkPrintContext.
565  *
566  * Return value: (transfer full): a new Pango context for @context
567  * 
568  * Since: 2.10
569  */
570 PangoContext *
571 gtk_print_context_create_pango_context (GtkPrintContext *context)
572 {
573   PangoContext *pango_context;
574   cairo_font_options_t *options;
575
576   g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
577   
578   pango_context = pango_font_map_create_context (_gtk_print_context_get_fontmap (context));
579
580   options = cairo_font_options_create ();
581   cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF);
582   pango_cairo_context_set_font_options (pango_context, options);
583   cairo_font_options_destroy (options);
584   
585   /* We use the unit-scaled resolution, as we still want 
586    * fonts given in points to work 
587    */
588   pango_cairo_context_set_resolution (pango_context,
589                                       context->surface_dpi_y / context->pixels_per_unit_y);
590   return pango_context;
591 }
592
593 /**
594  * gtk_print_context_create_pango_layout:
595  * @context: a #GtkPrintContext
596  *
597  * Creates a new #PangoLayout that is suitable for use
598  * with the #GtkPrintContext.
599  * 
600  * Return value: (transfer full): a new Pango layout for @context
601  *
602  * Since: 2.10
603  */
604 PangoLayout *
605 gtk_print_context_create_pango_layout (GtkPrintContext *context)
606 {
607   PangoContext *pango_context;
608   PangoLayout *layout;
609
610   g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), NULL);
611
612   pango_context = gtk_print_context_create_pango_context (context);
613   layout = pango_layout_new (pango_context);
614
615   pango_cairo_update_context (context->cr, pango_context);
616   g_object_unref (pango_context);
617
618   return layout;
619 }