1/* GTK - The GIMP Toolkit
2 * gtkpapersize.c: Paper Size
3 * Copyright (C) 2006, Red Hat, Inc.
4 * Copyright © 2006, 2007 Christian Persch
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "config.h"
21#include <string.h>
22#include <stdlib.h>
23#include <locale.h>
24#if defined(HAVE__NL_PAPER_HEIGHT) && defined(HAVE__NL_PAPER_WIDTH)
25#include <langinfo.h>
26#endif
27#include <math.h>
28
29#include "gtkpapersize.h"
30#include "gtkprintutils.h"
31#include "gtkprintoperation.h" /* for GtkPrintError */
32#include "gtkintl.h"
33
34/* _gtk_load_custom_papers() only on Unix so far */
35#ifdef G_OS_UNIX
36#include "gtkcustompaperunixdialog.h"
37#endif
38
39#include "paper_names_offsets.c"
40
41
42/**
43 * GtkPaperSize:
44 *
45 * `GtkPaperSize` handles paper sizes.
46 *
47 * It uses the standard called
48 * [PWG 5101.1-2002 PWG: Standard for Media Standardized Names](http://www.pwg.org/standards.html)
49 * to name the paper sizes (and to get the data for the page sizes).
50 * In addition to standard paper sizes, `GtkPaperSize` allows to
51 * construct custom paper sizes with arbitrary dimensions.
52 *
53 * The `GtkPaperSize` object stores not only the dimensions (width
54 * and height) of a paper size and its name, it also provides
55 * default print margins.
56 */
57
58
59struct _GtkPaperSize
60{
61 const PaperInfo *info;
62
63 /* If these are not set we fall back to info */
64 char *name;
65 char *display_name;
66 char *ppd_name;
67
68 double width, height; /* Stored in mm */
69 gboolean is_custom;
70 gboolean is_ipp;
71};
72
73G_DEFINE_BOXED_TYPE (GtkPaperSize, gtk_paper_size,
74 gtk_paper_size_copy,
75 gtk_paper_size_free)
76
77static const PaperInfo *
78lookup_paper_info (const char *name)
79{
80 int lower = 0;
81 int upper = G_N_ELEMENTS (standard_names_offsets) - 1;
82 int mid;
83 int cmp;
84
85 do
86 {
87 mid = (lower + upper) / 2;
88 cmp = strcmp (s1: name, s2: paper_names + standard_names_offsets[mid].name);
89 if (cmp < 0)
90 upper = mid - 1;
91 else if (cmp > 0)
92 lower = mid + 1;
93 else
94 return &standard_names_offsets[mid];
95 }
96 while (lower <= upper);
97
98 return NULL;
99}
100
101static gboolean
102parse_media_size (const char *size,
103 double *width_mm,
104 double *height_mm)
105{
106 const char *p;
107 char *e;
108 double short_dim, long_dim;
109
110 p = size;
111
112 short_dim = g_ascii_strtod (nptr: p, endptr: &e);
113
114 if (p == e || *e != 'x')
115 return FALSE;
116
117 p = e + 1; /* Skip x */
118
119 long_dim = g_ascii_strtod (nptr: p, endptr: &e);
120
121 if (p == e)
122 return FALSE;
123
124 p = e;
125
126 if (strcmp (s1: p, s2: "in") == 0)
127 {
128 short_dim = short_dim * MM_PER_INCH;
129 long_dim = long_dim * MM_PER_INCH;
130 }
131 else if (strcmp (s1: p, s2: "mm") != 0)
132 return FALSE;
133
134 if (width_mm)
135 *width_mm = short_dim;
136 if (height_mm)
137 *height_mm = long_dim;
138
139 return TRUE;
140}
141
142static gboolean
143parse_full_media_size_name (const char *full_name,
144 char **name,
145 double *width_mm,
146 double *height_mm)
147{
148 const char *p;
149 const char *end_of_name;
150
151 /* From the spec:
152 media-size-self-describing-name =
153 ( class-in "_" size-name "_" short-dim "x" long-dim "in" ) |
154 ( class-mm "_" size-name "_" short-dim "x" long-dim "mm" )
155 class-in = "custom" | "na" | "asme" | "roc" | "oe"
156 class-mm = "custom" | "iso" | "jis" | "jpn" | "prc" | "om"
157 size-name = ( lowalpha | digit ) *( lowalpha | digit | "-" )
158 short-dim = dim
159 long-dim = dim
160 dim = integer-part [fraction-part] | "0" fraction-part
161 integer-part = non-zero-digit *digit
162 fraction-part = "." *digit non-zero-digit
163 lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
164 "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
165 "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
166 non-zero-digit = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
167 digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
168 */
169
170 p = strchr (s: full_name, c: '_');
171 if (p == NULL)
172 return FALSE;
173
174 p++; /* Skip _ */
175
176 p = strchr (s: p, c: '_');
177 if (p == NULL)
178 return FALSE;
179
180 end_of_name = p;
181
182 p++; /* Skip _ */
183
184 if (!parse_media_size (size: p, width_mm, height_mm))
185 return FALSE;
186
187 if (name)
188 *name = g_strndup (str: full_name, n: end_of_name - full_name);
189
190 return TRUE;
191}
192
193static GtkPaperSize *
194gtk_paper_size_new_from_info (const PaperInfo *info)
195{
196 GtkPaperSize *size;
197
198 size = g_slice_new0 (GtkPaperSize);
199 size->info = info;
200 size->width = info->width;
201 size->height = info->height;
202
203 return size;
204}
205
206/**
207 * gtk_paper_size_new:
208 * @name: (nullable): a paper size name
209 *
210 * Creates a new `GtkPaperSize` object by parsing a
211 * [PWG 5101.1-2002](ftp://ftp.pwg.org/pub/pwg/candidates/cs-pwgmsn10-20020226-5101.1.pdf)
212 * paper name.
213 *
214 * If @name is %NULL, the default paper size is returned,
215 * see [func@Gtk.PaperSize.get_default].
216 *
217 * Returns: a new `GtkPaperSize`, use [method@Gtk.PaperSize.free]
218 * to free it
219 */
220GtkPaperSize *
221gtk_paper_size_new (const char *name)
222{
223 GtkPaperSize *size;
224 char *short_name;
225 double width, height;
226 const PaperInfo *info;
227
228 if (name == NULL)
229 name = gtk_paper_size_get_default ();
230
231 if (parse_full_media_size_name (full_name: name, name: &short_name, width_mm: &width, height_mm: &height))
232 {
233 info = lookup_paper_info (name: short_name);
234 if (info != NULL && info->width == width && info->height == height)
235 {
236 size = gtk_paper_size_new_from_info (info);
237 g_free (mem: short_name);
238 }
239 else
240 {
241 size = g_slice_new0 (GtkPaperSize);
242
243 size->width = width;
244 size->height = height;
245 size->name = short_name;
246 size->display_name = g_strdup (str: short_name);
247 if (strncmp (s1: short_name, s2: "custom", n: 6) == 0)
248 size->is_custom = TRUE;
249 }
250 }
251 else
252 {
253 info = lookup_paper_info (name);
254 if (info != NULL)
255 size = gtk_paper_size_new_from_info (info);
256 else
257 {
258 g_warning ("Unknown paper size %s", name);
259 size = g_slice_new0 (GtkPaperSize);
260 size->name = g_strdup (str: name);
261 size->display_name = g_strdup (str: name);
262 /* Default to A4 size */
263 size->width = 210;
264 size->height = 297;
265 }
266 }
267
268 return size;
269}
270
271static char *
272improve_displayname (const char *name)
273{
274 char *p, *p1, *p2, *s;
275
276 p = strrchr (s: name, c: 'x');
277 if (p && p != name &&
278 g_ascii_isdigit (*(p - 1)) &&
279 g_ascii_isdigit (*(p + 1)))
280 {
281 p1 = g_strndup (str: name, n: p - name);
282 p2 = g_strdup (str: p + 1);
283 s = g_strconcat (string1: p1, "×", p2, NULL);
284 g_free (mem: p1);
285 g_free (mem: p2);
286 }
287 else
288 s = g_strdup (str: name);
289
290 return s;
291}
292
293/**
294 * gtk_paper_size_new_from_ppd:
295 * @ppd_name: a PPD paper name
296 * @ppd_display_name: the corresponding human-readable name
297 * @width: the paper width, in points
298 * @height: the paper height in points
299 *
300 * Creates a new `GtkPaperSize` object by using
301 * PPD information.
302 *
303 * If @ppd_name is not a recognized PPD paper name,
304 * @ppd_display_name, @width and @height are used to
305 * construct a custom `GtkPaperSize` object.
306 *
307 * Returns: a new `GtkPaperSize`, use [method@Gtk.PaperSize.free]
308 * to free it
309 */
310GtkPaperSize *
311gtk_paper_size_new_from_ppd (const char *ppd_name,
312 const char *ppd_display_name,
313 double width,
314 double height)
315{
316 char *name;
317 const char *lookup_ppd_name;
318 char *freeme;
319 GtkPaperSize *size;
320 int i;
321 char *display_name;
322
323 lookup_ppd_name = ppd_name;
324
325 freeme = NULL;
326 /* Strip out Traverse suffix in matching. */
327 if (g_str_has_suffix (str: ppd_name, suffix: ".Transverse"))
328 {
329 lookup_ppd_name = freeme =
330 g_strndup (str: ppd_name, n: strlen (s: ppd_name) - strlen (s: ".Transverse"));
331 }
332
333 for (i = 0; i < G_N_ELEMENTS (standard_names_offsets); i++)
334 {
335 if (standard_names_offsets[i].ppd_name != -1 &&
336 strcmp (s1: paper_names + standard_names_offsets[i].ppd_name, s2: lookup_ppd_name) == 0)
337 {
338 size = gtk_paper_size_new_from_info (info: &standard_names_offsets[i]);
339 goto out;
340 }
341 }
342
343 for (i = 0; i < G_N_ELEMENTS (extra_ppd_names_offsets); i++)
344 {
345 if (strcmp (s1: paper_names + extra_ppd_names_offsets[i].ppd_name, s2: lookup_ppd_name) == 0)
346 {
347 size = gtk_paper_size_new (name: paper_names + extra_ppd_names_offsets[i].standard_name);
348 goto out;
349 }
350 }
351
352 name = g_strconcat (string1: "ppd_", ppd_name, NULL);
353 display_name = improve_displayname (name: ppd_display_name);
354 size = gtk_paper_size_new_custom (name, display_name, width, height, unit: GTK_UNIT_POINTS);
355 g_free (mem: display_name);
356 g_free (mem: name);
357
358 out:
359
360 if (size->info == NULL ||
361 size->info->ppd_name == -1 ||
362 strcmp (s1: paper_names + size->info->ppd_name, s2: ppd_name) != 0)
363 size->ppd_name = g_strdup (str: ppd_name);
364
365 g_free (mem: freeme);
366
367 return size;
368}
369
370/* Tolerance of paper size in points according to PostScript Language Reference */
371#define PAPER_SIZE_TOLERANCE 5
372
373/**
374 * gtk_paper_size_new_from_ipp:
375 * @ipp_name: an IPP paper name
376 * @width: the paper width, in points
377 * @height: the paper height in points
378 *
379 * Creates a new `GtkPaperSize` object by using
380 * IPP information.
381 *
382 * If @ipp_name is not a recognized paper name,
383 * @width and @height are used to
384 * construct a custom `GtkPaperSize` object.
385 *
386 * Returns: a new `GtkPaperSize`, use [method@Gtk.PaperSize.free]
387 * to free it
388 */
389GtkPaperSize *
390gtk_paper_size_new_from_ipp (const char *ipp_name,
391 double width,
392 double height)
393{
394 GtkPaperSize *size;
395 const char *name = NULL;
396 gboolean found = FALSE;
397 float x_dimension;
398 float y_dimension;
399 char *display_name = NULL;
400 int i;
401
402 /* Find paper size according to its name */
403 for (i = 0; i < G_N_ELEMENTS (standard_names_offsets); i++)
404 {
405 if (standard_names_offsets[i].name != -1)
406 name = paper_names + standard_names_offsets[i].name;
407 if (name != NULL &&
408 /* Given paper size name is equal to a name
409 from the standard paper size names list. */
410 ((g_strcmp0 (str1: ipp_name, str2: name) == 0) ||
411 /* Given paper size name is prefixed by a name
412 from the standard paper size names list +
413 it consists of size in its name (e.g. iso_a4_210x297mm). */
414 (g_str_has_prefix (str: ipp_name, prefix: name) &&
415 strlen (s: ipp_name) > strlen (s: name) + 2 &&
416 ipp_name[strlen (s: ipp_name)] == '_' &&
417 g_ascii_isdigit (ipp_name[strlen (ipp_name) + 1]) &&
418 (g_str_has_suffix (str: ipp_name, suffix: "mm") ||
419 g_str_has_suffix (str: ipp_name, suffix: "in")))))
420 {
421 display_name = g_strdup (str: g_dpgettext2 (GETTEXT_PACKAGE,
422 context: "paper size",
423 msgid: paper_names + standard_names_offsets[i].display_name));
424 found = TRUE;
425 break;
426 }
427 }
428
429 /* Find paper size according to its size */
430 if (display_name == NULL)
431 {
432 for (i = 0; i < G_N_ELEMENTS (standard_names_offsets); i++)
433 {
434 x_dimension = _gtk_print_convert_from_mm (len: standard_names_offsets[i].width, unit: GTK_UNIT_POINTS);
435 y_dimension = _gtk_print_convert_from_mm (len: standard_names_offsets[i].height, unit: GTK_UNIT_POINTS);
436
437 if (fabs (x: x_dimension - width) <= PAPER_SIZE_TOLERANCE &&
438 fabs (x: y_dimension - height) <= PAPER_SIZE_TOLERANCE)
439 {
440 display_name = g_strdup (str: g_dpgettext2 (GETTEXT_PACKAGE,
441 context: "paper size",
442 msgid: paper_names + standard_names_offsets[i].display_name));
443 found = TRUE;
444 break;
445 }
446 }
447 }
448
449 /* Fallback to name of the paper size as given in "ipp_name" parameter */
450 if (display_name == NULL)
451 display_name = g_strdup (str: ipp_name);
452
453 size = gtk_paper_size_new_custom (name: ipp_name, display_name, width, height, unit: GTK_UNIT_POINTS);
454 size->is_custom = !found;
455 size->is_ipp = found;
456
457 g_free (mem: display_name);
458
459 return size;
460}
461
462/**
463 * gtk_paper_size_new_custom:
464 * @name: the paper name
465 * @display_name: the human-readable name
466 * @width: the paper width, in units of @unit
467 * @height: the paper height, in units of @unit
468 * @unit: the unit for @width and @height. not %GTK_UNIT_NONE.
469 *
470 * Creates a new `GtkPaperSize` object with the
471 * given parameters.
472 *
473 * Returns: a new `GtkPaperSize` object, use [method@Gtk.PaperSize.free]
474 * to free it
475 */
476GtkPaperSize *
477gtk_paper_size_new_custom (const char *name,
478 const char *display_name,
479 double width,
480 double height,
481 GtkUnit unit)
482{
483 GtkPaperSize *size;
484 g_return_val_if_fail (name != NULL, NULL);
485 g_return_val_if_fail (unit != GTK_UNIT_NONE, NULL);
486
487 size = g_slice_new0 (GtkPaperSize);
488
489 size->name = g_strdup (str: name);
490 size->display_name = g_strdup (str: display_name);
491 size->is_custom = TRUE;
492
493 size->width = _gtk_print_convert_to_mm (len: width, unit);
494 size->height = _gtk_print_convert_to_mm (len: height, unit);
495
496 return size;
497}
498
499/**
500 * gtk_paper_size_copy:
501 * @other: a `GtkPaperSize`
502 *
503 * Copies an existing `GtkPaperSize`.
504 *
505 * Returns: a copy of @other
506 */
507GtkPaperSize *
508gtk_paper_size_copy (GtkPaperSize *other)
509{
510 GtkPaperSize *size;
511
512 size = g_slice_new0 (GtkPaperSize);
513
514 size->info = other->info;
515 if (other->name)
516 size->name = g_strdup (str: other->name);
517 if (other->display_name)
518 size->display_name = g_strdup (str: other->display_name);
519 if (other->ppd_name)
520 size->ppd_name = g_strdup (str: other->ppd_name);
521
522 size->width = other->width;
523 size->height = other->height;
524 size->is_custom = other->is_custom;
525 size->is_ipp = other->is_ipp;
526
527 return size;
528}
529
530/**
531 * gtk_paper_size_free:
532 * @size: a `GtkPaperSize`
533 *
534 * Free the given `GtkPaperSize` object.
535 */
536void
537gtk_paper_size_free (GtkPaperSize *size)
538{
539 g_free (mem: size->name);
540 g_free (mem: size->display_name);
541 g_free (mem: size->ppd_name);
542
543 g_slice_free (GtkPaperSize, size);
544}
545
546/**
547 * gtk_paper_size_is_equal:
548 * @size1: a `GtkPaperSize` object
549 * @size2: another `GtkPaperSize` object
550 *
551 * Compares two `GtkPaperSize` objects.
552 *
553 * Returns: %TRUE, if @size1 and @size2
554 * represent the same paper size
555 */
556gboolean
557gtk_paper_size_is_equal (GtkPaperSize *size1,
558 GtkPaperSize *size2)
559{
560 if (size1->info != NULL && size2->info != NULL)
561 return size1->info == size2->info;
562
563 return strcmp (s1: gtk_paper_size_get_name (size: size1),
564 s2: gtk_paper_size_get_name (size: size2)) == 0;
565}
566
567/**
568 * gtk_paper_size_get_paper_sizes:
569 * @include_custom: whether to include custom paper sizes
570 * as defined in the page setup dialog
571 *
572 * Creates a list of known paper sizes.
573 *
574 * Returns: (element-type GtkPaperSize) (transfer full): a newly allocated list of newly
575 * allocated `GtkPaperSize` objects
576 */
577GList *
578gtk_paper_size_get_paper_sizes (gboolean include_custom)
579{
580 GList *list = NULL;
581 guint i;
582/* _gtk_load_custom_papers() only on Unix so far */
583#ifdef G_OS_UNIX
584 if (include_custom)
585 {
586 GList *page_setups, *l;
587
588 page_setups = _gtk_load_custom_papers ();
589 for (l = page_setups; l != NULL; l = l->next)
590 {
591 GtkPageSetup *setup = (GtkPageSetup *) l->data;
592 GtkPaperSize *size;
593
594 size = gtk_page_setup_get_paper_size (setup);
595 list = g_list_prepend (list, data: gtk_paper_size_copy (other: size));
596 }
597
598 g_list_free_full (list: page_setups, free_func: g_object_unref);
599 }
600#endif
601 for (i = 0; i < G_N_ELEMENTS (standard_names_offsets); ++i)
602 {
603 GtkPaperSize *size;
604
605 size = gtk_paper_size_new_from_info (info: &standard_names_offsets[i]);
606 list = g_list_prepend (list, data: size);
607 }
608
609 return g_list_reverse (list);
610}
611
612
613/**
614 * gtk_paper_size_get_name:
615 * @size: a `GtkPaperSize` object
616 *
617 * Gets the name of the `GtkPaperSize`.
618 *
619 * Returns: the name of @size
620 */
621const char *
622gtk_paper_size_get_name (GtkPaperSize *size)
623{
624 if (size->name)
625 return size->name;
626 g_assert (size->info != NULL);
627 return paper_names + size->info->name;
628}
629
630/**
631 * gtk_paper_size_get_display_name:
632 * @size: a `GtkPaperSize` object
633 *
634 * Gets the human-readable name of the `GtkPaperSize`.
635 *
636 * Returns: the human-readable name of @size
637 */
638const char *
639gtk_paper_size_get_display_name (GtkPaperSize *size)
640{
641 const char *display_name;
642
643 if (size->display_name)
644 return size->display_name;
645
646 g_assert (size->info != NULL);
647
648 display_name = paper_names + size->info->display_name;
649 return g_dpgettext2 (GETTEXT_PACKAGE, context: "paper size", msgid: display_name);
650}
651
652/**
653 * gtk_paper_size_get_ppd_name:
654 * @size: a `GtkPaperSize` object
655 *
656 * Gets the PPD name of the `GtkPaperSize`, which
657 * may be %NULL.
658 *
659 * Returns: the PPD name of @size
660 */
661const char *
662gtk_paper_size_get_ppd_name (GtkPaperSize *size)
663{
664 if (size->ppd_name)
665 return size->ppd_name;
666 if (size->info)
667 return paper_names + size->info->ppd_name;
668 return NULL;
669}
670
671/**
672 * gtk_paper_size_get_width:
673 * @size: a `GtkPaperSize` object
674 * @unit: the unit for the return value, not %GTK_UNIT_NONE
675 *
676 * Gets the paper width of the `GtkPaperSize`, in
677 * units of @unit.
678 *
679 * Returns: the paper width
680 */
681double
682gtk_paper_size_get_width (GtkPaperSize *size,
683 GtkUnit unit)
684{
685 return _gtk_print_convert_from_mm (len: size->width, unit);
686}
687
688/**
689 * gtk_paper_size_get_height:
690 * @size: a `GtkPaperSize` object
691 * @unit: the unit for the return value, not %GTK_UNIT_NONE
692 *
693 * Gets the paper height of the `GtkPaperSize`, in
694 * units of @unit.
695 *
696 * Returns: the paper height
697 */
698double
699gtk_paper_size_get_height (GtkPaperSize *size,
700 GtkUnit unit)
701{
702 return _gtk_print_convert_from_mm (len: size->height, unit);
703}
704
705/**
706 * gtk_paper_size_is_custom:
707 * @size: a `GtkPaperSize` object
708 *
709 * Returns %TRUE if @size is not a standard paper size.
710 *
711 * Returns: whether @size is a custom paper size.
712 **/
713gboolean
714gtk_paper_size_is_custom (GtkPaperSize *size)
715{
716 return size->is_custom;
717}
718
719/**
720 * gtk_paper_size_is_ipp:
721 * @size: a `GtkPaperSize` object
722 *
723 * Returns %TRUE if @size is an IPP standard paper size.
724 *
725 * Returns: whether @size is not an IPP custom paper size.
726 **/
727gboolean
728gtk_paper_size_is_ipp (GtkPaperSize *size)
729{
730 return size->is_ipp;
731}
732
733/**
734 * gtk_paper_size_set_size:
735 * @size: a custom `GtkPaperSize` object
736 * @width: the new width in units of @unit
737 * @height: the new height in units of @unit
738 * @unit: the unit for @width and @height
739 *
740 * Changes the dimensions of a @size to @width x @height.
741 */
742void
743gtk_paper_size_set_size (GtkPaperSize *size,
744 double width,
745 double height,
746 GtkUnit unit)
747{
748 g_return_if_fail (size != NULL);
749 g_return_if_fail (size->is_custom);
750
751 size->width = _gtk_print_convert_to_mm (len: width, unit);
752 size->height = _gtk_print_convert_to_mm (len: height, unit);
753}
754
755#define NL_PAPER_GET(x) \
756 ((union { char *string; unsigned int word; })nl_langinfo(x)).word
757
758/**
759 * gtk_paper_size_get_default:
760 *
761 * Returns the name of the default paper size, which
762 * depends on the current locale.
763 *
764 * Returns: the name of the default paper size. The string
765 * is owned by GTK and should not be modified.
766 */
767const char *
768gtk_paper_size_get_default (void)
769{
770 char *locale, *freeme = NULL;
771 const char *paper_size;
772
773#if defined(HAVE__NL_PAPER_HEIGHT) && defined(HAVE__NL_PAPER_WIDTH)
774 {
775 int width = NL_PAPER_GET (_NL_PAPER_WIDTH);
776 int height = NL_PAPER_GET (_NL_PAPER_HEIGHT);
777
778 if (width == 210 && height == 297)
779 return GTK_PAPER_NAME_A4;
780
781 if (width == 216 && height == 279)
782 return GTK_PAPER_NAME_LETTER;
783 }
784#endif
785
786#ifdef G_OS_WIN32
787 freeme = locale = g_win32_getlocale ();
788#elif defined(LC_PAPER)
789 locale = setlocale(LC_PAPER, NULL);
790#else
791 locale = setlocale(LC_MESSAGES, NULL);
792#endif
793
794 if (!locale)
795 return GTK_PAPER_NAME_A4;
796
797 /* CLDR 1.8.1
798 * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/territory_language_information.html
799 */
800 if (g_regex_match_simple(pattern: "[^_.@]{2,3}_(BZ|CA|CL|CO|CR|GT|MX|NI|PA|PH|PR|SV|US|VE)",
801 string: locale, compile_options: G_REGEX_ANCHORED, match_options: G_REGEX_MATCH_ANCHORED))
802 paper_size = GTK_PAPER_NAME_LETTER;
803 else
804 paper_size = GTK_PAPER_NAME_A4;
805
806 g_free (mem: freeme);
807 return paper_size;
808}
809
810/* These get the default margins used for the paper size. Its
811 * larger than most printers margins, so that it will be within
812 * the imageble area on any printer.
813 *
814 * I’ve taken the actual values used from the OSX page setup dialog.
815 * I’m not sure exactly where they got these values for, but might
816 * correspond to this (from ghostscript docs):
817 *
818 * All DeskJets have 0.5 inches (1.27cm) of unprintable bottom margin,
819 * due to the mechanical arrangement used to grab the paper. Side margins
820 * are approximately 0.25 inches (0.64cm) for U.S. letter paper, and 0.15
821 * inches (0.38cm) for A4.
822 */
823
824/**
825 * gtk_paper_size_get_default_top_margin:
826 * @size: a `GtkPaperSize` object
827 * @unit: the unit for the return value, not %GTK_UNIT_NONE
828 *
829 * Gets the default top margin for the `GtkPaperSize`.
830 *
831 * Returns: the default top margin
832 */
833double
834gtk_paper_size_get_default_top_margin (GtkPaperSize *size,
835 GtkUnit unit)
836{
837 double margin;
838
839 margin = _gtk_print_convert_to_mm (len: 0.25, unit: GTK_UNIT_INCH);
840 return _gtk_print_convert_from_mm (len: margin, unit);
841}
842
843/**
844 * gtk_paper_size_get_default_bottom_margin:
845 * @size: a `GtkPaperSize` object
846 * @unit: the unit for the return value, not %GTK_UNIT_NONE
847 *
848 * Gets the default bottom margin for the `GtkPaperSize`.
849 *
850 * Returns: the default bottom margin
851 */
852double
853gtk_paper_size_get_default_bottom_margin (GtkPaperSize *size,
854 GtkUnit unit)
855{
856 double margin;
857 const char *name;
858
859 margin = _gtk_print_convert_to_mm (len: 0.25, unit: GTK_UNIT_INCH);
860
861 name = gtk_paper_size_get_name (size);
862 if (strcmp (s1: name, s2: "na_letter") == 0 ||
863 strcmp (s1: name, s2: "na_legal") == 0 ||
864 strcmp (s1: name, s2: "iso_a4") == 0)
865 margin = _gtk_print_convert_to_mm (len: 0.56, unit: GTK_UNIT_INCH);
866
867 return _gtk_print_convert_from_mm (len: margin, unit);
868}
869
870/**
871 * gtk_paper_size_get_default_left_margin:
872 * @size: a `GtkPaperSize` object
873 * @unit: the unit for the return value, not %GTK_UNIT_NONE
874 *
875 * Gets the default left margin for the `GtkPaperSize`.
876 *
877 * Returns: the default left margin
878 */
879double
880gtk_paper_size_get_default_left_margin (GtkPaperSize *size,
881 GtkUnit unit)
882{
883 double margin;
884
885 margin = _gtk_print_convert_to_mm (len: 0.25, unit: GTK_UNIT_INCH);
886 return _gtk_print_convert_from_mm (len: margin, unit);
887}
888
889/**
890 * gtk_paper_size_get_default_right_margin:
891 * @size: a `GtkPaperSize` object
892 * @unit: the unit for the return value, not %GTK_UNIT_NONE
893 *
894 * Gets the default right margin for the `GtkPaperSize`.
895 *
896 * Returns: the default right margin
897 */
898double
899gtk_paper_size_get_default_right_margin (GtkPaperSize *size,
900 GtkUnit unit)
901{
902 double margin;
903
904 margin = _gtk_print_convert_to_mm (len: 0.25, unit: GTK_UNIT_INCH);
905 return _gtk_print_convert_from_mm (len: margin, unit);
906}
907
908/**
909 * gtk_paper_size_new_from_key_file:
910 * @key_file: the `GKeyFile` to retrieve the papersize from
911 * @group_name: (nullable): the name of the group in the key file to read,
912 * or %NULL to read the first group
913 * @error: (nullable): return location for an error
914 *
915 * Reads a paper size from the group @group_name in the key file
916 * @key_file.
917 *
918 * Returns: a new `GtkPaperSize` object with the restored paper size
919 */
920GtkPaperSize *
921gtk_paper_size_new_from_key_file (GKeyFile *key_file,
922 const char *group_name,
923 GError **error)
924{
925 GtkPaperSize *paper_size = NULL;
926 char *name = NULL;
927 char *ppd_name = NULL;
928 char *display_name = NULL;
929 char *freeme = NULL;
930 double width, height;
931 GError *err = NULL;
932
933 g_return_val_if_fail (key_file != NULL, NULL);
934
935 if (!group_name)
936 group_name = freeme = g_key_file_get_start_group (key_file);
937 if (!group_name || !g_key_file_has_group (key_file, group_name))
938 {
939 g_set_error_literal (err: error,
940 GTK_PRINT_ERROR,
941 code: GTK_PRINT_ERROR_INVALID_FILE,
942 _("Not a valid page setup file"));
943 goto out;
944 }
945
946#define GET_DOUBLE(kf, group, name, v) \
947 v = g_key_file_get_double (kf, group, name, &err); \
948 if (err != NULL) \
949 {\
950 g_propagate_error (error, err);\
951 goto out;\
952 }
953
954 GET_DOUBLE (key_file, group_name, "Width", width);
955 GET_DOUBLE (key_file, group_name, "Height", height);
956
957#undef GET_DOUBLE
958
959 name = g_key_file_get_string (key_file, group_name,
960 key: "Name", NULL);
961 ppd_name = g_key_file_get_string (key_file, group_name,
962 key: "PPDName", NULL);
963 display_name = g_key_file_get_string (key_file, group_name,
964 key: "DisplayName", NULL);
965 /* Fallback for old ~/.gtk-custom-paper entries */
966 if (!display_name)
967 display_name = g_strdup (str: name);
968
969 if (ppd_name != NULL)
970 paper_size = gtk_paper_size_new_from_ppd (ppd_name,
971 ppd_display_name: display_name,
972 width: _gtk_print_convert_from_mm (len: width, unit: GTK_UNIT_POINTS),
973 height: _gtk_print_convert_from_mm (len: height, unit: GTK_UNIT_POINTS));
974 else if (name != NULL)
975 paper_size = gtk_paper_size_new_custom (name, display_name,
976 width, height, unit: GTK_UNIT_MM);
977 else
978 {
979 g_set_error_literal (err: error,
980 GTK_PRINT_ERROR,
981 code: GTK_PRINT_ERROR_INVALID_FILE,
982 _("Not a valid page setup file"));
983 goto out;
984 }
985
986 g_assert (paper_size != NULL);
987
988out:
989 g_free (mem: ppd_name);
990 g_free (mem: name);
991 g_free (mem: display_name);
992 g_free (mem: freeme);
993
994 return paper_size;
995}
996
997/**
998 * gtk_paper_size_to_key_file:
999 * @size: a `GtkPaperSize`
1000 * @key_file: the `GKeyFile` to save the paper size to
1001 * @group_name: the group to add the settings to in @key_file
1002 *
1003 * This function adds the paper size from @size to @key_file.
1004 */
1005void
1006gtk_paper_size_to_key_file (GtkPaperSize *size,
1007 GKeyFile *key_file,
1008 const char *group_name)
1009{
1010 const char *name, *ppd_name, *display_name;
1011
1012 g_return_if_fail (size != NULL);
1013 g_return_if_fail (key_file != NULL);
1014
1015 name = gtk_paper_size_get_name (size);
1016 display_name = gtk_paper_size_get_display_name (size);
1017 ppd_name = gtk_paper_size_get_ppd_name (size);
1018
1019 if (ppd_name != NULL)
1020 g_key_file_set_string (key_file, group_name,
1021 key: "PPDName", string: ppd_name);
1022 else
1023 g_key_file_set_string (key_file, group_name,
1024 key: "Name", string: name);
1025
1026 if (display_name)
1027 g_key_file_set_string (key_file, group_name,
1028 key: "DisplayName", string: display_name);
1029
1030 g_key_file_set_double (key_file, group_name,
1031 key: "Width", value: gtk_paper_size_get_width (size, unit: GTK_UNIT_MM));
1032 g_key_file_set_double (key_file, group_name,
1033 key: "Height", value: gtk_paper_size_get_height (size, unit: GTK_UNIT_MM));
1034}
1035
1036/**
1037 * gtk_paper_size_to_gvariant:
1038 * @paper_size: a `GtkPaperSize`
1039 *
1040 * Serialize a paper size to an `a{sv}` variant.
1041 *
1042 * Returns: (transfer none): a new, floating, `GVariant`
1043 */
1044GVariant *
1045gtk_paper_size_to_gvariant (GtkPaperSize *paper_size)
1046{
1047 const char *name;
1048 const char *ppd_name;
1049 const char *display_name;
1050 GVariantBuilder builder;
1051
1052 g_variant_builder_init (builder: &builder, G_VARIANT_TYPE_VARDICT);
1053
1054 name = gtk_paper_size_get_name (size: paper_size);
1055 ppd_name = gtk_paper_size_get_ppd_name (size: paper_size);
1056 display_name = gtk_paper_size_get_display_name (size: paper_size);
1057
1058 if (ppd_name != NULL)
1059 g_variant_builder_add (builder: &builder, format_string: "{sv}", "PPDName", g_variant_new_string (string: ppd_name));
1060 else
1061 g_variant_builder_add (builder: &builder, format_string: "{sv}", "Name", g_variant_new_string (string: name));
1062
1063 if (display_name != NULL)
1064 g_variant_builder_add (builder: &builder, format_string: "{sv}", "DisplayName", g_variant_new_string (string: display_name));
1065
1066 g_variant_builder_add (builder: &builder, format_string: "{sv}", "Width", g_variant_new_double (value: gtk_paper_size_get_width (size: paper_size, unit: GTK_UNIT_MM)));
1067 g_variant_builder_add (builder: &builder, format_string: "{sv}", "Height", g_variant_new_double (value: gtk_paper_size_get_height (size: paper_size, unit: GTK_UNIT_MM)));
1068
1069 return g_variant_builder_end (builder: &builder);
1070}
1071
1072/**
1073 * gtk_paper_size_new_from_gvariant:
1074 * @variant: an a{sv} `GVariant`
1075 *
1076 * Deserialize a paper size from a `GVariant`.
1077 *
1078 * The `GVariant must be in the format produced by
1079 * [method@Gtk.PaperSize.to_gvariant].
1080 *
1081 * Returns: (transfer full): a new `GtkPaperSize` object
1082 */
1083GtkPaperSize *
1084gtk_paper_size_new_from_gvariant (GVariant *variant)
1085{
1086 GtkPaperSize *paper_size;
1087 const char *name;
1088 const char *ppd_name;
1089 const char *display_name;
1090 double width, height;
1091
1092 g_return_val_if_fail (g_variant_is_of_type (variant, G_VARIANT_TYPE_VARDICT), NULL);
1093
1094 if (!g_variant_lookup (dictionary: variant, key: "Width", format_string: "d", &width) ||
1095 !g_variant_lookup (dictionary: variant, key: "Height", format_string: "d", &height))
1096 return NULL;
1097
1098 if (!g_variant_lookup (dictionary: variant, key: "Name", format_string: "&s", &name))
1099 name = NULL;
1100
1101 if (!g_variant_lookup (dictionary: variant, key: "PPDName", format_string: "&s", &ppd_name))
1102 ppd_name = NULL;
1103
1104 if (!g_variant_lookup (dictionary: variant, key: "DisplayName", format_string: "&s", &display_name))
1105 display_name = name;
1106
1107 if (ppd_name != NULL)
1108 paper_size = gtk_paper_size_new_from_ppd (ppd_name,
1109 ppd_display_name: display_name,
1110 width: _gtk_print_convert_from_mm (len: width, unit: GTK_UNIT_POINTS),
1111 height: _gtk_print_convert_from_mm (len: height, unit: GTK_UNIT_POINTS));
1112 else if (name != NULL)
1113 paper_size = gtk_paper_size_new_custom (name, display_name,
1114 width, height, unit: GTK_UNIT_MM);
1115 else
1116 paper_size = NULL;
1117
1118 return paper_size;
1119}
1120

source code of gtk/gtk/gtkpapersize.c