1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; coding: utf-8 -*- *
2  *
3  * This file is part of GtkSourceView
4  *
5  * Copyright (C) 2000, 2001 Chema Celorio
6  * Copyright (C) 2003  Gustavo Giráldez
7  * Copyright (C) 2004  Red Hat, Inc.
8  * Copyright (C) 2001-2007  Paolo Maggi
9  * Copyright (C) 2008  Paolo Maggi, Paolo Borelli and Yevgen Muntyan
10  *
11  * GtkSourceView is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * GtkSourceView is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this library; if not, see <http://www.gnu.org/licenses/>.
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 #include <string.h>
30 #include <time.h>
31 
32 #include "gtksourceprintcompositor.h"
33 #include "gtksourceview.h"
34 #include "gtksourcebuffer.h"
35 #include "gtksourcebuffer-private.h"
36 
37 /**
38  * SECTION:printcompositor
39  * @Short_description: Compose a GtkSourceBuffer for printing
40  * @Title: GtkSourcePrintCompositor
41  *
42  * The #GtkSourcePrintCompositor object is used to compose a #GtkSourceBuffer
43  * for printing. You can set various configuration options to customize the
44  * printed output. #GtkSourcePrintCompositor is designed to be used with the
45  * high-level printing API of gtk+, i.e. #GtkPrintOperation.
46  *
47  * The margins specified in this object are the layout margins: they define the
48  * blank space bordering the printed area of the pages. They must not be
49  * confused with the "print margins", i.e. the parts of the page that the
50  * printer cannot print on, defined in the #GtkPageSetup objects. If the
51  * specified layout margins are smaller than the "print margins", the latter
52  * ones are used as a fallback by the #GtkSourcePrintCompositor object, so that
53  * the printed area is not clipped.
54  */
55 
56 /*
57 #define ENABLE_DEBUG
58 #define ENABLE_PROFILE
59 */
60 
61 #undef ENABLE_DEBUG
62 #undef ENABLE_PROFILE
63 
64 #ifdef ENABLE_DEBUG
65 #define DEBUG(x) (x)
66 #else
67 #define DEBUG(x)
68 #endif
69 
70 #ifdef ENABLE_PROFILE
71 #define PROFILE(x) (x)
72 static GTimer *pagination_timer = NULL;
73 #else
74 #define PROFILE(x)
75 #endif
76 
77 #define DEFAULT_TAB_WIDTH 		8
78 #define MAX_TAB_WIDTH			32
79 
80 #define DEFAULT_FONT_NAME   "Monospace 10"
81 
82 /* 5 mm */
83 #define NUMBERS_TEXT_SEPARATION convert_from_mm (5, GTK_UNIT_POINTS)
84 
85 #define HEADER_FOOTER_SIZE_FACTOR 2.2
86 #define SEPARATOR_SPACING_FACTOR  0.4
87 #define SEPARATOR_LINE_WIDTH      0.7
88 
89 /* Number of pages paginated on each invocation of the paginate() method. */
90 #define PAGINATION_CHUNK_SIZE 3
91 
92 typedef enum _PaginatorState
93 {
94 	/* Initial state: properties can be changed only when the paginator
95 	   is in the INIT state */
96 	INIT,
97 
98 	/* Paginating state: paginator goes in this state when the paginate
99 	   function is called for the first time */
100 	PAGINATING,
101 
102 	/* Done state: paginator goes in this state when the entire document
103 	   has been paginated */
104 	DONE
105 } PaginatorState;
106 
107 struct _GtkSourcePrintCompositorPrivate
108 {
109 	GtkSourceBuffer         *buffer;
110 
111 	/* Properties */
112 	guint			 tab_width;
113 	GtkWrapMode		 wrap_mode;
114 	gboolean                 highlight_syntax;
115 	guint                    print_line_numbers;
116 
117 	PangoFontDescription    *body_font;
118 	PangoFontDescription    *line_numbers_font;
119 	PangoFontDescription    *header_font;
120 	PangoFontDescription    *footer_font;
121 
122 	/* Paper size, stored in points */
123 	gdouble                  paper_width;
124 	gdouble                  paper_height;
125 
126 	/* These are stored in mm */
127 	gdouble                  margin_top;
128 	gdouble                  margin_bottom;
129 	gdouble                  margin_left;
130 	gdouble                  margin_right;
131 
132 	gboolean                 print_header;
133 	gboolean                 print_footer;
134 
135 	gchar                   *header_format_left;
136 	gchar                   *header_format_center;
137 	gchar                   *header_format_right;
138 	gboolean                 header_separator;
139 	gchar                   *footer_format_left;
140 	gchar                   *footer_format_center;
141 	gchar                   *footer_format_right;
142 	gboolean                 footer_separator;
143 
144 	/* State */
145 	PaginatorState           state;
146 
147 	GArray                  *pages; /* pages[i] contains the begin offset
148 	                                   of i-th  */
149 
150 	guint                    paginated_lines;
151 	gint                     n_pages;
152 	gint                     current_page;
153 
154 	/* Stored in points */
155 	gdouble                  header_height;
156 	gdouble                  footer_height;
157 	gdouble                  line_numbers_width;
158 	gdouble                  line_numbers_height;
159 
160 	gdouble                  footer_font_descent;
161 
162 	/* layout objects */
163 	PangoLayout             *layout;
164 	PangoLayout             *line_numbers_layout;
165 	PangoLayout             *header_layout;
166 	PangoLayout             *footer_layout;
167 
168 	gdouble                  real_margin_top;
169 	gdouble                  real_margin_bottom;
170 	gdouble                  real_margin_left;
171 	gdouble                  real_margin_right;
172 
173 	gdouble                  page_margin_top;
174 	gdouble                  page_margin_left;
175 
176 	PangoLanguage           *language; /* must not be freed */
177 
178 	GtkTextMark             *pagination_mark;
179 };
180 
181 enum
182 {
183 	PROP_0,
184 	PROP_BUFFER,
185 	PROP_TAB_WIDTH,
186 	PROP_WRAP_MODE,
187 	PROP_HIGHLIGHT_SYNTAX,
188 	PROP_PRINT_LINE_NUMBERS,
189 	PROP_PRINT_HEADER,
190 	PROP_PRINT_FOOTER,
191 	PROP_BODY_FONT_NAME,
192 	PROP_LINE_NUMBERS_FONT_NAME,
193 	PROP_HEADER_FONT_NAME,
194 	PROP_FOOTER_FONT_NAME,
195 	PROP_N_PAGES
196 };
197 
G_DEFINE_TYPE_WITH_PRIVATE(GtkSourcePrintCompositor,gtk_source_print_compositor,G_TYPE_OBJECT)198 G_DEFINE_TYPE_WITH_PRIVATE (GtkSourcePrintCompositor, gtk_source_print_compositor, G_TYPE_OBJECT)
199 
200 #define MM_PER_INCH 25.4
201 #define POINTS_PER_INCH 72
202 
203 static gdouble
204 convert_to_mm (gdouble len, GtkUnit unit)
205 {
206 	switch (unit)
207 	{
208 		case GTK_UNIT_MM:
209 			return len;
210 
211 		case GTK_UNIT_INCH:
212 			return len * MM_PER_INCH;
213 
214 		default:
215 		case GTK_UNIT_PIXEL:
216 			g_warning ("Unsupported unit");
217 			/* Fall through */
218 
219 		case GTK_UNIT_POINTS:
220 			return len * (MM_PER_INCH / POINTS_PER_INCH);
221     	}
222 }
223 
224 static gdouble
convert_from_mm(gdouble len,GtkUnit unit)225 convert_from_mm (gdouble len, GtkUnit unit)
226 {
227 	switch (unit)
228 	{
229 		case GTK_UNIT_MM:
230 			return len;
231 
232 		case GTK_UNIT_INCH:
233 			return len / MM_PER_INCH;
234 
235 		default:
236 		case GTK_UNIT_PIXEL:
237 			g_warning ("Unsupported unit");
238 			/* Fall through */
239 
240 		case GTK_UNIT_POINTS:
241 			return len / (MM_PER_INCH / POINTS_PER_INCH);
242 	}
243 }
244 
245 static void
gtk_source_print_compositor_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)246 gtk_source_print_compositor_get_property (GObject    *object,
247 					  guint       prop_id,
248 					  GValue     *value,
249 					  GParamSpec *pspec)
250 {
251 	GtkSourcePrintCompositor *compositor = GTK_SOURCE_PRINT_COMPOSITOR (object);
252 
253 	switch (prop_id)
254 	{
255 		case PROP_BUFFER:
256 			g_value_set_object (value, compositor->priv->buffer);
257 			break;
258 		case PROP_TAB_WIDTH:
259 			g_value_set_uint (value,
260 					  gtk_source_print_compositor_get_tab_width (compositor));
261 			break;
262 		case PROP_WRAP_MODE:
263 			g_value_set_enum (value,
264 					  gtk_source_print_compositor_get_wrap_mode (compositor));
265 			break;
266 		case PROP_HIGHLIGHT_SYNTAX:
267 			g_value_set_boolean (value,
268 					     gtk_source_print_compositor_get_highlight_syntax (compositor));
269 			break;
270 		case PROP_PRINT_LINE_NUMBERS:
271 			g_value_set_uint (value,
272 					  gtk_source_print_compositor_get_print_line_numbers (compositor));
273 			break;
274 		case PROP_PRINT_HEADER:
275 			g_value_set_boolean (value,
276 					     gtk_source_print_compositor_get_print_header (compositor));
277 			break;
278 		case PROP_PRINT_FOOTER:
279 			g_value_set_boolean (value,
280 					     gtk_source_print_compositor_get_print_footer (compositor));
281 			break;
282 		case PROP_BODY_FONT_NAME:
283 			g_value_set_string (value,
284 					    gtk_source_print_compositor_get_body_font_name (compositor));
285 			break;
286 		case PROP_LINE_NUMBERS_FONT_NAME:
287 			g_value_set_string (value,
288 					    gtk_source_print_compositor_get_line_numbers_font_name (compositor));
289 			break;
290 		case PROP_HEADER_FONT_NAME:
291 			g_value_set_string (value,
292 					    gtk_source_print_compositor_get_header_font_name (compositor));
293 			break;
294 		case PROP_FOOTER_FONT_NAME:
295 			g_value_set_string (value,
296 					    gtk_source_print_compositor_get_footer_font_name (compositor));
297 			break;
298 		case PROP_N_PAGES:
299 			g_value_set_int (value,
300 					 gtk_source_print_compositor_get_n_pages (compositor));
301 			break;
302 		default:
303 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
304 			break;
305 	}
306 }
307 
308 static void
gtk_source_print_compositor_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)309 gtk_source_print_compositor_set_property (GObject      *object,
310 					  guint         prop_id,
311 					  const GValue *value,
312 					  GParamSpec   *pspec)
313 {
314 	GtkSourcePrintCompositor *compositor = GTK_SOURCE_PRINT_COMPOSITOR (object);
315 
316 	switch (prop_id)
317 	{
318 		case PROP_BUFFER:
319 			compositor->priv->buffer = GTK_SOURCE_BUFFER (g_value_dup_object (value));
320 			break;
321 		case PROP_TAB_WIDTH:
322 			gtk_source_print_compositor_set_tab_width (compositor,
323 								   g_value_get_uint (value));
324 			break;
325 		case PROP_WRAP_MODE:
326 			gtk_source_print_compositor_set_wrap_mode (compositor,
327 								   g_value_get_enum (value));
328 			break;
329 		case PROP_HIGHLIGHT_SYNTAX:
330 			gtk_source_print_compositor_set_highlight_syntax (compositor,
331 									  g_value_get_boolean (value));
332 			break;
333 		case PROP_PRINT_LINE_NUMBERS:
334 			gtk_source_print_compositor_set_print_line_numbers (compositor,
335 									    g_value_get_uint (value));
336 			break;
337 		case PROP_PRINT_HEADER:
338 			gtk_source_print_compositor_set_print_header (compositor, g_value_get_boolean (value));
339 			break;
340 
341 		case PROP_PRINT_FOOTER:
342 			gtk_source_print_compositor_set_print_footer (compositor, g_value_get_boolean (value));
343 			break;
344 		case PROP_BODY_FONT_NAME:
345 			gtk_source_print_compositor_set_body_font_name (compositor,
346 									g_value_get_string (value));
347 			break;
348 		case PROP_LINE_NUMBERS_FONT_NAME:
349 			gtk_source_print_compositor_set_line_numbers_font_name (compositor,
350 										g_value_get_string (value));
351 			break;
352 		case PROP_HEADER_FONT_NAME:
353 			gtk_source_print_compositor_set_header_font_name (compositor,
354 									g_value_get_string (value));
355 			break;
356 		case PROP_FOOTER_FONT_NAME:
357 			gtk_source_print_compositor_set_footer_font_name (compositor,
358 									g_value_get_string (value));
359 			break;
360 		default:
361 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
362 			break;
363 	}
364 }
365 
366 static void
gtk_source_print_compositor_finalize(GObject * object)367 gtk_source_print_compositor_finalize (GObject *object)
368 {
369 	GtkSourcePrintCompositor *compositor;
370 
371 	compositor = GTK_SOURCE_PRINT_COMPOSITOR (object);
372 
373 	if (compositor->priv->pages != NULL)
374 		g_array_free (compositor->priv->pages, TRUE);
375 
376 	if (compositor->priv->layout != NULL)
377 		g_object_unref (compositor->priv->layout);
378 
379 	if (compositor->priv->line_numbers_layout != NULL)
380 		g_object_unref (compositor->priv->line_numbers_layout);
381 
382 	if (compositor->priv->header_layout != NULL)
383 		g_object_unref (compositor->priv->header_layout);
384 
385 	if (compositor->priv->footer_layout != NULL)
386 		g_object_unref (compositor->priv->footer_layout);
387 
388 	pango_font_description_free (compositor->priv->body_font);
389 
390 	if (compositor->priv->line_numbers_font != NULL)
391 		pango_font_description_free (compositor->priv->line_numbers_font);
392 
393 	if (compositor->priv->header_font != NULL)
394 		pango_font_description_free (compositor->priv->header_font);
395 
396 	if (compositor->priv->footer_font != NULL)
397 		pango_font_description_free (compositor->priv->footer_font);
398 
399 	g_free (compositor->priv->header_format_left);
400 	g_free (compositor->priv->header_format_right);
401 	g_free (compositor->priv->header_format_center);
402 	g_free (compositor->priv->footer_format_left);
403 	g_free (compositor->priv->footer_format_right);
404 	g_free (compositor->priv->footer_format_center);
405 
406 	G_OBJECT_CLASS (gtk_source_print_compositor_parent_class)->finalize (object);
407 }
408 
409 static void
gtk_source_print_compositor_dispose(GObject * object)410 gtk_source_print_compositor_dispose (GObject *object)
411 {
412 	GtkSourcePrintCompositor *compositor;
413 
414 	compositor = GTK_SOURCE_PRINT_COMPOSITOR (object);
415 
416 	g_clear_object (&compositor->priv->buffer);
417 
418 	G_OBJECT_CLASS (gtk_source_print_compositor_parent_class)->dispose (object);
419 }
420 
421 static void
gtk_source_print_compositor_class_init(GtkSourcePrintCompositorClass * klass)422 gtk_source_print_compositor_class_init (GtkSourcePrintCompositorClass *klass)
423 {
424 	GObjectClass *object_class;
425 
426 	object_class = G_OBJECT_CLASS (klass);
427 
428 	object_class->get_property = gtk_source_print_compositor_get_property;
429 	object_class->set_property = gtk_source_print_compositor_set_property;
430 	object_class->finalize = gtk_source_print_compositor_finalize;
431 	object_class->dispose = gtk_source_print_compositor_dispose;
432 
433 	/**
434 	 * GtkSourcePrintCompositor:buffer:
435 	 *
436 	 * The GtkSourceBuffer object to print.
437 	 *
438 	 * Since: 2.2
439 	 */
440 	g_object_class_install_property (object_class,
441 					 PROP_BUFFER,
442 					 g_param_spec_object ("buffer",
443 							      "Source Buffer",
444 							      "The GtkSourceBuffer object to print",
445 							      GTK_SOURCE_TYPE_BUFFER,
446 							      G_PARAM_READWRITE |
447 							      G_PARAM_CONSTRUCT_ONLY |
448 							      G_PARAM_STATIC_STRINGS));
449 
450 	/**
451 	 * GtkSourcePrintCompositor:tab-width:
452 	 *
453 	 * Width of a tab character expressed in spaces.
454 	 *
455 	 * The value of this property cannot be changed anymore after the first
456 	 * call to the gtk_source_print_compositor_paginate() function.
457 	 *
458 	 * Since: 2.2
459 	 */
460 	g_object_class_install_property (object_class,
461 					 PROP_TAB_WIDTH,
462 					 g_param_spec_uint ("tab-width",
463 							    "Tab Width",
464 							    "Width of a tab character expressed in spaces",
465 							    1,
466 							    MAX_TAB_WIDTH,
467 							    DEFAULT_TAB_WIDTH,
468 							    G_PARAM_READWRITE |
469 							    G_PARAM_STATIC_STRINGS));
470 
471 
472 	/**
473 	 * GtkSourcePrintCompositor:wrap-mode:
474 	 *
475 	 * Whether to wrap lines never, at word boundaries, or at character boundaries.
476 	 *
477  	 * The value of this property cannot be changed anymore after the first
478 	 * call to the gtk_source_print_compositor_paginate() function.
479 	 *
480 	 * Since: 2.2
481 	 */
482 	g_object_class_install_property (object_class,
483 					 PROP_WRAP_MODE,
484 					 g_param_spec_enum ("wrap-mode",
485 							    "Wrap Mode",
486 							    "",
487 							    GTK_TYPE_WRAP_MODE,
488 							    GTK_WRAP_NONE,
489 							    G_PARAM_READWRITE |
490 							    G_PARAM_STATIC_STRINGS));
491 
492 	/**
493 	 * GtkSourcePrintCompositor:highlight-syntax:
494 	 *
495 	 * Whether to print the document with highlighted syntax.
496 	 *
497  	 * The value of this property cannot be changed anymore after the first
498 	 * call to the gtk_source_print_compositor_paginate() function.
499 	 *
500 	 * Since: 2.2
501 	 */
502 	g_object_class_install_property (object_class,
503 					 PROP_HIGHLIGHT_SYNTAX,
504 					 g_param_spec_boolean ("highlight-syntax",
505 							       "Highlight Syntax",
506 							       "",
507 							       TRUE,
508 							       G_PARAM_READWRITE |
509 							       G_PARAM_STATIC_STRINGS));
510 
511 	/**
512 	 * GtkSourcePrintCompositor:print-line-numbers:
513 	 *
514 	 * Interval of printed line numbers. If this property is set to 0 no
515 	 * numbers will be printed.  If greater than 0, a number will be
516  	 * printed every "print-line-numbers" lines (i.e. 1 will print all line numbers).
517 	 *
518 	 * The value of this property cannot be changed anymore after the first
519 	 * call to the gtk_source_print_compositor_paginate() function.
520 	 *
521 	 * Since: 2.2
522 	 */
523 	g_object_class_install_property (object_class,
524 					 PROP_PRINT_LINE_NUMBERS,
525 					 g_param_spec_uint ("print-line-numbers",
526 							    "Print Line Numbers",
527 							    "",
528 							    0, 100, 1,
529 							    G_PARAM_READWRITE |
530 							    G_PARAM_STATIC_STRINGS));
531 
532 	/**
533 	 * GtkSourcePrintCompositor:print-header:
534 	 *
535 	 * Whether to print a header in each page.
536 	 *
537 	 * Note that by default the header format is unspecified, and if it is
538 	 * unspecified the header will not be printed, regardless of the value of
539 	 * this property.
540 	 *
541 	 * The value of this property cannot be changed anymore after the first
542 	 * call to the gtk_source_print_compositor_paginate() function.
543 	 *
544 	 * Since: 2.2
545 	 */
546 	g_object_class_install_property (object_class,
547 					 PROP_PRINT_HEADER,
548 					 g_param_spec_boolean ("print-header",
549 							       "Print Header",
550 							       "",
551 							       FALSE,
552 							       G_PARAM_READWRITE |
553 							       G_PARAM_STATIC_STRINGS));
554 
555 	/**
556 	 * GtkSourcePrintCompositor:print-footer:
557 	 *
558 	 * Whether to print a footer in each page.
559 	 *
560 	 * Note that by default the footer format is unspecified, and if it is
561 	 * unspecified the footer will not be printed, regardless of the value of
562 	 * this property.
563 	 *
564 	 * The value of this property cannot be changed anymore after the first
565 	 * call to the gtk_source_print_compositor_paginate() function.
566 	 *
567 	 * Since: 2.2
568 	 */
569 	g_object_class_install_property (object_class,
570 					 PROP_PRINT_FOOTER,
571 					 g_param_spec_boolean ("print-footer",
572 							       "Print Footer",
573 							       "",
574 							       FALSE,
575 							       G_PARAM_READWRITE |
576 							       G_PARAM_STATIC_STRINGS));
577 
578 	/**
579 	 * GtkSourcePrintCompositor:body-font-name:
580 	 *
581 	 * Name of the font used for the text body.
582 	 *
583 	 * Accepted values are strings representing a font description Pango can understand.
584 	 * (e.g. &quot;Monospace 10&quot;). See pango_font_description_from_string()
585 	 * for a description of the format of the string representation.
586 	 *
587 	 * The value of this property cannot be changed anymore after the first
588 	 * call to the gtk_source_print_compositor_paginate() function.
589 	 *
590 	 * Since: 2.2
591 	 */
592 	g_object_class_install_property (object_class,
593 					 PROP_BODY_FONT_NAME,
594 					 g_param_spec_string ("body-font-name",
595 							      "Body Font Name",
596 							      "",
597 							      NULL,
598 							      G_PARAM_READWRITE |
599 							      G_PARAM_STATIC_STRINGS));
600 
601 	/**
602 	 * GtkSourcePrintCompositor:line-numbers-font-name:
603 	 *
604 	 * Name of the font used to print line numbers on the left margin.
605 	 * If this property is unspecified, the text body font is used.
606 	 *
607 	 * Accepted values are strings representing a font description Pango can understand.
608 	 * (e.g. &quot;Monospace 10&quot;). See pango_font_description_from_string()
609 	 * for a description of the format of the string representation.
610 	 *
611 	 * The value of this property cannot be changed anymore after the first
612 	 * call to the gtk_source_print_compositor_paginate() function.
613 	 *
614 	 * Since: 2.2
615 	 */
616 	g_object_class_install_property (object_class,
617 					 PROP_LINE_NUMBERS_FONT_NAME,
618 					 g_param_spec_string ("line-numbers-font-name",
619 							      "Line Numbers Font Name",
620 							      "",
621 							      NULL,
622 							      G_PARAM_READWRITE |
623 							      G_PARAM_STATIC_STRINGS));
624 
625 	/**
626 	 * GtkSourcePrintCompositor:header-font-name:
627 	 *
628 	 * Name of the font used to print page header.
629 	 * If this property is unspecified, the text body font is used.
630 	 *
631 	 * Accepted values are strings representing a font description Pango can understand.
632 	 * (e.g. &quot;Monospace 10&quot;). See pango_font_description_from_string()
633 	 * for a description of the format of the string representation.
634 	 *
635 	 * The value of this property cannot be changed anymore after the first
636 	 * call to the gtk_source_print_compositor_paginate() function.
637 	 *
638 	 * Since: 2.2
639 	 */
640 	g_object_class_install_property (object_class,
641 					 PROP_HEADER_FONT_NAME,
642 					 g_param_spec_string ("header-font-name",
643 							      "Header Font Name",
644 							      "",
645 							      NULL,
646 							      G_PARAM_READWRITE |
647 							      G_PARAM_STATIC_STRINGS));
648 
649 	/**
650 	 * GtkSourcePrintCompositor:footer-font-name:
651 	 *
652 	 * Name of the font used to print page footer.
653 	 * If this property is unspecified, the text body font is used.
654 	 *
655 	 * Accepted values are strings representing a font description Pango can understand.
656 	 * (e.g. &quot;Monospace 10&quot;). See pango_font_description_from_string()
657 	 * for a description of the format of the string representation.
658 	 *
659 	 * The value of this property cannot be changed anymore after the first
660 	 * call to the gtk_source_print_compositor_paginate() function.
661 	 *
662 	 * Since: 2.2
663 	 */
664 	g_object_class_install_property (object_class,
665 					 PROP_FOOTER_FONT_NAME,
666 					 g_param_spec_string ("footer-font-name",
667 							      "Footer Font Name",
668 							      "",
669 							      NULL,
670 							      G_PARAM_READWRITE |
671 							      G_PARAM_STATIC_STRINGS));
672 
673 	/**
674 	 * GtkSourcePrintCompositor:n-pages:
675 	 *
676 	 * The number of pages in the document or <code>-1</code> if the
677  	 * document has not been completely paginated.
678  	 *
679  	 * Since: 2.2
680  	 */
681 	g_object_class_install_property (object_class,
682 					 PROP_N_PAGES,
683 					 g_param_spec_int ("n-pages",
684 							   "Number of pages",
685 							   "",
686 							   -1, G_MAXINT, -1,
687 							   G_PARAM_READABLE |
688 							   G_PARAM_STATIC_STRINGS));
689 }
690 
691 static void
gtk_source_print_compositor_init(GtkSourcePrintCompositor * compositor)692 gtk_source_print_compositor_init (GtkSourcePrintCompositor *compositor)
693 {
694 	GtkSourcePrintCompositorPrivate *priv;
695 
696 	priv = gtk_source_print_compositor_get_instance_private (compositor);
697 
698 	compositor->priv = priv;
699 
700 	priv->buffer = NULL;
701 
702 	priv->tab_width = DEFAULT_TAB_WIDTH;
703 	priv->wrap_mode = GTK_WRAP_NONE;
704 	priv->highlight_syntax = TRUE;
705 	priv->print_line_numbers = 0;
706 
707 	priv->body_font = pango_font_description_from_string (DEFAULT_FONT_NAME);
708 	priv->line_numbers_font = NULL;
709 	priv->header_font = NULL;
710 	priv->footer_font = NULL;
711 
712 	priv->paper_width = 0.0;
713 	priv->paper_height = 0.0;
714 
715 	priv->margin_top = 0.0;
716 	priv->margin_bottom = 0.0;
717 	priv->margin_left = 0.0;
718 	priv->margin_right = 0.0;
719 
720 	priv->print_header = FALSE;
721 	priv->print_footer = FALSE;
722 
723 	priv->header_format_left = NULL;
724 	priv->header_format_center = NULL;
725 	priv->header_format_right = NULL;
726 	priv->header_separator = FALSE;
727 
728 	priv->footer_format_left = NULL;
729 	priv->footer_format_center = NULL;
730 	priv->footer_format_right = NULL;
731 	priv->footer_separator = FALSE;
732 
733 	priv->state = INIT;
734 
735 	priv->pages = NULL;
736 
737 	priv->paginated_lines = 0;
738 	priv->n_pages = -1;
739 	priv->current_page = -1;
740 
741 	priv->layout = NULL;
742 	priv->line_numbers_layout = NULL;
743 
744 	priv->language = gtk_get_default_language ();
745 
746 	/* Negative values mean uninitialized */
747 	priv->header_height = -1.0;
748 	priv->footer_height = -1.0;
749 	priv->line_numbers_width = -1.0;
750 	priv->line_numbers_height = -1.0;
751 }
752 
753 /**
754  * gtk_source_print_compositor_new:
755  * @buffer: the #GtkSourceBuffer to print.
756  *
757  * Creates a new print compositor that can be used to print @buffer.
758  *
759  * Return value: a new print compositor object.
760  *
761  * Since: 2.2
762  **/
763 GtkSourcePrintCompositor *
gtk_source_print_compositor_new(GtkSourceBuffer * buffer)764 gtk_source_print_compositor_new (GtkSourceBuffer *buffer)
765 {
766 	g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (buffer), NULL);
767 
768 	return g_object_new (GTK_SOURCE_TYPE_PRINT_COMPOSITOR,
769 			     "buffer", buffer,
770 			     NULL);
771 }
772 
773 /**
774  * gtk_source_print_compositor_new_from_view:
775  * @view: a #GtkSourceView to get configuration from.
776  *
777  * Creates a new print compositor that can be used to print the buffer
778  * associated with @view.
779  * This constructor sets some configuration properties to make the
780  * printed output match @view as much as possible.  The properties set are
781  * #GtkSourcePrintCompositor:tab-width, #GtkSourcePrintCompositor:highlight-syntax,
782  * #GtkSourcePrintCompositor:wrap-mode, #GtkSourcePrintCompositor:body-font-name and
783  * #GtkSourcePrintCompositor:print-line-numbers.
784  *
785  * Return value: a new print compositor object.
786  *
787  * Since: 2.2
788  **/
789 GtkSourcePrintCompositor *
gtk_source_print_compositor_new_from_view(GtkSourceView * view)790 gtk_source_print_compositor_new_from_view (GtkSourceView *view)
791 {
792 	GtkSourceBuffer *buffer = NULL;
793 	PangoContext *pango_context;
794 	PangoFontDescription* font_desc;
795 	GtkSourcePrintCompositor *compositor;
796 
797 	g_return_val_if_fail (GTK_SOURCE_IS_VIEW (view), NULL);
798 	g_return_val_if_fail (GTK_SOURCE_IS_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view))), NULL);
799 
800 	buffer = GTK_SOURCE_BUFFER (gtk_text_view_get_buffer (GTK_TEXT_VIEW (view)));
801 
802 	compositor = GTK_SOURCE_PRINT_COMPOSITOR (
803 			g_object_new (GTK_SOURCE_TYPE_PRINT_COMPOSITOR,
804 				     "buffer", buffer,
805 				     "tab-width", gtk_source_view_get_tab_width (view),
806 				     "highlight-syntax", gtk_source_buffer_get_highlight_syntax (buffer) != FALSE,
807 				     "wrap-mode", gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view)),
808 				     "print-line-numbers", (gtk_source_view_get_show_line_numbers (view) == FALSE) ? 0 : 1,
809 				     NULL));
810 
811 	/* Set the body font directly since the property get a name while body_font is a PangoFontDescription */
812 	pango_context = gtk_widget_get_pango_context (GTK_WIDGET (view));
813 
814 	font_desc = pango_context_get_font_description (pango_context);
815 
816 	compositor->priv->body_font = pango_font_description_copy (font_desc);
817 	g_object_notify (G_OBJECT (compositor), "body-font-name"); /* FIXME: is this needed? */
818 
819 	return compositor;
820 }
821 
822 /**
823  * gtk_source_print_compositor_get_buffer:
824  * @compositor: a #GtkSourcePrintCompositor.
825  *
826  * Gets the #GtkSourceBuffer associated with the compositor. The returned
827  * object reference is owned by the compositor object and
828  * should not be unreferenced.
829  *
830  * Return value: (transfer none): the #GtkSourceBuffer associated with the compositor.
831  *
832  * Since: 2.2
833  **/
834 GtkSourceBuffer *
gtk_source_print_compositor_get_buffer(GtkSourcePrintCompositor * compositor)835 gtk_source_print_compositor_get_buffer (GtkSourcePrintCompositor *compositor)
836 {
837 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), NULL);
838 
839 	return compositor->priv->buffer;
840 }
841 
842 /**
843  * gtk_source_print_compositor_set_tab_width:
844  * @compositor: a #GtkSourcePrintCompositor.
845  * @width: width of tab in characters.
846  *
847  * Sets the width of tabulation in characters for printed text.
848  *
849  * This function cannot be called anymore after the first call to the
850  * gtk_source_print_compositor_paginate() function.
851  *
852  * Since: 2.2
853  */
854 void
gtk_source_print_compositor_set_tab_width(GtkSourcePrintCompositor * compositor,guint width)855 gtk_source_print_compositor_set_tab_width (GtkSourcePrintCompositor *compositor,
856 					   guint                     width)
857 {
858 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
859 	g_return_if_fail (width > 0 && width <= MAX_TAB_WIDTH);
860 	g_return_if_fail (compositor->priv->state == INIT);
861 
862 	if (width == compositor->priv->tab_width)
863 		return;
864 
865 	compositor->priv->tab_width = width;
866 
867 	g_object_notify (G_OBJECT (compositor), "tab-width");
868 }
869 
870 /**
871  * gtk_source_print_compositor_get_tab_width:
872  * @compositor: a #GtkSourcePrintCompositor.
873  *
874  * Returns the width of tabulation in characters for printed text.
875  *
876  * Return value: width of tab.
877  *
878  * Since: 2.2
879  */
880 guint
gtk_source_print_compositor_get_tab_width(GtkSourcePrintCompositor * compositor)881 gtk_source_print_compositor_get_tab_width (GtkSourcePrintCompositor *compositor)
882 {
883 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), DEFAULT_TAB_WIDTH);
884 
885 	return compositor->priv->tab_width;
886 }
887 
888 /**
889  * gtk_source_print_compositor_set_wrap_mode:
890  * @compositor: a #GtkSourcePrintCompositor.
891  * @wrap_mode: a #GtkWrapMode.
892  *
893  * Sets the line wrapping mode for the printed text.
894  *
895  * This function cannot be called anymore after the first call to the
896  * gtk_source_print_compositor_paginate() function.
897  *
898  * Since: 2.2
899  */
900 void
gtk_source_print_compositor_set_wrap_mode(GtkSourcePrintCompositor * compositor,GtkWrapMode wrap_mode)901 gtk_source_print_compositor_set_wrap_mode (GtkSourcePrintCompositor *compositor,
902 					   GtkWrapMode               wrap_mode)
903 {
904 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
905 	g_return_if_fail (compositor->priv->state == INIT);
906 
907 	if (wrap_mode == compositor->priv->wrap_mode)
908 		return;
909 
910 	compositor->priv->wrap_mode = wrap_mode;
911 
912 	g_object_notify (G_OBJECT (compositor), "wrap-mode");
913 }
914 
915 /**
916  * gtk_source_print_compositor_get_wrap_mode:
917  * @compositor: a #GtkSourcePrintCompositor.
918  *
919  * Gets the line wrapping mode for the printed text.
920  *
921  * Return value: the line wrap mode.
922  *
923  * Since: 2.2
924  */
925 GtkWrapMode
gtk_source_print_compositor_get_wrap_mode(GtkSourcePrintCompositor * compositor)926 gtk_source_print_compositor_get_wrap_mode (GtkSourcePrintCompositor *compositor)
927 {
928 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), GTK_WRAP_NONE);
929 
930 	return compositor->priv->wrap_mode;
931 }
932 
933 /**
934  * gtk_source_print_compositor_set_highlight_syntax:
935  * @compositor: a #GtkSourcePrintCompositor.
936  * @highlight: whether syntax should be highlighted.
937  *
938  * Sets whether the printed text will be highlighted according to the
939  * buffer rules.  Both color and font style are applied.
940  *
941  * This function cannot be called anymore after the first call to the
942  * gtk_source_print_compositor_paginate() function.
943  *
944  * Since: 2.2
945  **/
946 void
gtk_source_print_compositor_set_highlight_syntax(GtkSourcePrintCompositor * compositor,gboolean highlight)947 gtk_source_print_compositor_set_highlight_syntax (GtkSourcePrintCompositor *compositor,
948 						  gboolean                  highlight)
949 {
950 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
951 	g_return_if_fail (compositor->priv->state == INIT);
952 
953 	highlight = (highlight != FALSE);
954 
955 	if (highlight == compositor->priv->highlight_syntax)
956 		return;
957 
958 	compositor->priv->highlight_syntax = highlight;
959 
960 	g_object_notify (G_OBJECT (compositor), "highlight-syntax");
961 }
962 
963 /**
964  * gtk_source_print_compositor_get_highlight_syntax:
965  * @compositor: a #GtkSourcePrintCompositor.
966  *
967  * Determines whether the printed text will be highlighted according to the
968  * buffer rules.  Note that highlighting will happen
969  * only if the buffer to print has highlighting activated.
970  *
971  * Return value: %TRUE if the printed output will be highlighted.
972  *
973  * Since: 2.2
974  **/
975 gboolean
gtk_source_print_compositor_get_highlight_syntax(GtkSourcePrintCompositor * compositor)976 gtk_source_print_compositor_get_highlight_syntax (GtkSourcePrintCompositor *compositor)
977 {
978 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), FALSE);
979 
980 	return compositor->priv->highlight_syntax;
981 }
982 
983 /**
984  * gtk_source_print_compositor_set_print_line_numbers:
985  * @compositor: a #GtkSourcePrintCompositor.
986  * @interval: interval for printed line numbers.
987  *
988  * Sets the interval for printed line numbers.  If @interval is 0 no
989  * numbers will be printed.  If greater than 0, a number will be
990  * printed every @interval lines (i.e. 1 will print all line numbers).
991  *
992  * Maximum accepted value for @interval is 100.
993  *
994  * This function cannot be called anymore after the first call to the
995  * gtk_source_print_compositor_paginate() function.
996  *
997  * Since: 2.2
998  **/
999 void
gtk_source_print_compositor_set_print_line_numbers(GtkSourcePrintCompositor * compositor,guint interval)1000 gtk_source_print_compositor_set_print_line_numbers (GtkSourcePrintCompositor *compositor,
1001 						    guint                     interval)
1002 {
1003 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1004 	g_return_if_fail (compositor->priv->state == INIT);
1005 	g_return_if_fail (interval <= 100);
1006 
1007 	if (interval == compositor->priv->print_line_numbers)
1008 		return;
1009 
1010 	compositor->priv->print_line_numbers = interval;
1011 
1012 	g_object_notify (G_OBJECT (compositor), "print-line-numbers");
1013 }
1014 
1015 /**
1016  * gtk_source_print_compositor_set_print_header:
1017  * @compositor: a #GtkSourcePrintCompositor.
1018  * @print: %TRUE if you want the header to be printed.
1019  *
1020  * Sets whether you want to print a header in each page.  The
1021  * header consists of three pieces of text and an optional line
1022  * separator, configurable with
1023  * gtk_source_print_compositor_set_header_format().
1024  *
1025  * Note that by default the header format is unspecified, and if it's
1026  * empty it will not be printed, regardless of this setting.
1027  *
1028  * This function cannot be called anymore after the first call to the
1029  * gtk_source_print_compositor_paginate() function.
1030  *
1031  * Since: 2.2
1032  **/
1033 void
gtk_source_print_compositor_set_print_header(GtkSourcePrintCompositor * compositor,gboolean print)1034 gtk_source_print_compositor_set_print_header (GtkSourcePrintCompositor *compositor,
1035 					      gboolean                  print)
1036 {
1037 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1038 	g_return_if_fail (compositor->priv->state == INIT);
1039 
1040 	print = (print != FALSE);
1041 
1042 	if (print == compositor->priv->print_header)
1043 		return;
1044 
1045 	compositor->priv->print_header = print;
1046 
1047 	g_object_notify (G_OBJECT (compositor), "print-header");
1048 }
1049 
1050 /**
1051  * gtk_source_print_compositor_get_print_header:
1052  * @compositor: a #GtkSourcePrintCompositor.
1053  *
1054  * Determines if a header is set to be printed for each page.  A
1055  * header will be printed if this function returns %TRUE
1056  * <emphasis>and</emphasis> some format strings have been specified
1057  * with gtk_source_print_compositor_set_header_format().
1058  *
1059  * Return value: %TRUE if the header is set to be printed.
1060  *
1061  * Since: 2.2
1062  **/
1063 gboolean
gtk_source_print_compositor_get_print_header(GtkSourcePrintCompositor * compositor)1064 gtk_source_print_compositor_get_print_header (GtkSourcePrintCompositor *compositor)
1065 {
1066 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), FALSE);
1067 
1068 	return compositor->priv->print_header;
1069 }
1070 
1071 /**
1072  * gtk_source_print_compositor_set_print_footer:
1073  * @compositor: a #GtkSourcePrintCompositor.
1074  * @print: %TRUE if you want the footer to be printed.
1075  *
1076  * Sets whether you want to print a footer in each page.  The
1077  * footer consists of three pieces of text and an optional line
1078  * separator, configurable with
1079  * gtk_source_print_compositor_set_footer_format().
1080  *
1081  * Note that by default the footer format is unspecified, and if it's
1082  * empty it will not be printed, regardless of this setting.
1083  *
1084  * This function cannot be called anymore after the first call to the
1085  * gtk_source_print_compositor_paginate() function.
1086  *
1087  * Since: 2.2
1088  **/
1089 void
gtk_source_print_compositor_set_print_footer(GtkSourcePrintCompositor * compositor,gboolean print)1090 gtk_source_print_compositor_set_print_footer (GtkSourcePrintCompositor *compositor,
1091 					      gboolean                  print)
1092 {
1093 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1094 	g_return_if_fail (compositor->priv->state == INIT);
1095 
1096 	print = (print != FALSE);
1097 
1098 	if (print == compositor->priv->print_footer)
1099 		return;
1100 
1101 	compositor->priv->print_footer = print;
1102 
1103 	g_object_notify (G_OBJECT (compositor), "print-footer");
1104 }
1105 
1106 /**
1107  * gtk_source_print_compositor_get_print_footer:
1108  * @compositor: a #GtkSourcePrintCompositor.
1109  *
1110  * Determines if a footer is set to be printed for each page.  A
1111  * footer will be printed if this function returns %TRUE
1112  * <emphasis>and</emphasis> some format strings have been specified
1113  * with gtk_source_print_compositor_set_footer_format().
1114  *
1115  * Return value: %TRUE if the footer is set to be printed.
1116  *
1117  * Since: 2.2
1118  **/
1119 gboolean
gtk_source_print_compositor_get_print_footer(GtkSourcePrintCompositor * compositor)1120 gtk_source_print_compositor_get_print_footer (GtkSourcePrintCompositor *compositor)
1121 {
1122 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), FALSE);
1123 
1124 	return compositor->priv->print_footer;
1125 }
1126 
1127 /**
1128  * gtk_source_print_compositor_set_header_format:
1129  * @compositor: a #GtkSourcePrintCompositor.
1130  * @separator: %TRUE if you want a separator line to be printed.
1131  * @left: (nullable): a format string to print on the left of the header.
1132  * @center: (nullable): a format string to print on the center of the header.
1133  * @right: (nullable): a format string to print on the right of the header.
1134  *
1135  * Sets strftime like header format strings, to be printed on the
1136  * left, center and right of the top of each page.  The strings may
1137  * include strftime(3) codes which will be expanded at print time.
1138  * A subset of strftime() codes are accepted, see g_date_time_format()
1139  * for more details on the accepted format specifiers.
1140  * Additionally the following format specifiers are accepted:
1141  * - #N: the page number
1142  * - #Q: the page count.
1143  *
1144  * @separator specifies if a solid line should be drawn to separate
1145  * the header from the document text.
1146  *
1147  * If %NULL is given for any of the three arguments, that particular
1148  * string will not be printed.
1149  *
1150  * For the header to be printed, in
1151  * addition to specifying format strings, you need to enable header
1152  * printing with gtk_source_print_compositor_set_print_header().
1153  *
1154  * This function cannot be called anymore after the first call to the
1155  * gtk_source_print_compositor_paginate() function.
1156  *
1157  * Since: 2.2
1158  **/
1159 void
gtk_source_print_compositor_set_header_format(GtkSourcePrintCompositor * compositor,gboolean separator,const gchar * left,const gchar * center,const gchar * right)1160 gtk_source_print_compositor_set_header_format (GtkSourcePrintCompositor *compositor,
1161 					       gboolean                  separator,
1162 					       const gchar              *left,
1163 					       const gchar              *center,
1164 					       const gchar              *right)
1165 {
1166 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1167 	g_return_if_fail (compositor->priv->state == INIT);
1168 
1169 	/* FIXME: validate given strings? */
1170 	g_free (compositor->priv->header_format_left);
1171 	g_free (compositor->priv->header_format_center);
1172 	g_free (compositor->priv->header_format_right);
1173 
1174 	compositor->priv->header_separator = separator;
1175 
1176 	compositor->priv->header_format_left = g_strdup (left);
1177 	compositor->priv->header_format_center = g_strdup (center);
1178 	compositor->priv->header_format_right = g_strdup (right);
1179 }
1180 
1181 /**
1182  * gtk_source_print_compositor_set_footer_format:
1183  * @compositor: a #GtkSourcePrintCompositor.
1184  * @separator: %TRUE if you want a separator line to be printed.
1185  * @left: (nullable): a format string to print on the left of the footer.
1186  * @center: (nullable): a format string to print on the center of the footer.
1187  * @right: (nullable): a format string to print on the right of the footer.
1188  *
1189  * See gtk_source_print_compositor_set_header_format() for more information
1190  * about the parameters.
1191  *
1192  * Since: 2.2
1193  **/
1194 void
gtk_source_print_compositor_set_footer_format(GtkSourcePrintCompositor * compositor,gboolean separator,const gchar * left,const gchar * center,const gchar * right)1195 gtk_source_print_compositor_set_footer_format (GtkSourcePrintCompositor *compositor,
1196 					       gboolean                  separator,
1197 					       const gchar              *left,
1198 					       const gchar              *center,
1199 					       const gchar              *right)
1200 {
1201 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1202 	g_return_if_fail (compositor->priv->state == INIT);
1203 
1204 	/* FIXME: validate given strings? */
1205 	g_free (compositor->priv->footer_format_left);
1206 	g_free (compositor->priv->footer_format_center);
1207 	g_free (compositor->priv->footer_format_right);
1208 
1209 	compositor->priv->footer_separator = separator;
1210 
1211 	compositor->priv->footer_format_left = g_strdup (left);
1212 	compositor->priv->footer_format_center = g_strdup (center);
1213 	compositor->priv->footer_format_right = g_strdup (right);
1214 }
1215 
1216 /**
1217  * gtk_source_print_compositor_get_print_line_numbers:
1218  * @compositor: a #GtkSourcePrintCompositor.
1219  *
1220  * Returns the interval used for line number printing.  If the
1221  * value is 0, no line numbers will be printed.  The default value is
1222  * 1 (i.e. numbers printed in all lines).
1223  *
1224  * Return value: the interval of printed line numbers.
1225  *
1226  * Since: 2.2
1227  **/
1228 guint
gtk_source_print_compositor_get_print_line_numbers(GtkSourcePrintCompositor * compositor)1229 gtk_source_print_compositor_get_print_line_numbers (GtkSourcePrintCompositor *compositor)
1230 {
1231 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), 0);
1232 
1233 	return compositor->priv->print_line_numbers;
1234 }
1235 
1236 static gboolean
set_font_description_from_name(GtkSourcePrintCompositor * compositor,PangoFontDescription ** font,const gchar * font_name)1237 set_font_description_from_name (GtkSourcePrintCompositor  *compositor,
1238 				PangoFontDescription     **font,
1239 				const gchar               *font_name)
1240 {
1241 	PangoFontDescription *new;
1242 
1243 	if (font_name != NULL)
1244 		new = pango_font_description_from_string (font_name);
1245 	else
1246 	{
1247 		g_return_val_if_fail (compositor->priv->body_font != NULL, FALSE);
1248 		new = pango_font_description_copy (compositor->priv->body_font);
1249 	}
1250 
1251 	if (*font == NULL || !pango_font_description_equal (*font, new))
1252 	{
1253 		if (*font != NULL)
1254 			pango_font_description_free (*font);
1255 		*font = new;
1256 
1257 		return TRUE;
1258 	}
1259 	else
1260 	{
1261 		pango_font_description_free (new);
1262 
1263 		return FALSE;
1264 	}
1265 }
1266 
1267 /**
1268  * gtk_source_print_compositor_set_body_font_name:
1269  * @compositor: a #GtkSourcePrintCompositor.
1270  * @font_name: the name of the default font for the body text.
1271  *
1272  * Sets the default font for the printed text.
1273  *
1274  * @font_name should be a
1275  * string representation of a font description Pango can understand.
1276  * (e.g. &quot;Monospace 10&quot;). See pango_font_description_from_string()
1277  * for a description of the format of the string representation.
1278  *
1279  * This function cannot be called anymore after the first call to the
1280  * gtk_source_print_compositor_paginate() function.
1281  *
1282  * Since: 2.2
1283  */
1284 void
gtk_source_print_compositor_set_body_font_name(GtkSourcePrintCompositor * compositor,const gchar * font_name)1285 gtk_source_print_compositor_set_body_font_name (GtkSourcePrintCompositor *compositor,
1286 						const gchar              *font_name)
1287 {
1288 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1289 	g_return_if_fail (font_name != NULL);
1290 	g_return_if_fail (compositor->priv->state == INIT);
1291 
1292 	if (set_font_description_from_name (compositor,
1293 					    &compositor->priv->body_font,
1294 					    font_name))
1295 	{
1296 		g_object_notify (G_OBJECT (compositor), "body-font-name");
1297 	}
1298 }
1299 
1300 /**
1301  * gtk_source_print_compositor_get_body_font_name:
1302  * @compositor: a #GtkSourcePrintCompositor.
1303  *
1304  * Returns the name of the font used to print the text body. The returned string
1305  * must be freed with g_free().
1306  *
1307  * Return value: a new string containing the name of the font used to print the
1308  * text body.
1309  *
1310  * Since: 2.2
1311  */
1312 gchar *
gtk_source_print_compositor_get_body_font_name(GtkSourcePrintCompositor * compositor)1313 gtk_source_print_compositor_get_body_font_name (GtkSourcePrintCompositor *compositor)
1314 {
1315 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), NULL);
1316 
1317 	return pango_font_description_to_string (compositor->priv->body_font);
1318 }
1319 
1320 /**
1321  * gtk_source_print_compositor_set_line_numbers_font_name:
1322  * @compositor: a #GtkSourcePrintCompositor.
1323  * @font_name: (nullable): the name of the font for line numbers, or %NULL.
1324  *
1325  * Sets the font for printing line numbers on the left margin.  If
1326  * %NULL is supplied, the default font (i.e. the one being used for the
1327  * text) will be used instead.
1328  *
1329  * @font_name should be a
1330  * string representation of a font description Pango can understand.
1331  * (e.g. &quot;Monospace 10&quot;). See pango_font_description_from_string()
1332  * for a description of the format of the string representation.
1333  *
1334  * This function cannot be called anymore after the first call to the
1335  * gtk_source_print_compositor_paginate() function.
1336  *
1337  * Since: 2.2
1338  */
1339 void
gtk_source_print_compositor_set_line_numbers_font_name(GtkSourcePrintCompositor * compositor,const gchar * font_name)1340 gtk_source_print_compositor_set_line_numbers_font_name (GtkSourcePrintCompositor *compositor,
1341 							const gchar              *font_name)
1342 {
1343 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1344 	g_return_if_fail (font_name != NULL);
1345 	g_return_if_fail (compositor->priv->state == INIT);
1346 
1347 	if (set_font_description_from_name (compositor,
1348 					    &compositor->priv->line_numbers_font,
1349 					    font_name))
1350 	{
1351 		g_object_notify (G_OBJECT (compositor), "line-numbers-font-name");
1352 	}
1353 }
1354 
1355 /**
1356  * gtk_source_print_compositor_get_line_numbers_font_name:
1357  * @compositor: a #GtkSourcePrintCompositor.
1358  *
1359  * Returns the name of the font used to print line numbers on the left margin.
1360  * The returned string must be freed with g_free().
1361  *
1362  * Return value: a new string containing the name of the font used to print
1363  * line numbers on the left margin.
1364  *
1365  * Since: 2.2
1366  */
1367 gchar *
gtk_source_print_compositor_get_line_numbers_font_name(GtkSourcePrintCompositor * compositor)1368 gtk_source_print_compositor_get_line_numbers_font_name (GtkSourcePrintCompositor *compositor)
1369 {
1370 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), NULL);
1371 
1372 	if (compositor->priv->line_numbers_font == NULL)
1373 	{
1374 		g_return_val_if_fail (compositor->priv->body_font != NULL, NULL);
1375 		compositor->priv->line_numbers_font = pango_font_description_copy (compositor->priv->body_font);
1376 	}
1377 
1378 	return pango_font_description_to_string (compositor->priv->line_numbers_font);
1379 }
1380 
1381 /**
1382  * gtk_source_print_compositor_set_header_font_name:
1383  * @compositor: a #GtkSourcePrintCompositor.
1384  * @font_name: (nullable): the name of the font for header text, or %NULL.
1385  *
1386  * Sets the font for printing the page header. If
1387  * %NULL is supplied, the default font (i.e. the one being used for the
1388  * text) will be used instead.
1389  *
1390  * @font_name should be a
1391  * string representation of a font description Pango can understand.
1392  * (e.g. &quot;Monospace 10&quot;). See pango_font_description_from_string()
1393  * for a description of the format of the string representation.
1394  *
1395  * This function cannot be called anymore after the first call to the
1396  * gtk_source_print_compositor_paginate() function.
1397  *
1398  * Since: 2.2
1399  */
1400 void
gtk_source_print_compositor_set_header_font_name(GtkSourcePrintCompositor * compositor,const gchar * font_name)1401 gtk_source_print_compositor_set_header_font_name (GtkSourcePrintCompositor *compositor,
1402 							const gchar              *font_name)
1403 {
1404 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1405 	g_return_if_fail (font_name != NULL);
1406 	g_return_if_fail (compositor->priv->state == INIT);
1407 
1408 	if (set_font_description_from_name (compositor,
1409 					    &compositor->priv->header_font,
1410 					    font_name))
1411 
1412 	{
1413 		g_object_notify (G_OBJECT (compositor), "header-font-name");
1414 	}
1415 }
1416 
1417 /**
1418  * gtk_source_print_compositor_get_header_font_name:
1419  * @compositor: a #GtkSourcePrintCompositor.
1420  *
1421  * Returns the name of the font used to print the page header.
1422  * The returned string must be freed with g_free().
1423  *
1424  * Return value: a new string containing the name of the font used to print
1425  * the page header.
1426  *
1427  * Since: 2.2
1428  */
1429 gchar *
gtk_source_print_compositor_get_header_font_name(GtkSourcePrintCompositor * compositor)1430 gtk_source_print_compositor_get_header_font_name (GtkSourcePrintCompositor *compositor)
1431 {
1432 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), NULL);
1433 
1434 	if (compositor->priv->header_font == NULL)
1435 	{
1436 		g_return_val_if_fail (compositor->priv->body_font != NULL, NULL);
1437 		compositor->priv->header_font = pango_font_description_copy (compositor->priv->body_font);
1438 	}
1439 
1440 	return pango_font_description_to_string (compositor->priv->header_font);
1441 }
1442 
1443 /**
1444  * gtk_source_print_compositor_set_footer_font_name:
1445  * @compositor: a #GtkSourcePrintCompositor.
1446  * @font_name: (nullable): the name of the font for the footer text, or %NULL.
1447  *
1448  * Sets the font for printing the page footer. If
1449  * %NULL is supplied, the default font (i.e. the one being used for the
1450  * text) will be used instead.
1451  *
1452  * @font_name should be a
1453  * string representation of a font description Pango can understand.
1454  * (e.g. &quot;Monospace 10&quot;). See pango_font_description_from_string()
1455  * for a description of the format of the string representation.
1456  *
1457  * This function cannot be called anymore after the first call to the
1458  * gtk_source_print_compositor_paginate() function.
1459  *
1460  * Since: 2.2
1461  */
1462 void
gtk_source_print_compositor_set_footer_font_name(GtkSourcePrintCompositor * compositor,const gchar * font_name)1463 gtk_source_print_compositor_set_footer_font_name (GtkSourcePrintCompositor *compositor,
1464 						  const gchar              *font_name)
1465 {
1466 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1467 	g_return_if_fail (font_name != NULL);
1468 	g_return_if_fail (compositor->priv->state == INIT);
1469 
1470 	if (set_font_description_from_name (compositor,
1471 					    &compositor->priv->footer_font,
1472 					    font_name))
1473 
1474 	{
1475 		g_object_notify (G_OBJECT (compositor), "footer-font-name");
1476 	}
1477 }
1478 
1479 /**
1480  * gtk_source_print_compositor_get_footer_font_name:
1481  * @compositor: a #GtkSourcePrintCompositor.
1482  *
1483  * Returns the name of the font used to print the page footer.
1484  * The returned string must be freed with g_free().
1485  *
1486  * Return value: a new string containing the name of the font used to print
1487  * the page footer.
1488  *
1489  * Since: 2.2
1490  */
1491 gchar *
gtk_source_print_compositor_get_footer_font_name(GtkSourcePrintCompositor * compositor)1492 gtk_source_print_compositor_get_footer_font_name (GtkSourcePrintCompositor *compositor)
1493 {
1494 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), NULL);
1495 
1496 	if (compositor->priv->footer_font == NULL)
1497 	{
1498 		g_return_val_if_fail (compositor->priv->body_font != NULL, NULL);
1499 		compositor->priv->footer_font = pango_font_description_copy (compositor->priv->body_font);
1500 	}
1501 
1502 	return pango_font_description_to_string (compositor->priv->footer_font);
1503 }
1504 
1505 /**
1506  * gtk_source_print_compositor_set_top_margin:
1507  * @compositor: a #GtkSourcePrintCompositor.
1508  * @margin: the new top margin in units of @unit
1509  * @unit: the units for @margin
1510  *
1511  * Sets the top margin used by @compositor.
1512  *
1513  * Since: 2.2
1514  */
1515 void
gtk_source_print_compositor_set_top_margin(GtkSourcePrintCompositor * compositor,gdouble margin,GtkUnit unit)1516 gtk_source_print_compositor_set_top_margin (GtkSourcePrintCompositor *compositor,
1517 					    gdouble                   margin,
1518 					    GtkUnit                   unit)
1519 {
1520 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1521 
1522 	compositor->priv->margin_top = convert_to_mm (margin, unit);
1523 }
1524 
1525 /**
1526  * gtk_source_print_compositor_get_top_margin:
1527  * @compositor: a #GtkSourcePrintCompositor.
1528  * @unit: the unit for the return value.
1529  *
1530  * Gets the top margin in units of @unit.
1531  *
1532  * Return value: the top margin.
1533  *
1534  * Since: 2.2
1535  */
1536 gdouble
gtk_source_print_compositor_get_top_margin(GtkSourcePrintCompositor * compositor,GtkUnit unit)1537 gtk_source_print_compositor_get_top_margin (GtkSourcePrintCompositor *compositor,
1538 					    GtkUnit                   unit)
1539 {
1540 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), 0);
1541 
1542 	return convert_from_mm (compositor->priv->margin_top, unit);
1543 }
1544 
1545 /**
1546  * gtk_source_print_compositor_set_bottom_margin:
1547  * @compositor: a #GtkSourcePrintCompositor.
1548  * @margin: the new bottom margin in units of @unit.
1549  * @unit: the units for @margin.
1550  *
1551  * Sets the bottom margin used by @compositor.
1552  *
1553  * Since: 2.2
1554  */
1555 void
gtk_source_print_compositor_set_bottom_margin(GtkSourcePrintCompositor * compositor,gdouble margin,GtkUnit unit)1556 gtk_source_print_compositor_set_bottom_margin (GtkSourcePrintCompositor *compositor,
1557 					       gdouble                   margin,
1558 					       GtkUnit                   unit)
1559 {
1560 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1561 
1562 	compositor->priv->margin_bottom = convert_to_mm (margin, unit);
1563 }
1564 
1565 /**
1566  * gtk_source_print_compositor_get_bottom_margin:
1567  * @compositor: a #GtkSourcePrintCompositor.
1568  * @unit: the unit for the return value.
1569  *
1570  * Gets the bottom margin in units of @unit.
1571  *
1572  * Return value: the bottom margin.
1573  *
1574  * Since: 2.2
1575  */
1576 gdouble
gtk_source_print_compositor_get_bottom_margin(GtkSourcePrintCompositor * compositor,GtkUnit unit)1577 gtk_source_print_compositor_get_bottom_margin (GtkSourcePrintCompositor *compositor,
1578 					       GtkUnit                   unit)
1579 {
1580 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), 0);
1581 
1582 	return convert_from_mm (compositor->priv->margin_bottom, unit);
1583 }
1584 
1585 /**
1586  * gtk_source_print_compositor_set_left_margin:
1587  * @compositor: a #GtkSourcePrintCompositor.
1588  * @margin: the new left margin in units of @unit.
1589  * @unit: the units for @margin.
1590  *
1591  * Sets the left margin used by @compositor.
1592  *
1593  * Since: 2.2
1594  */
1595 void
gtk_source_print_compositor_set_left_margin(GtkSourcePrintCompositor * compositor,gdouble margin,GtkUnit unit)1596 gtk_source_print_compositor_set_left_margin (GtkSourcePrintCompositor *compositor,
1597 					     gdouble                   margin,
1598 					     GtkUnit                   unit)
1599 {
1600 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1601 
1602 	compositor->priv->margin_left = convert_to_mm (margin, unit);
1603 }
1604 
1605 /**
1606  * gtk_source_print_compositor_get_left_margin:
1607  * @compositor: a #GtkSourcePrintCompositor.
1608  * @unit: the unit for the return value.
1609  *
1610  * Gets the left margin in units of @unit.
1611  *
1612  * Return value: the left margin
1613  *
1614  * Since: 2.2
1615  */
1616 gdouble
gtk_source_print_compositor_get_left_margin(GtkSourcePrintCompositor * compositor,GtkUnit unit)1617 gtk_source_print_compositor_get_left_margin (GtkSourcePrintCompositor *compositor,
1618 					     GtkUnit                   unit)
1619 {
1620 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), 0);
1621 
1622 	return convert_from_mm (compositor->priv->margin_left, unit);
1623 }
1624 
1625 /**
1626  * gtk_source_print_compositor_set_right_margin:
1627  * @compositor: a #GtkSourcePrintCompositor.
1628  * @margin: the new right margin in units of @unit.
1629  * @unit: the units for @margin.
1630  *
1631  * Sets the right margin used by @compositor.
1632  *
1633  * Since: 2.2
1634  */
1635 void
gtk_source_print_compositor_set_right_margin(GtkSourcePrintCompositor * compositor,gdouble margin,GtkUnit unit)1636 gtk_source_print_compositor_set_right_margin (GtkSourcePrintCompositor *compositor,
1637 					      gdouble                   margin,
1638 					      GtkUnit                   unit)
1639 {
1640 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
1641 
1642 	compositor->priv->margin_right = convert_to_mm (margin, unit);
1643 }
1644 
1645 /**
1646  * gtk_source_print_compositor_get_right_margin:
1647  * @compositor: a #GtkSourcePrintCompositor.
1648  * @unit: the unit for the return value.
1649  *
1650  * Gets the right margin in units of @unit.
1651  *
1652  * Return value: the right margin.
1653  *
1654  * Since: 2.2
1655  */
1656 gdouble
gtk_source_print_compositor_get_right_margin(GtkSourcePrintCompositor * compositor,GtkUnit unit)1657 gtk_source_print_compositor_get_right_margin (GtkSourcePrintCompositor *compositor,
1658 					      GtkUnit                   unit)
1659 {
1660 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), 0);
1661 
1662 	return convert_from_mm (compositor->priv->margin_right, unit);
1663 }
1664 
1665 /**
1666  * gtk_source_print_compositor_get_n_pages:
1667  * @compositor: a #GtkSourcePrintCompositor.
1668  *
1669  * Returns the number of pages in the document or <code>-1</code> if the
1670  * document has not been completely paginated.
1671  *
1672  * Return value: the number of pages in the document or <code>-1</code> if the
1673  * document has not been completely paginated.
1674  *
1675  * Since: 2.2
1676  */
1677 gint
gtk_source_print_compositor_get_n_pages(GtkSourcePrintCompositor * compositor)1678 gtk_source_print_compositor_get_n_pages	(GtkSourcePrintCompositor *compositor)
1679 {
1680 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), -1);
1681 
1682 	if (compositor->priv->state != DONE)
1683 		return -1;
1684 
1685 	return compositor->priv->n_pages;
1686 }
1687 
1688 /* utility functions to deal with coordinates (returns) */
1689 
1690 static gdouble
get_text_x(GtkSourcePrintCompositor * compositor)1691 get_text_x (GtkSourcePrintCompositor *compositor)
1692 {
1693 	gdouble x;
1694 
1695 	x = compositor->priv->real_margin_left;
1696 
1697 	if (compositor->priv->print_line_numbers)
1698 	     x += compositor->priv->line_numbers_width + NUMBERS_TEXT_SEPARATION;
1699 
1700 	return x;
1701 }
1702 
1703 static gdouble
get_text_y(GtkSourcePrintCompositor * compositor)1704 get_text_y (GtkSourcePrintCompositor *compositor)
1705 {
1706 	gdouble y;
1707 
1708 	y = compositor->priv->real_margin_top + compositor->priv->header_height;
1709 
1710 	return y;
1711 }
1712 
1713 static gdouble
get_line_numbers_x(GtkSourcePrintCompositor * compositor)1714 get_line_numbers_x (GtkSourcePrintCompositor *compositor)
1715 {
1716 	gdouble x;
1717 
1718 	x = compositor->priv->real_margin_left;
1719 
1720 	return x;
1721 }
1722 
1723 static gdouble
get_text_width(GtkSourcePrintCompositor * compositor)1724 get_text_width (GtkSourcePrintCompositor *compositor)
1725 {
1726 	gdouble w;
1727 
1728 	w = compositor->priv->paper_width -
1729 	    compositor->priv->real_margin_left -
1730 	    compositor->priv->real_margin_right;
1731 
1732 	if (compositor->priv->print_line_numbers)
1733 		w -= (compositor->priv->line_numbers_width + NUMBERS_TEXT_SEPARATION);
1734 
1735 	if (w < convert_from_mm (50, GTK_UNIT_POINTS)) {
1736 		g_warning ("Printable page width too little.");
1737 		return convert_from_mm (50, GTK_UNIT_POINTS);
1738 	}
1739 
1740 	return w;
1741 }
1742 
1743 static gdouble
get_text_height(GtkSourcePrintCompositor * compositor)1744 get_text_height (GtkSourcePrintCompositor *compositor)
1745 {
1746 	double h;
1747 
1748 	h = compositor->priv->paper_height -
1749 	    compositor->priv->real_margin_top -
1750 	    compositor->priv->real_margin_bottom -
1751 	    compositor->priv->header_height -
1752 	    compositor->priv->footer_height;
1753 
1754 	if (h < convert_from_mm (50, GTK_UNIT_POINTS)) {
1755 		g_warning ("Printable page height too little.");
1756 		return convert_from_mm (50, GTK_UNIT_POINTS);
1757 	}
1758 
1759 	return h;
1760 }
1761 
1762 static gboolean
is_header_to_print(GtkSourcePrintCompositor * compositor)1763 is_header_to_print (GtkSourcePrintCompositor *compositor)
1764 {
1765 	return (compositor->priv->print_header &&
1766 	       ((compositor->priv->header_format_left != NULL) ||
1767 	        (compositor->priv->header_format_center != NULL) ||
1768 	        (compositor->priv->header_format_right != NULL)));
1769 }
1770 
1771 static gboolean
is_footer_to_print(GtkSourcePrintCompositor * compositor)1772 is_footer_to_print (GtkSourcePrintCompositor *compositor)
1773 {
1774 	return (compositor->priv->print_footer &&
1775 	       ((compositor->priv->footer_format_left != NULL) ||
1776 	        (compositor->priv->footer_format_center != NULL) ||
1777 	        (compositor->priv->footer_format_right != NULL)));
1778 }
1779 
1780 static void
set_layout_tab_width(GtkSourcePrintCompositor * compositor,PangoLayout * layout)1781 set_layout_tab_width (GtkSourcePrintCompositor *compositor,
1782 		      PangoLayout              *layout)
1783 {
1784 	gchar *str;
1785 	gint tab_width = 0;
1786 
1787 	str = g_strnfill (compositor->priv->tab_width, ' ');
1788 	pango_layout_set_text (layout, str, -1);
1789 	g_free (str);
1790 
1791 	pango_layout_get_size (layout, &tab_width, NULL);
1792 
1793 	if (tab_width > 0)
1794 	{
1795 		PangoTabArray *tab_array;
1796 
1797 		tab_array = pango_tab_array_new (1, FALSE);
1798 
1799 		pango_tab_array_set_tab (tab_array,
1800 					 0,
1801 					 PANGO_TAB_LEFT,
1802 					 tab_width);
1803 		pango_layout_set_tabs (layout, tab_array);
1804 
1805 		pango_tab_array_free (tab_array);
1806 	}
1807 }
1808 
1809 static void
setup_pango_layouts(GtkSourcePrintCompositor * compositor,GtkPrintContext * context)1810 setup_pango_layouts (GtkSourcePrintCompositor *compositor,
1811 		     GtkPrintContext          *context)
1812 {
1813 	PangoLayout *layout;
1814 
1815 	/* Layout for the text */
1816 	layout = gtk_print_context_create_pango_layout (context);
1817 	pango_layout_set_font_description (layout, compositor->priv->body_font);
1818 
1819 	switch (compositor->priv->wrap_mode)
1820 	{
1821 		case GTK_WRAP_CHAR:
1822 			pango_layout_set_wrap (layout, PANGO_WRAP_CHAR);
1823 			break;
1824 		case GTK_WRAP_WORD:
1825 			pango_layout_set_wrap (layout, PANGO_WRAP_WORD);
1826 			break;
1827 		case GTK_WRAP_WORD_CHAR:
1828 			pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
1829 			break;
1830 		case GTK_WRAP_NONE:
1831 			/* FIXME: hack
1832 			 * Ellipsize the paragraph when text wrapping is disabled.
1833 			 * Another possibility would be to set the width so the text
1834 			 * breaks into multiple lines, and paginate/render just the
1835 			 * first one.
1836 			 * See also Comment #23 by Owen on bug #143874.
1837 			 */
1838 			pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
1839 			break;
1840 		default:
1841 			g_return_if_reached ();
1842 	}
1843 
1844 	set_layout_tab_width (compositor, layout);
1845 
1846 	g_return_if_fail (compositor->priv->layout == NULL);
1847 	compositor->priv->layout = layout;
1848 
1849 	/* Layout for line numbers */
1850 	if (compositor->priv->print_line_numbers > 0)
1851 	{
1852 		layout = gtk_print_context_create_pango_layout (context);
1853 
1854 		if (compositor->priv->line_numbers_font == NULL)
1855 			compositor->priv->line_numbers_font = pango_font_description_copy_static (compositor->priv->body_font);
1856 		pango_layout_set_font_description (layout, compositor->priv->line_numbers_font);
1857 		pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT);
1858 
1859 		g_return_if_fail (compositor->priv->line_numbers_layout == NULL);
1860 		compositor->priv->line_numbers_layout = layout;
1861 	}
1862 
1863 	/* Layout for header */
1864 	if (is_header_to_print (compositor))
1865 	{
1866 		layout = gtk_print_context_create_pango_layout (context);
1867 
1868 		if (compositor->priv->header_font == NULL)
1869 			compositor->priv->header_font = pango_font_description_copy_static (compositor->priv->body_font);
1870 
1871 		pango_layout_set_font_description (layout, compositor->priv->header_font);
1872 
1873 		g_return_if_fail (compositor->priv->header_layout == NULL);
1874 		compositor->priv->header_layout = layout;
1875 	}
1876 
1877 	/* Layout for footer */
1878 	if (is_footer_to_print (compositor))
1879 	{
1880 		layout = gtk_print_context_create_pango_layout (context);
1881 
1882 		if (compositor->priv->footer_font == NULL)
1883 			compositor->priv->footer_font = pango_font_description_copy_static (compositor->priv->body_font);
1884 
1885 		pango_layout_set_font_description (layout, compositor->priv->footer_font);
1886 
1887 		g_return_if_fail (compositor->priv->footer_layout == NULL);
1888 		compositor->priv->footer_layout = layout;
1889 	}
1890 }
1891 
1892 static gchar *
evaluate_format_string(GtkSourcePrintCompositor * compositor,const gchar * format)1893 evaluate_format_string (GtkSourcePrintCompositor *compositor,
1894 			const gchar              *format)
1895 {
1896 	GDateTime *now;
1897 	GString *eval;
1898 	gchar *eval_str, *retval;
1899 	gunichar ch;
1900 
1901 	now = g_date_time_new_now_local ();
1902 
1903 	/* analyze format string and replace the codes we know */
1904 	eval = g_string_new_len (NULL, strlen (format));
1905 	ch = g_utf8_get_char (format);
1906 	while (ch != 0)
1907 	{
1908 		if (ch == '%')
1909 		{
1910 			format = g_utf8_next_char (format);
1911 			ch = g_utf8_get_char (format);
1912 			if (ch == 'N')
1913 				g_string_append_printf (eval, "%d", compositor->priv->current_page + 1);
1914 			else if (ch == 'Q')
1915 				g_string_append_printf (eval, "%d", compositor->priv->n_pages);
1916 			else
1917 			{
1918 				g_string_append_c (eval, '%');
1919 				g_string_append_unichar (eval, ch);
1920 			}
1921 		}
1922 		else
1923 		{
1924 			g_string_append_unichar (eval, ch);
1925 		}
1926 
1927 		format = g_utf8_next_char (format);
1928 		ch = g_utf8_get_char (format);
1929 	}
1930 
1931 	eval_str = g_string_free (eval, FALSE);
1932 	retval = g_date_time_format (now, eval_str);
1933 	g_free (eval_str);
1934 
1935 	g_date_time_unref (now);
1936 
1937 	return retval;
1938 }
1939 
1940 static void
get_layout_size(PangoLayout * layout,double * width,double * height)1941 get_layout_size (PangoLayout *layout,
1942                  double      *width,
1943                  double      *height)
1944 {
1945 	PangoRectangle rect;
1946 
1947 	pango_layout_get_extents (layout, NULL, &rect);
1948 
1949 	if (width)
1950 		*width = (double) rect.width / (double) PANGO_SCALE;
1951 
1952 	if (height)
1953 		*height = (double) rect.height / (double) PANGO_SCALE;
1954 }
1955 
1956 static gsize
get_n_digits(guint n)1957 get_n_digits (guint n)
1958 {
1959 	gsize d = 1;
1960 
1961 	while (n /= 10)
1962 		d++;
1963 
1964 	return d;
1965 }
1966 
1967 static void
calculate_line_numbers_layout_size(GtkSourcePrintCompositor * compositor,GtkPrintContext * context)1968 calculate_line_numbers_layout_size (GtkSourcePrintCompositor *compositor,
1969 				    GtkPrintContext          *context)
1970 {
1971 	gint line_count;
1972 	gint n_digits;
1973 	gchar *str;
1974 
1975 	if (compositor->priv->print_line_numbers == 0)
1976 	{
1977 		compositor->priv->line_numbers_width = 0.0;
1978 		compositor->priv->line_numbers_height = 0.0;
1979 
1980 		DEBUG ({
1981 			g_debug ("line_numbers_width: %f points (%f mm)",
1982 				 compositor->priv->line_numbers_width,
1983 				 convert_to_mm (compositor->priv->line_numbers_width, GTK_UNIT_POINTS));
1984 			g_debug ("line_numbers_height: %f points (%f mm)",
1985 				 compositor->priv->line_numbers_height,
1986 				 convert_to_mm (compositor->priv->line_numbers_height, GTK_UNIT_POINTS));
1987 		});
1988 
1989 		return;
1990 	}
1991 
1992 	line_count = gtk_text_buffer_get_line_count (GTK_TEXT_BUFFER (compositor->priv->buffer));
1993 	n_digits = get_n_digits (line_count);
1994 	str = g_strnfill (n_digits, '9');
1995 	pango_layout_set_text (compositor->priv->line_numbers_layout, str, -1);
1996 	g_free (str);
1997 
1998 	get_layout_size (compositor->priv->line_numbers_layout,
1999 			 &compositor->priv->line_numbers_width,
2000 			 &compositor->priv->line_numbers_height);
2001 
2002 	DEBUG ({
2003 		g_debug ("line_numbers_width: %f points (%f mm)",
2004 			 compositor->priv->line_numbers_width,
2005 			 convert_to_mm (compositor->priv->line_numbers_width, GTK_UNIT_POINTS));
2006 		g_debug ("line_numbers_height: %f points (%f mm)",
2007 			 compositor->priv->line_numbers_height,
2008 			 convert_to_mm (compositor->priv->line_numbers_height, GTK_UNIT_POINTS));
2009 	});
2010 }
2011 
2012 static gdouble
calculate_header_footer_height(GtkSourcePrintCompositor * compositor,GtkPrintContext * context,PangoFontDescription * font,gdouble * d)2013 calculate_header_footer_height (GtkSourcePrintCompositor *compositor,
2014 		                GtkPrintContext          *context,
2015 		                PangoFontDescription     *font,
2016 		                gdouble                  *d)
2017 {
2018 	PangoContext *pango_context;
2019 	PangoFontMetrics* font_metrics;
2020 	gdouble ascent;
2021 	gdouble descent;
2022 
2023 	pango_context = gtk_print_context_create_pango_context (context);
2024 	pango_context_set_font_description (pango_context, font);
2025 
2026 	font_metrics = pango_context_get_metrics (pango_context,
2027 						  font,
2028 						  compositor->priv->language);
2029 
2030 	ascent = (gdouble) pango_font_metrics_get_ascent (font_metrics) / PANGO_SCALE;
2031 	descent = (gdouble) pango_font_metrics_get_descent (font_metrics) / PANGO_SCALE;
2032 
2033 	pango_font_metrics_unref (font_metrics);
2034 	g_object_unref (pango_context);
2035 
2036 	if (d != NULL)
2037 		*d = descent;
2038 
2039 	return HEADER_FOOTER_SIZE_FACTOR * (ascent + descent);
2040 }
2041 
2042 static void
calculate_header_height(GtkSourcePrintCompositor * compositor,GtkPrintContext * context)2043 calculate_header_height (GtkSourcePrintCompositor *compositor,
2044 		         GtkPrintContext          *context)
2045 {
2046 	if (!is_header_to_print(compositor))
2047 	{
2048 		compositor->priv->header_height = 0.0;
2049 
2050 		DEBUG ({
2051 			g_debug ("header_height: %f points (%f mm)",
2052 				 compositor->priv->header_height,
2053 				 convert_to_mm (compositor->priv->header_height, GTK_UNIT_POINTS));
2054 		});
2055 
2056 		return;
2057 	}
2058 
2059 	g_return_if_fail (compositor->priv->header_font != NULL);
2060 
2061 	compositor->priv->header_height = calculate_header_footer_height (compositor,
2062 									  context,
2063 									  compositor->priv->header_font,
2064 									  NULL);
2065 
2066 	DEBUG ({
2067 		g_debug ("header_height: %f points (%f mm)",
2068 			 compositor->priv->header_height,
2069 			 convert_to_mm (compositor->priv->header_height, GTK_UNIT_POINTS));
2070 	});
2071 }
2072 
2073 static void
calculate_footer_height(GtkSourcePrintCompositor * compositor,GtkPrintContext * context)2074 calculate_footer_height (GtkSourcePrintCompositor *compositor,
2075 		         GtkPrintContext          *context)
2076 {
2077 	if (!is_footer_to_print (compositor))
2078 	{
2079 		compositor->priv->footer_height = 0.0;
2080 
2081 		DEBUG ({
2082 			g_debug ("footer_height: %f points (%f mm)",
2083 				 compositor->priv->footer_height,
2084 				 convert_to_mm (compositor->priv->footer_height, GTK_UNIT_POINTS));
2085 		});
2086 
2087 
2088 		return;
2089 	}
2090 
2091 	if (compositor->priv->footer_font == NULL)
2092 		compositor->priv->footer_font = pango_font_description_copy_static (compositor->priv->body_font);
2093 
2094 	compositor->priv->footer_height = calculate_header_footer_height (compositor,
2095 									  context,
2096 									  compositor->priv->footer_font,
2097 									  &compositor->priv->footer_font_descent);
2098 
2099 	DEBUG ({
2100 		g_debug ("footer_height: %f points (%f mm)",
2101 			 compositor->priv->footer_height,
2102 			 convert_to_mm (compositor->priv->footer_height, GTK_UNIT_POINTS));
2103 	});
2104 }
2105 
2106 static void
calculate_page_size_and_margins(GtkSourcePrintCompositor * compositor,GtkPrintContext * context)2107 calculate_page_size_and_margins (GtkSourcePrintCompositor *compositor,
2108 			         GtkPrintContext          *context)
2109 {
2110 	GtkPageSetup *page_setup;
2111 
2112 	/* calculate_line_numbers_layout_size and calculate_header_footer_height
2113 	   functions must be called before calculate_page_size_and_margins */
2114 	g_return_if_fail (compositor->priv->line_numbers_width >= 0.0);
2115 	g_return_if_fail (compositor->priv->header_height >= 0.0);
2116 	g_return_if_fail (compositor->priv->footer_height >= 0.0);
2117 
2118 	page_setup = gtk_print_context_get_page_setup (context);
2119 
2120 	compositor->priv->page_margin_top = gtk_page_setup_get_top_margin (page_setup, GTK_UNIT_POINTS);
2121 	compositor->priv->page_margin_left = gtk_page_setup_get_left_margin (page_setup, GTK_UNIT_POINTS);
2122 
2123 	/* Calculate real margins: the margins specified in the GtkPageSetup object are the "print margins".
2124 	   they are used to determine the minimal size for the layout margins. */
2125 	compositor->priv->real_margin_top = MAX (compositor->priv->page_margin_top,
2126 						 convert_from_mm (compositor->priv->margin_top, GTK_UNIT_POINTS));
2127 	compositor->priv->real_margin_bottom = MAX (gtk_page_setup_get_bottom_margin (page_setup, GTK_UNIT_POINTS),
2128 						    convert_from_mm (compositor->priv->margin_bottom, GTK_UNIT_POINTS));
2129 	compositor->priv->real_margin_left = MAX (compositor->priv->page_margin_left,
2130 						  convert_from_mm (compositor->priv->margin_left, GTK_UNIT_POINTS));
2131 	compositor->priv->real_margin_right = MAX (gtk_page_setup_get_right_margin (page_setup, GTK_UNIT_POINTS),
2132 						   convert_from_mm (compositor->priv->margin_right, GTK_UNIT_POINTS));
2133 
2134 	DEBUG ({
2135 		g_debug ("real_margin_top: %f points (%f mm)",
2136 			 compositor->priv->real_margin_top,
2137 			 convert_to_mm (compositor->priv->real_margin_top, GTK_UNIT_POINTS));
2138 		g_debug ("real_margin_bottom: %f points (%f mm)",
2139 			 compositor->priv->real_margin_bottom,
2140 			 convert_to_mm (compositor->priv->real_margin_bottom, GTK_UNIT_POINTS));
2141 		g_debug ("real_margin_left: %f points (%f mm)",
2142 			 compositor->priv->real_margin_left,
2143 			 convert_to_mm (compositor->priv->real_margin_left, GTK_UNIT_POINTS));
2144 		g_debug ("real_margin_righ: %f points (%f mm)",
2145 			 compositor->priv->real_margin_right,
2146 			 convert_to_mm (compositor->priv->real_margin_right, GTK_UNIT_POINTS));
2147 	});
2148 
2149 	compositor->priv->paper_width = gtk_page_setup_get_paper_width (page_setup, GTK_UNIT_POINTS);
2150 	compositor->priv->paper_height = gtk_page_setup_get_paper_height (page_setup, GTK_UNIT_POINTS);
2151 
2152 	DEBUG ({
2153 		gdouble text_width;
2154 		gdouble text_height;
2155 		g_debug ("paper_width: %f points (%f mm)",
2156 			 compositor->priv->paper_width,
2157 			 convert_to_mm (compositor->priv->paper_width, GTK_UNIT_POINTS));
2158 		g_debug ("paper_heigth: %f points (%f mm)",
2159 			 compositor->priv->paper_height,
2160 			 convert_to_mm (compositor->priv->paper_height, GTK_UNIT_POINTS));
2161 		text_width = get_text_width (compositor);
2162 		text_height = get_text_height (compositor);
2163 		g_debug ("text_width: %f points (%f mm)", text_width, convert_to_mm (text_width, GTK_UNIT_POINTS));
2164 		g_debug ("text_height: %f points (%f mm)", text_height, convert_to_mm (text_height, GTK_UNIT_POINTS));
2165 
2166 	});
2167 }
2168 
2169 /* TODO: maybe we should have a public api to set
2170  * which tags need to be printed and which should not.
2171  * For now we special case bracket matches.
2172  */
2173 static gboolean
ignore_tag(GtkSourcePrintCompositor * compositor,GtkTextTag * tag)2174 ignore_tag (GtkSourcePrintCompositor *compositor,
2175             GtkTextTag               *tag)
2176 {
2177 	GtkTextTag *bm_tag;
2178 
2179 	bm_tag = _gtk_source_buffer_get_bracket_match_tag (compositor->priv->buffer);
2180 	if ((bm_tag != NULL) && (tag == bm_tag))
2181 		return TRUE;
2182 
2183 	return FALSE;
2184 }
2185 
2186 static GSList *
get_iter_attrs(GtkSourcePrintCompositor * compositor,GtkTextIter * iter,GtkTextIter * limit)2187 get_iter_attrs (GtkSourcePrintCompositor *compositor,
2188 		GtkTextIter              *iter,
2189 		GtkTextIter              *limit)
2190 {
2191 	GSList *attrs = NULL;
2192 	GSList *tags;
2193 	PangoAttribute *bg = NULL, *fg = NULL, *style = NULL, *ul = NULL;
2194 	PangoAttribute *weight = NULL, *st = NULL;
2195 
2196 	tags = gtk_text_iter_get_tags (iter);
2197 	gtk_text_iter_forward_to_tag_toggle (iter, NULL);
2198 
2199 	if (gtk_text_iter_compare (iter, limit) > 0)
2200 		*iter = *limit;
2201 
2202 	while (tags)
2203 	{
2204 		GtkTextTag *tag;
2205 		gboolean bg_set, fg_set, style_set, ul_set, weight_set, st_set;
2206 
2207 		tag = tags->data;
2208 		tags = g_slist_delete_link (tags, tags);
2209 
2210 		if (ignore_tag (compositor, tag))
2211 			continue;
2212 
2213 		g_object_get (tag,
2214 			     "background-set", &bg_set,
2215 			     "foreground-set", &fg_set,
2216 			     "style-set", &style_set,
2217 			     "underline-set", &ul_set,
2218 			     "weight-set", &weight_set,
2219 			     "strikethrough-set", &st_set,
2220 			     NULL);
2221 
2222 		if (bg_set)
2223 		{
2224 			GdkRGBA *color = NULL;
2225 
2226 			if (bg != NULL)
2227 			{
2228 				pango_attribute_destroy (bg);
2229 			}
2230 
2231 			g_object_get (tag, "background-rgba", &color, NULL);
2232 			bg = pango_attr_background_new (color->red * 65535,
2233 							color->green * 65535,
2234 							color->blue * 65535);
2235 			gdk_rgba_free (color);
2236 		}
2237 
2238 		if (fg_set)
2239 		{
2240 			GdkRGBA *color = NULL;
2241 
2242 			if (fg != NULL)
2243 			{
2244 				pango_attribute_destroy (fg);
2245 			}
2246 
2247 			g_object_get (tag, "foreground-rgba", &color, NULL);
2248 			fg = pango_attr_foreground_new (color->red * 65535,
2249 							color->green * 65535,
2250 							color->blue * 65535);
2251 			gdk_rgba_free (color);
2252 		}
2253 
2254 		if (style_set)
2255 		{
2256 			PangoStyle style_value;
2257 			if (style) pango_attribute_destroy (style);
2258 			g_object_get (tag, "style", &style_value, NULL);
2259 			style = pango_attr_style_new (style_value);
2260 		}
2261 
2262 		if (ul_set)
2263 		{
2264 			PangoUnderline underline;
2265 			if (ul) pango_attribute_destroy (ul);
2266 			g_object_get (tag, "underline", &underline, NULL);
2267 			ul = pango_attr_underline_new (underline);
2268 		}
2269 
2270 		if (weight_set)
2271 		{
2272 			PangoWeight weight_value;
2273 			if (weight) pango_attribute_destroy (weight);
2274 			g_object_get (tag, "weight", &weight_value, NULL);
2275 			weight = pango_attr_weight_new (weight_value);
2276 		}
2277 
2278 		if (st_set)
2279 		{
2280 			gboolean strikethrough;
2281 			if (st) pango_attribute_destroy (st);
2282 			g_object_get (tag, "strikethrough", &strikethrough, NULL);
2283 			st = pango_attr_strikethrough_new (strikethrough);
2284 		}
2285 	}
2286 
2287 	if (bg)
2288 		attrs = g_slist_prepend (attrs, bg);
2289 	if (fg)
2290 		attrs = g_slist_prepend (attrs, fg);
2291 	if (style)
2292 		attrs = g_slist_prepend (attrs, style);
2293 	if (ul)
2294 		attrs = g_slist_prepend (attrs, ul);
2295 	if (weight)
2296 		attrs = g_slist_prepend (attrs, weight);
2297 	if (st)
2298 		attrs = g_slist_prepend (attrs, st);
2299 
2300 	return attrs;
2301 }
2302 
2303 static gboolean
is_empty_line(const gchar * text)2304 is_empty_line (const gchar *text)
2305 {
2306 	if (*text != '\0')
2307 	{
2308 		const gchar *p;
2309 
2310 		for (p = text; p != NULL; p = g_utf8_next_char (p))
2311 		{
2312 			if (!g_unichar_isspace (*p))
2313 			{
2314 				return FALSE;
2315 			}
2316 		}
2317 	}
2318 
2319 	return TRUE;
2320 }
2321 
2322 static void
layout_paragraph(GtkSourcePrintCompositor * compositor,GtkTextIter * start,GtkTextIter * end)2323 layout_paragraph (GtkSourcePrintCompositor *compositor,
2324 		  GtkTextIter              *start,
2325 		  GtkTextIter              *end)
2326 {
2327 	gchar *text;
2328 
2329 	text = gtk_text_iter_get_slice (start, end);
2330 
2331 	/* If it is an empty line (or it just contains tabs) pango has problems:
2332 	 * see for instance comment #22 and #23 on bug #143874 and bug #457990.
2333 	 * We just hack around it by inserting a space... not elegant but
2334 	 * works :-) */
2335 	if (gtk_text_iter_ends_line (start) ||
2336 	    is_empty_line (text))
2337 	{
2338 		pango_layout_set_text (compositor->priv->layout, " ", 1);
2339 		g_free (text);
2340 		return;
2341 	}
2342 
2343 	pango_layout_set_text (compositor->priv->layout, text, -1);
2344 	g_free (text);
2345 
2346 	if (compositor->priv->highlight_syntax)
2347 	{
2348 		PangoAttrList *attr_list = NULL;
2349 		GtkTextIter segm_start, segm_end;
2350 		int start_index;
2351 
2352 		/* Make sure it is highlighted even if it was not shown yet */
2353 		gtk_source_buffer_ensure_highlight (compositor->priv->buffer,
2354 						    start,
2355 						    end);
2356 
2357 		segm_start = *start;
2358 		start_index = gtk_text_iter_get_line_index (start);
2359 
2360 		while (gtk_text_iter_compare (&segm_start, end) < 0)
2361 		{
2362 			GSList *attrs;
2363 			int si, ei;
2364 
2365 			segm_end = segm_start;
2366 			attrs = get_iter_attrs (compositor, &segm_end, end);
2367 			if (attrs)
2368 			{
2369 				si = gtk_text_iter_get_line_index (&segm_start) - start_index;
2370 				ei = gtk_text_iter_get_line_index (&segm_end) - start_index;
2371 			}
2372 
2373 			while (attrs)
2374 			{
2375 				PangoAttribute *a = attrs->data;
2376 
2377 				a->start_index = si;
2378 				a->end_index = ei;
2379 
2380 				if (!attr_list)
2381 					attr_list = pango_attr_list_new ();
2382 
2383 				pango_attr_list_insert (attr_list, a);
2384 
2385 				attrs = g_slist_delete_link (attrs, attrs);
2386 			}
2387 
2388 			segm_start = segm_end;
2389 		}
2390 
2391 		pango_layout_set_attributes (compositor->priv->layout,
2392 					     attr_list);
2393 
2394 		if (attr_list)
2395 			pango_attr_list_unref (attr_list);
2396 	}
2397 }
2398 
2399 static gboolean
line_is_numbered(GtkSourcePrintCompositor * compositor,gint line_number)2400 line_is_numbered (GtkSourcePrintCompositor *compositor,
2401 		  gint                      line_number)
2402 {
2403 	return (compositor->priv->print_line_numbers > 0) &&
2404 	       ((line_number + 1) % compositor->priv->print_line_numbers == 0);
2405 }
2406 
2407 static void
set_pango_layouts_width(GtkSourcePrintCompositor * compositor)2408 set_pango_layouts_width (GtkSourcePrintCompositor *compositor)
2409 {
2410 	g_return_if_fail (compositor->priv->layout != NULL);
2411 	pango_layout_set_width (compositor->priv->layout,
2412 				get_text_width (compositor) * PANGO_SCALE);
2413 
2414 	if (compositor->priv->print_line_numbers)
2415 	{
2416 		g_return_if_fail (compositor->priv->line_numbers_layout != NULL);
2417 		pango_layout_set_width (compositor->priv->line_numbers_layout,
2418 					compositor->priv->line_numbers_width * PANGO_SCALE);
2419 	}
2420 }
2421 
2422 /* If you want
2423    to use the ::paginate signal to perform pagination in async way, it is suggested to
2424    ensure the buffer is not modified until pagination terminates. */
2425 
2426 /**
2427  * gtk_source_print_compositor_paginate:
2428  * @compositor: a #GtkSourcePrintCompositor.
2429  * @context: the #GtkPrintContext whose parameters (e.g. paper size, print margins, etc.)
2430  * are used by the the @compositor to paginate the document.
2431  *
2432  * Paginate the document associated with the @compositor.
2433  *
2434  * In order to support non-blocking pagination, document is paginated in small chunks.
2435  * Each time gtk_source_print_compositor_paginate() is invoked, a chunk of the document
2436  * is paginated. To paginate the entire document, gtk_source_print_compositor_paginate()
2437  * must be invoked multiple times.
2438  * It returns %TRUE if the document has been completely paginated, otherwise it returns %FALSE.
2439  *
2440  * This method has been designed to be invoked in the handler of the #GtkPrintOperation::paginate signal,
2441  * as shown in the following example:
2442  *
2443  * <informalexample><programlisting>
2444  * // Signal handler for the GtkPrintOperation::paginate signal
2445  *
2446  * static gboolean
2447  * paginate (GtkPrintOperation *operation,
2448  *           GtkPrintContext   *context,
2449  *           gpointer           user_data)
2450  * {
2451  *     GtkSourcePrintCompositor *compositor;
2452  *
2453  *     compositor = GTK_SOURCE_PRINT_COMPOSITOR (user_data);
2454  *
2455  *     if (gtk_source_print_compositor_paginate (compositor, context))
2456  *     {
2457  *         gint n_pages;
2458  *
2459  *         n_pages = gtk_source_print_compositor_get_n_pages (compositor);
2460  *         gtk_print_operation_set_n_pages (operation, n_pages);
2461  *
2462  *         return TRUE;
2463  *     }
2464  *
2465  *     return FALSE;
2466  * }
2467  * </programlisting></informalexample>
2468  *
2469  * If you don't need to do pagination in chunks, you can simply do it all in the
2470  * #GtkPrintOperation::begin-print handler, and set the number of pages from there, like
2471  * in the following example:
2472  *
2473  * <informalexample><programlisting>
2474  * // Signal handler for the GtkPrintOperation::begin-print signal
2475  *
2476  * static void
2477  * begin_print (GtkPrintOperation *operation,
2478  *              GtkPrintContext   *context,
2479  *              gpointer           user_data)
2480  * {
2481  *     GtkSourcePrintCompositor *compositor;
2482  *     gint n_pages;
2483  *
2484  *     compositor = GTK_SOURCE_PRINT_COMPOSITOR (user_data);
2485  *
2486  *     while (!gtk_source_print_compositor_paginate (compositor, context));
2487  *
2488  *     n_pages = gtk_source_print_compositor_get_n_pages (compositor);
2489  *     gtk_print_operation_set_n_pages (operation, n_pages);
2490  * }
2491  * </programlisting></informalexample>
2492  *
2493  * Return value: %TRUE if the document has been completely paginated, %FALSE otherwise.
2494  *
2495  * Since: 2.2
2496  */
2497 gboolean
gtk_source_print_compositor_paginate(GtkSourcePrintCompositor * compositor,GtkPrintContext * context)2498 gtk_source_print_compositor_paginate (GtkSourcePrintCompositor *compositor,
2499 				      GtkPrintContext          *context)
2500 {
2501 	GtkTextIter start, end;
2502 	gint page_start_offset;
2503 	double text_height;
2504 	double cur_height;
2505 
2506 	gboolean done;
2507 	gint pages_count;
2508 
2509 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), TRUE);
2510 	g_return_val_if_fail (GTK_IS_PRINT_CONTEXT (context), TRUE);
2511 
2512 	if (compositor->priv->state == DONE)
2513 		return TRUE;
2514 
2515 	if (compositor->priv->state == INIT)
2516 	{
2517 		PROFILE ({
2518 			if (pagination_timer != NULL)
2519 				g_timer_destroy (pagination_timer);
2520 
2521 			pagination_timer = g_timer_new ();
2522 		});
2523 
2524 		g_return_val_if_fail (compositor->priv->pages == NULL, TRUE);
2525 
2526 		compositor->priv->pages = g_array_new (FALSE, FALSE, sizeof (gint));
2527 
2528 		setup_pango_layouts (compositor, context);
2529 
2530 		calculate_line_numbers_layout_size (compositor, context);
2531 		calculate_footer_height (compositor, context);
2532 		calculate_header_height (compositor, context);
2533 		calculate_page_size_and_margins (compositor, context);
2534 
2535 		/* Set layouts width otherwise "aligh right" does not work as expected */
2536 		/* Cannot be done when setting up layouts since we need the width */
2537 		set_pango_layouts_width (compositor);
2538 
2539 		compositor->priv->state = PAGINATING;
2540 	}
2541 
2542 	g_return_val_if_fail (compositor->priv->state == PAGINATING, FALSE);
2543 	g_return_val_if_fail (compositor->priv->layout != NULL, FALSE);
2544 
2545 	if (compositor->priv->pagination_mark == NULL)
2546 	{
2547 		gtk_text_buffer_get_start_iter (GTK_TEXT_BUFFER (compositor->priv->buffer), &start);
2548 
2549 		compositor->priv->pagination_mark = gtk_text_buffer_create_mark (GTK_TEXT_BUFFER (compositor->priv->buffer),
2550 										 NULL,
2551 										 &start,
2552 										 TRUE);
2553 
2554 		/* add the first page start */
2555 		page_start_offset = gtk_text_iter_get_offset (&start);
2556 		g_array_append_val (compositor->priv->pages, page_start_offset);
2557 	}
2558 	else
2559 	{
2560 		gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (compositor->priv->buffer),
2561 						  &start,
2562 						  compositor->priv->pagination_mark);
2563 	}
2564 
2565 	DEBUG ({
2566 		g_debug ("Start paginating at %d", gtk_text_iter_get_offset (&start));
2567 	});
2568 
2569 	gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (compositor->priv->buffer), &end);
2570 
2571 	cur_height = 0;
2572 	text_height = get_text_height (compositor);
2573 
2574 	done = gtk_text_iter_compare (&start, &end) >= 0;
2575 	pages_count = 0;
2576 
2577 	while (!done && (pages_count < PAGINATION_CHUNK_SIZE))
2578 	{
2579 		gint line_number;
2580 		GtkTextIter line_end;
2581 		gdouble line_height;
2582 
2583 		line_number = gtk_text_iter_get_line (&start);
2584 
2585 		line_end = start;
2586 		if (!gtk_text_iter_ends_line (&line_end))
2587 			gtk_text_iter_forward_to_line_end (&line_end);
2588 
2589 		layout_paragraph (compositor, &start, &line_end);
2590 
2591 		get_layout_size (compositor->priv->layout, NULL, &line_height);
2592 
2593 		if (line_is_numbered (compositor, line_number))
2594 		{
2595 			g_assert (compositor->priv->line_numbers_height > 0);
2596 
2597 			line_height = MAX (line_height,
2598 					   compositor->priv->line_numbers_height);
2599 		}
2600 
2601 #define EPS (.1)
2602 		if (cur_height + line_height > text_height + EPS)
2603 		{
2604 			/* if we have multiline paragraphs, see how much of
2605 			 * it we can fit in the current page */
2606 			if (compositor->priv->wrap_mode != GTK_WRAP_NONE &&
2607 			    pango_layout_get_line_count (compositor->priv->layout) > 1)
2608 			{
2609 				PangoLayoutIter *layout_iter;
2610 				PangoRectangle logical_rect;
2611 				gboolean is_first_line = TRUE;
2612 				double part_height = 0;
2613 				gint idx;
2614 
2615 				layout_iter = pango_layout_get_iter (compositor->priv->layout);
2616 
2617 				do
2618 				{
2619 					double layout_line_height;
2620 
2621 					pango_layout_iter_get_line_extents (layout_iter, NULL, &logical_rect);
2622 					layout_line_height = (double) logical_rect.height / PANGO_SCALE;
2623 
2624 					if (is_first_line &&
2625 					    line_is_numbered (compositor, line_number))
2626 					{
2627 						layout_line_height = MAX (compositor->priv->line_numbers_height,
2628 									  layout_line_height);
2629 					}
2630 
2631 					if (cur_height + part_height + layout_line_height > text_height + EPS)
2632 						break;
2633 
2634 					part_height += layout_line_height;
2635 					is_first_line = FALSE;
2636 				}
2637 				while (pango_layout_iter_next_line (layout_iter));
2638 
2639 				/* move our start iter to the page break:
2640 				 * note that text_iter_set_index mesures from
2641 				 * the start of the line, while our layout
2642 				 * may start in the middle of a line, so we have
2643 				 * to add.
2644 				 */
2645 				idx = gtk_text_iter_get_line_index (&start);
2646 				idx += pango_layout_iter_get_index (layout_iter);
2647 				gtk_text_iter_set_line_index (&start, idx);
2648 
2649 				pango_layout_iter_free (layout_iter);
2650 
2651 				page_start_offset = gtk_text_iter_get_offset (&start);
2652 
2653 				gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (compositor->priv->buffer),
2654 							   compositor->priv->pagination_mark,
2655 							   &start);
2656 
2657 				/* if the remainder fits on the next page, go
2658 				 * on to the next line, otherwise restart pagination
2659 				 * from the page break we found */
2660 				if (line_height - part_height > text_height + EPS)
2661 				{
2662 					cur_height = 0;
2663 				}
2664 				else
2665 				{
2666 					/* reset cur_height for the next page */
2667 					cur_height = line_height - part_height;
2668 					gtk_text_iter_forward_line (&start);
2669 				}
2670 			}
2671 			else
2672 			{
2673 				page_start_offset = gtk_text_iter_get_offset (&start);
2674 
2675 				gtk_text_buffer_move_mark (GTK_TEXT_BUFFER (compositor->priv->buffer),
2676 							   compositor->priv->pagination_mark,
2677 							   &start);
2678 
2679 				/* reset cur_height for the next page */
2680 				cur_height = line_height;
2681 				gtk_text_iter_forward_line (&start);
2682 			}
2683 
2684 			/* store the start of the new page */
2685 			g_array_append_val (compositor->priv->pages,
2686 					    page_start_offset);
2687 
2688 			++pages_count;
2689 		}
2690 		else
2691 		{
2692 			cur_height += line_height;
2693 			gtk_text_iter_forward_line (&start);
2694 		}
2695 
2696 		done = gtk_text_iter_compare (&start, &end) >= 0;
2697 	}
2698 #undef EPS
2699 
2700 	if (done)
2701 	{
2702 		PROFILE ({
2703 			g_debug ("Paginated in %f seconds:\n", g_timer_elapsed (pagination_timer, NULL));
2704 
2705 			g_timer_destroy (pagination_timer);
2706 			pagination_timer = NULL;
2707 		});
2708 
2709 		DEBUG ({
2710 			int i;
2711 
2712 			for (i = 0; i < compositor->priv->pages->len; i += 1)
2713 			{
2714 				gint offset;
2715 				GtkTextIter iter;
2716 
2717 				offset = g_array_index (compositor->priv->pages, int, i);
2718 				gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (compositor->priv->buffer), &iter, offset);
2719 
2720 				g_debug ("  page %d starts at line %d (offset %d)\n", i, gtk_text_iter_get_line (&iter), offset);
2721 			}
2722 		});
2723 
2724 		compositor->priv->state = DONE;
2725 
2726 		compositor->priv->n_pages = compositor->priv->pages->len;
2727 
2728 		/* Remove the pagination mark */
2729 		gtk_text_buffer_delete_mark (GTK_TEXT_BUFFER (compositor->priv->buffer),
2730 					     compositor->priv->pagination_mark);
2731 		compositor->priv->pagination_mark = NULL;
2732 	}
2733 
2734 	return (done != FALSE);
2735 }
2736 
2737 /**
2738  * gtk_source_print_compositor_get_pagination_progress:
2739  * @compositor: a #GtkSourcePrintCompositor.
2740  *
2741  * Returns the current fraction of the document pagination that has been completed.
2742  *
2743  * Return value: a fraction from 0.0 to 1.0 inclusive.
2744  *
2745  * Since: 2.2
2746  */
2747 gdouble
gtk_source_print_compositor_get_pagination_progress(GtkSourcePrintCompositor * compositor)2748 gtk_source_print_compositor_get_pagination_progress (GtkSourcePrintCompositor *compositor)
2749 {
2750 	GtkTextIter current;
2751 	gint char_count;
2752 
2753 	g_return_val_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor), 0.0);
2754 
2755 	if (compositor->priv->state == INIT)
2756 		return 0.0;
2757 
2758 	if (compositor->priv->state == DONE)
2759 		return 1.0;
2760 
2761 	char_count = gtk_text_buffer_get_char_count (GTK_TEXT_BUFFER (compositor->priv->buffer));
2762 	if (char_count == 0)
2763 		return 1.0;
2764 
2765 	g_return_val_if_fail (compositor->priv->pagination_mark != NULL, 0.0);
2766 
2767 	gtk_text_buffer_get_iter_at_mark (GTK_TEXT_BUFFER (compositor->priv->buffer),
2768 					  &current,
2769 					  compositor->priv->pagination_mark);
2770 
2771 	return (gdouble) gtk_text_iter_get_offset (&current) / (gdouble) char_count;
2772 }
2773 
2774 static void
print_header_string(GtkSourcePrintCompositor * compositor,cairo_t * cr,PangoAlignment alignment,const gchar * format)2775 print_header_string (GtkSourcePrintCompositor *compositor,
2776 		     cairo_t                  *cr,
2777 		     PangoAlignment            alignment,
2778 		     const gchar              *format)
2779 {
2780 	gchar *text;
2781 
2782 	text = evaluate_format_string (compositor, format);
2783 	if (text != NULL)
2784 	{
2785 		PangoLayoutLine* line;
2786 		gdouble	baseline;
2787 		PangoLayoutIter *iter;
2788 
2789 		gdouble layout_width;
2790 		gdouble layout_height;
2791 		gdouble header_width;
2792 		gdouble x;
2793 
2794 		header_width = compositor->priv->paper_width -
2795 			       compositor->priv->real_margin_left -
2796 			       compositor->priv->real_margin_right;
2797 
2798 		pango_layout_set_text (compositor->priv->header_layout, text, -1);
2799 
2800 		/* Print only the first line */
2801 		iter = pango_layout_get_iter (compositor->priv->header_layout);
2802 		baseline = (gdouble) pango_layout_iter_get_baseline (iter) / (gdouble) PANGO_SCALE;
2803 
2804 		get_layout_size (compositor->priv->header_layout, &layout_width, &layout_height);
2805 
2806 		switch (alignment)
2807 		{
2808 			case PANGO_ALIGN_RIGHT:
2809 				x = compositor->priv->real_margin_left + header_width - layout_width;
2810 				break;
2811 
2812 			case PANGO_ALIGN_CENTER:
2813 				x = compositor->priv->real_margin_left + header_width / 2 - layout_width / 2;
2814 				break;
2815 
2816 			case PANGO_ALIGN_LEFT:
2817 			default:
2818 				x = compositor->priv->real_margin_left;
2819 				break;
2820 		}
2821 
2822 		DEBUG ({
2823 			cairo_save (cr);
2824 
2825 			cairo_set_line_width (cr, 1.);
2826 			cairo_set_source_rgb (cr, 0., 0., 1.);
2827 			cairo_rectangle (cr,
2828 					 x,
2829 					 compositor->priv->real_margin_top,
2830 					 layout_width,
2831 					 layout_height);
2832 			cairo_stroke (cr);
2833 
2834 			cairo_restore (cr);
2835 		});
2836 
2837 		line = pango_layout_iter_get_line_readonly (iter);
2838 
2839 		cairo_move_to (cr,
2840 			       x,
2841 			       compositor->priv->real_margin_top + baseline);
2842 
2843 		pango_cairo_show_layout_line (cr, line);
2844 
2845 		pango_layout_iter_free (iter);
2846 		g_free (text);
2847 	}
2848 }
2849 
2850 static void
print_header(GtkSourcePrintCompositor * compositor,cairo_t * cr)2851 print_header (GtkSourcePrintCompositor *compositor,
2852 	      cairo_t                  *cr)
2853 {
2854 	pango_cairo_update_layout (cr, compositor->priv->header_layout);
2855 
2856 	/* left format */
2857 	if (compositor->priv->header_format_left != NULL)
2858 		print_header_string (compositor,
2859 				     cr,
2860 				     PANGO_ALIGN_LEFT,
2861 				     compositor->priv->header_format_left);
2862 
2863 	/* right format */
2864 	if (compositor->priv->header_format_right != NULL)
2865 		print_header_string (compositor,
2866 				     cr,
2867 				     PANGO_ALIGN_RIGHT,
2868 				     compositor->priv->header_format_right);
2869 
2870 	/* center format */
2871 	if (compositor->priv->header_format_center != NULL)
2872 		print_header_string (compositor,
2873 				     cr,
2874 				     PANGO_ALIGN_CENTER,
2875 				     compositor->priv->header_format_center);
2876 
2877 	/* separator */
2878 	if (compositor->priv->header_separator)
2879 	{
2880 		gdouble y = compositor->priv->real_margin_top +
2881 			    (1 - SEPARATOR_SPACING_FACTOR) * compositor->priv->header_height;
2882 
2883 		cairo_save (cr);
2884 
2885 		cairo_move_to (cr, compositor->priv->real_margin_left, y);
2886 		cairo_set_line_width (cr, SEPARATOR_LINE_WIDTH);
2887 		cairo_line_to (cr, compositor->priv->paper_width - compositor->priv->real_margin_right, y);
2888 		cairo_stroke (cr);
2889 
2890 		cairo_restore (cr);
2891 	}
2892 }
2893 
2894 static void
print_footer_string(GtkSourcePrintCompositor * compositor,cairo_t * cr,PangoAlignment alignment,const gchar * format)2895 print_footer_string (GtkSourcePrintCompositor *compositor,
2896 		     cairo_t                  *cr,
2897 		     PangoAlignment            alignment,
2898 		     const gchar              *format)
2899 {
2900 	gchar *text;
2901 
2902 	text = evaluate_format_string (compositor, format);
2903 	if (text != NULL)
2904 	{
2905 		PangoLayoutLine* line;
2906 
2907 		gdouble layout_width;
2908 		gdouble layout_height;
2909 		gdouble footer_width;
2910 		gdouble x;
2911 
2912 		footer_width = compositor->priv->paper_width -
2913 			       compositor->priv->real_margin_left -
2914 			       compositor->priv->real_margin_right;
2915 
2916 		pango_layout_set_text (compositor->priv->footer_layout, text, -1);
2917 
2918 		get_layout_size (compositor->priv->footer_layout, &layout_width, &layout_height);
2919 
2920 		switch (alignment)
2921 		{
2922 			case PANGO_ALIGN_RIGHT:
2923 				x = compositor->priv->real_margin_left + footer_width - layout_width;
2924 				break;
2925 
2926 			case PANGO_ALIGN_CENTER:
2927 				x = compositor->priv->real_margin_left + footer_width / 2 - layout_width / 2;
2928 				break;
2929 
2930 			case PANGO_ALIGN_LEFT:
2931 			default:
2932 				x = compositor->priv->real_margin_left;
2933 				break;
2934 		}
2935 		/* Print only the first line */
2936 		line = pango_layout_get_line (compositor->priv->footer_layout, 0);
2937 
2938 		DEBUG ({
2939 			gdouble w;
2940 			gdouble h;
2941 
2942 			get_layout_size (compositor->priv->footer_layout, &w, &h);
2943 
2944 			cairo_save (cr);
2945 			cairo_set_line_width (cr, 1.);
2946 			cairo_set_source_rgb (cr, 0., 0., 1.);
2947 			cairo_rectangle (cr,
2948 					 x,
2949 					 compositor->priv->paper_height - compositor->priv->real_margin_bottom - h,
2950 					 layout_width,
2951 					 layout_height);
2952 			cairo_stroke (cr);
2953 			cairo_restore (cr);
2954 		});
2955 
2956 		cairo_move_to (cr,
2957 			       x,
2958 			       compositor->priv->paper_height -
2959 			       	compositor->priv->real_margin_bottom - compositor->priv->footer_font_descent);
2960 
2961 		pango_cairo_show_layout_line (cr, line);
2962 
2963 		g_free (text);
2964 	}
2965 }
2966 
2967 static void
print_footer(GtkSourcePrintCompositor * compositor,cairo_t * cr)2968 print_footer (GtkSourcePrintCompositor *compositor,
2969 	      cairo_t                  *cr)
2970 {
2971 	pango_cairo_update_layout (cr, compositor->priv->footer_layout);
2972 
2973 	/* left format */
2974 	if (compositor->priv->footer_format_left != NULL)
2975 		print_footer_string (compositor,
2976 				     cr,
2977 				     PANGO_ALIGN_LEFT,
2978 				     compositor->priv->footer_format_left);
2979 
2980 	/* right format */
2981 	if (compositor->priv->footer_format_right != NULL)
2982 		print_footer_string (compositor,
2983 				     cr,
2984 				     PANGO_ALIGN_RIGHT,
2985 				     compositor->priv->footer_format_right);
2986 
2987 	/* center format */
2988 	if (compositor->priv->footer_format_center != NULL)
2989 		print_footer_string (compositor,
2990 				     cr,
2991 				     PANGO_ALIGN_CENTER,
2992 				     compositor->priv->footer_format_center);
2993 
2994 	/* separator */
2995 	if (compositor->priv->footer_separator)
2996 	{
2997 		gdouble y = compositor->priv->paper_height -
2998 			    compositor->priv->real_margin_bottom -
2999 			    (1 - SEPARATOR_SPACING_FACTOR) * compositor->priv->footer_height;
3000 
3001 		cairo_save (cr);
3002 
3003 		cairo_move_to (cr, compositor->priv->real_margin_left, y);
3004 		cairo_set_line_width (cr, SEPARATOR_LINE_WIDTH);
3005 		cairo_line_to (cr, compositor->priv->paper_width - compositor->priv->real_margin_right, y);
3006 		cairo_stroke (cr);
3007 
3008 		cairo_restore (cr);
3009 	}
3010 }
3011 
3012 /**
3013  * gtk_source_print_compositor_draw_page:
3014  * @compositor: a #GtkSourcePrintCompositor.
3015  * @context: the #GtkPrintContext encapsulating the context information that is required when
3016  *           drawing the page for printing.
3017  * @page_nr: the number of the page to print.
3018  *
3019  * Draw page @page_nr for printing on the the Cairo context encapsuled in @context.
3020  *
3021  * This method has been designed to be called in the handler of the #GtkPrintOperation::draw_page signal
3022  * as shown in the following example:
3023  *
3024  * <informalexample><programlisting>
3025  * // Signal handler for the GtkPrintOperation::draw_page signal
3026  *
3027  * static void
3028  * draw_page (GtkPrintOperation *operation,
3029  *            GtkPrintContext   *context,
3030  *            gint               page_nr,
3031  *            gpointer           user_data)
3032  * {
3033  *     GtkSourcePrintCompositor *compositor;
3034  *
3035  *     compositor = GTK_SOURCE_PRINT_COMPOSITOR (user_data);
3036  *
3037  *     gtk_source_print_compositor_draw_page (compositor,
3038  *                                            context,
3039  *                                            page_nr);
3040  * }
3041  * </programlisting></informalexample>
3042  */
3043 void
gtk_source_print_compositor_draw_page(GtkSourcePrintCompositor * compositor,GtkPrintContext * context,gint page_nr)3044 gtk_source_print_compositor_draw_page (GtkSourcePrintCompositor *compositor,
3045 				       GtkPrintContext          *context,
3046 				       gint                      page_nr)
3047 {
3048 	cairo_t *cr;
3049 	GtkTextIter start, end;
3050 	gint offset;
3051 	double x, y, ln_x;
3052 
3053 	g_return_if_fail (GTK_SOURCE_IS_PRINT_COMPOSITOR (compositor));
3054 	g_return_if_fail (GTK_IS_PRINT_CONTEXT (context));
3055 	g_return_if_fail (page_nr >= 0);
3056 
3057 	compositor->priv->current_page = page_nr;
3058 
3059 	cr = gtk_print_context_get_cairo_context (context);
3060 	cairo_set_source_rgb (cr, 0, 0, 0);
3061 	cairo_translate (cr,
3062 			 -1 * compositor->priv->page_margin_left,
3063 			 -1 * compositor->priv->page_margin_top);
3064 
3065 	if (is_header_to_print (compositor))
3066 	{
3067 		print_header (compositor, cr);
3068 	}
3069 
3070 	if (is_footer_to_print (compositor))
3071 	{
3072 		print_footer (compositor, cr);
3073 	}
3074 
3075 	x = get_text_x (compositor);
3076 	y = get_text_y (compositor);
3077 	ln_x = get_line_numbers_x (compositor);
3078 
3079 	DEBUG ({
3080 		cairo_save (cr);
3081 
3082 		cairo_set_line_width (cr, 1.);
3083 		cairo_set_source_rgb (cr, 0., 0., 1.);
3084 		cairo_rectangle (cr,
3085 				 compositor->priv->real_margin_left,
3086 				 compositor->priv->real_margin_top,
3087 				 compositor->priv->paper_width -
3088 				 	compositor->priv->real_margin_left - compositor->priv->real_margin_right,
3089 				 compositor->priv->paper_height -
3090 				 	compositor->priv->real_margin_top - compositor->priv->real_margin_bottom);
3091 		cairo_stroke (cr);
3092 
3093 		cairo_set_source_rgb (cr, 1., 0., 0.);
3094 		cairo_rectangle (cr,
3095 				 ln_x, y,
3096 				 compositor->priv->line_numbers_width,
3097 				 get_text_height (compositor));
3098 		cairo_stroke (cr);
3099 
3100 		cairo_set_source_rgb (cr, 0., 1., 0.);
3101 		cairo_rectangle (cr,
3102 				 x, y,
3103 				 get_text_width (compositor),
3104 				 get_text_height (compositor));
3105 		cairo_stroke (cr);
3106 
3107 		cairo_set_source_rgb (cr, 1., 0., 0.);
3108 		cairo_rectangle (cr,
3109 				 0, 0,
3110 				 compositor->priv->paper_width,
3111 				 compositor->priv->paper_height);
3112 		cairo_stroke (cr);
3113 
3114 		cairo_restore (cr);
3115 	});
3116 
3117 	g_return_if_fail (compositor->priv->layout != NULL);
3118 	pango_cairo_update_layout (cr, compositor->priv->layout);
3119 
3120 	if (compositor->priv->print_line_numbers)
3121 	{
3122 		g_return_if_fail (compositor->priv->line_numbers_layout != NULL);
3123 		pango_cairo_update_layout (cr, compositor->priv->line_numbers_layout);
3124 	}
3125 
3126 	g_return_if_fail (compositor->priv->buffer != NULL);
3127 	g_return_if_fail (compositor->priv->pages != NULL);
3128 	g_return_if_fail ((guint) page_nr < compositor->priv->pages->len);
3129 
3130 	offset = g_array_index (compositor->priv->pages, int, page_nr);
3131 	gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (compositor->priv->buffer),
3132 					    &start, offset);
3133 
3134 	if ((guint) page_nr + 1 < compositor->priv->pages->len)
3135 	{
3136 		offset = g_array_index (compositor->priv->pages, int, page_nr + 1);
3137 		gtk_text_buffer_get_iter_at_offset (GTK_TEXT_BUFFER (compositor->priv->buffer),
3138 						    &end, offset);
3139 	}
3140 	else
3141 	{
3142 		gtk_text_buffer_get_end_iter (GTK_TEXT_BUFFER (compositor->priv->buffer),
3143 					      &end);
3144 	}
3145 
3146 	while (gtk_text_iter_compare (&start, &end) < 0)
3147 	{
3148 		GtkTextIter line_end;
3149 		gint line_number;
3150 		double line_height;
3151 		double baseline_offset;
3152 
3153 		line_end = start;
3154 		if (!gtk_text_iter_ends_line (&line_end))
3155 			gtk_text_iter_forward_to_line_end (&line_end);
3156 		if (gtk_text_iter_compare (&line_end, &end) > 0)
3157 			line_end = end;
3158 
3159 		if (gtk_text_iter_starts_line (&start))
3160 		{
3161 			line_number = gtk_text_iter_get_line (&start);
3162 		}
3163 		else
3164 		{
3165 			/* This happens only if the first line of the page
3166 			 * is the continuation of the last line of the previous page.
3167 			 * In this case the line numbers must not be print
3168 			 */
3169 			line_number = -1;
3170 		}
3171 
3172 		layout_paragraph (compositor, &start, &line_end);
3173 
3174 		get_layout_size (compositor->priv->layout, NULL, &line_height);
3175 
3176 		baseline_offset = 0;
3177 
3178 		/* print the line number if needed */
3179 		if ((line_number >= 0) && line_is_numbered (compositor, line_number))
3180 		{
3181 			PangoLayoutIter *iter;
3182 			double baseline;
3183 			double ln_baseline;
3184 			double ln_baseline_offset;
3185 			gchar *str;
3186 
3187 			str = g_strdup_printf ("%d", line_number + 1);
3188 			pango_layout_set_text (compositor->priv->line_numbers_layout, str, -1);
3189 			g_free (str);
3190 
3191 			/* Adjust the baseline */
3192 			iter = pango_layout_get_iter (compositor->priv->layout);
3193 			baseline = (double) pango_layout_iter_get_baseline (iter) / (double) PANGO_SCALE;
3194 			pango_layout_iter_free (iter);
3195 
3196 			iter = pango_layout_get_iter (compositor->priv->line_numbers_layout);
3197 			ln_baseline = (double) pango_layout_iter_get_baseline (iter) / (double) PANGO_SCALE;
3198 			pango_layout_iter_free (iter);
3199 
3200 			ln_baseline_offset = baseline - ln_baseline;
3201 
3202 			if (ln_baseline_offset < 0)
3203 			{
3204 				baseline_offset = -ln_baseline_offset;
3205 				ln_baseline_offset =  0;
3206 			}
3207 
3208 			cairo_move_to (cr, ln_x, y + ln_baseline_offset);
3209 
3210 			g_return_if_fail (compositor->priv->line_numbers_layout != NULL);
3211 			pango_cairo_show_layout (cr, compositor->priv->line_numbers_layout);
3212 		}
3213 
3214 		cairo_move_to (cr, x, y + baseline_offset);
3215 		pango_cairo_show_layout (cr, compositor->priv->layout);
3216 
3217 		line_height = MAX (line_height,
3218 				   compositor->priv->line_numbers_height);
3219 
3220 		y += line_height;
3221 		gtk_text_iter_forward_line (&start);
3222 	}
3223 }
3224 
3225