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. "Monospace 10"). 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. "Monospace 10"). 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. "Monospace 10"). 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. "Monospace 10"). 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. "Monospace 10"). 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. "Monospace 10"). 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. "Monospace 10"). 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. "Monospace 10"). 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 ¤t,
2769 compositor->priv->pagination_mark);
2770
2771 return (gdouble) gtk_text_iter_get_offset (¤t) / (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