]> Pileus Git - ~andy/gtk/blob - gtk/gtkpapersize.c
Bug 663856 - Make option-foo accelerators use the right symbol
[~andy/gtk] / gtk / gtkpapersize.c
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, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "config.h"
23 #include <string.h>
24 #include <stdlib.h>
25 #include <locale.h>
26 #if defined(HAVE__NL_PAPER_HEIGHT) && defined(HAVE__NL_PAPER_WIDTH)
27 #include <langinfo.h>
28 #endif
29
30 #include "gtkpapersize.h"
31 #include "gtkprintutils.h"
32 #include "gtkprintoperation.h"  /* for GtkPrintError */
33 #include "gtkintl.h"
34
35 #include "paper_names_offsets.c"
36
37
38 /**
39  * SECTION:gtkpapersize
40  * @Short_description: Support for named paper sizes
41  * @Title: GtkPaperSize
42  * @See_also:#GtkPageSetup
43  *
44  * GtkPaperSize handles paper sizes. It uses the standard called
45  * <ulink url="http://www.pwg.org/standards.html">"PWG 5101.1-2002 PWG: Standard for Media Standardized Names"</ulink>
46  * to name the paper sizes (and to get the data for the page sizes).
47  * In addition to standard paper sizes, GtkPaperSize allows to
48  * construct custom paper sizes with arbitrary dimensions.
49  *
50  * The #GtkPaperSize object stores not only the dimensions (width
51  * and height) of a paper size and its name, it also provides
52  * default <link linkend="print-margins">print margins</link>.
53  *
54  * Printing support has been added in GTK+ 2.10.
55  */
56
57
58 struct _GtkPaperSize
59 {
60   const PaperInfo *info;
61
62   /* If these are not set we fall back to info */
63   gchar *name;
64   gchar *display_name;
65   gchar *ppd_name;
66
67   gdouble width, height; /* Stored in mm */
68   gboolean is_custom;
69 };
70
71 G_DEFINE_BOXED_TYPE (GtkPaperSize, gtk_paper_size,
72                      gtk_paper_size_copy,
73                      gtk_paper_size_free)
74
75 static const PaperInfo *
76 lookup_paper_info (const gchar *name)
77 {
78   int lower = 0;
79   int upper = G_N_ELEMENTS (standard_names_offsets) - 1;
80   int mid;
81   int cmp;
82
83   do
84     {
85        mid = (lower + upper) / 2;
86        cmp = strcmp (name, paper_names + standard_names_offsets[mid].name);
87        if (cmp < 0)
88          upper = mid - 1;
89        else if (cmp > 0)
90          lower = mid + 1;
91        else
92          return &standard_names_offsets[mid];
93     }
94   while (lower <= upper);
95
96   return NULL;
97 }
98
99 static gboolean
100 parse_media_size (const gchar *size,
101                   gdouble     *width_mm,
102                   gdouble     *height_mm)
103 {
104   const char *p;
105   char *e;
106   double short_dim, long_dim;
107
108   p = size;
109
110   short_dim = g_ascii_strtod (p, &e);
111
112   if (p == e || *e != 'x')
113     return FALSE;
114
115   p = e + 1; /* Skip x */
116
117   long_dim = g_ascii_strtod (p, &e);
118
119   if (p == e)
120     return FALSE;
121
122   p = e;
123
124   if (strcmp (p, "in") == 0)
125     {
126       short_dim = short_dim * MM_PER_INCH;
127       long_dim = long_dim * MM_PER_INCH;
128     }
129   else if (strcmp (p, "mm") != 0)
130     return FALSE;
131
132   if (width_mm)
133     *width_mm = short_dim;
134   if (height_mm)
135     *height_mm = long_dim;
136
137   return TRUE;
138 }
139
140 static gboolean
141 parse_full_media_size_name (const gchar  *full_name,
142                             gchar       **name,
143                             gdouble      *width_mm,
144                             gdouble      *height_mm)
145 {
146   const char *p;
147   const char *end_of_name;
148
149   /* From the spec:
150    media-size-self-describing-name =
151         ( class-in "_" size-name "_" short-dim "x" long-dim "in" ) |
152         ( class-mm "_" size-name "_" short-dim "x" long-dim "mm" )
153    class-in = "custom" | "na" | "asme" | "roc" | "oe"
154    class-mm = "custom" | "iso" | "jis" | "jpn" | "prc" | "om"
155    size-name = ( lowalpha | digit ) *( lowalpha | digit | "-" )
156    short-dim = dim
157    long-dim = dim
158    dim = integer-part [fraction-part] | "0" fraction-part
159    integer-part = non-zero-digit *digit
160    fraction-part = "." *digit non-zero-digit
161    lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
162               "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
163               "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
164    non-zero-digit = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
165    digit    = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
166  */
167
168   p = strchr (full_name, '_');
169   if (p == NULL)
170     return FALSE;
171
172   p++; /* Skip _ */
173
174   p = strchr (p, '_');
175   if (p == NULL)
176     return FALSE;
177
178   end_of_name = p;
179
180   p++; /* Skip _ */
181
182   if (!parse_media_size (p, width_mm, height_mm))
183     return FALSE;
184
185   if (name)
186     *name = g_strndup (full_name, end_of_name - full_name);
187
188   return TRUE;
189 }
190
191 static GtkPaperSize *
192 gtk_paper_size_new_from_info (const PaperInfo *info)
193 {
194   GtkPaperSize *size;
195
196   size = g_slice_new0 (GtkPaperSize);
197   size->info = info;
198   size->width = info->width;
199   size->height = info->height;
200
201   return size;
202 }
203
204 /**
205  * gtk_paper_size_new:
206  * @name: (allow-none): a paper size name, or %NULL
207  *
208  * Creates a new #GtkPaperSize object by parsing a
209  * <ulink url="ftp://ftp.pwg.org/pub/pwg/candidates/cs-pwgmsn10-20020226-5101.1.pdf">PWG 5101.1-2002</ulink>
210  * paper name.
211  *
212  * If @name is %NULL, the default paper size is returned,
213  * see gtk_paper_size_get_default().
214  *
215  * Return value: a new #GtkPaperSize, use gtk_paper_size_free()
216  * to free it
217  *
218  * Since: 2.10
219  */
220 GtkPaperSize *
221 gtk_paper_size_new (const gchar *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 (name, &short_name, &width, &height))
232     {
233       info = lookup_paper_info (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 (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 (short_name);
247           if (strncmp (short_name, "custom", 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\n", name);
259           size = g_slice_new0 (GtkPaperSize);
260           size->name = g_strdup (name);
261           size->display_name = g_strdup (name);
262           /* Default to A4 size */
263           size->width = 210;
264           size->height = 297;
265         }
266     }
267
268   return size;
269 }
270
271 /**
272  * gtk_paper_size_new_from_ppd:
273  * @ppd_name: a PPD paper name
274  * @ppd_display_name: the corresponding human-readable name
275  * @width: the paper width, in points
276  * @height: the paper height in points
277  *
278  * Creates a new #GtkPaperSize object by using
279  * PPD information.
280  *
281  * If @ppd_name is not a recognized PPD paper name,
282  * @ppd_display_name, @width and @height are used to
283  * construct a custom #GtkPaperSize object.
284  *
285  * Return value: a new #GtkPaperSize, use gtk_paper_size_free()
286  * to free it
287  *
288  * Since: 2.10
289  */
290 GtkPaperSize *
291 gtk_paper_size_new_from_ppd (const gchar *ppd_name,
292                              const gchar *ppd_display_name,
293                              gdouble      width,
294                              gdouble      height)
295 {
296   char *name;
297   const char *lookup_ppd_name;
298   char *freeme;
299   GtkPaperSize *size;
300   int i;
301
302   lookup_ppd_name = ppd_name;
303
304   freeme = NULL;
305   /* Strip out Traverse suffix in matching. */
306   if (g_str_has_suffix (ppd_name, ".Transverse"))
307     {
308       lookup_ppd_name = freeme =
309         g_strndup (ppd_name, strlen (ppd_name) - strlen (".Transverse"));
310     }
311
312   for (i = 0; i < G_N_ELEMENTS(standard_names_offsets); i++)
313     {
314       if (standard_names_offsets[i].ppd_name != -1 &&
315           strcmp (paper_names + standard_names_offsets[i].ppd_name, lookup_ppd_name) == 0)
316         {
317           size = gtk_paper_size_new_from_info (&standard_names_offsets[i]);
318           goto out;
319         }
320     }
321
322   for (i = 0; i < G_N_ELEMENTS(extra_ppd_names_offsets); i++)
323     {
324       if (strcmp (paper_names + extra_ppd_names_offsets[i].ppd_name, lookup_ppd_name) == 0)
325         {
326           size = gtk_paper_size_new (paper_names + extra_ppd_names_offsets[i].standard_name);
327           goto out;
328         }
329     }
330
331   name = g_strconcat ("ppd_", ppd_name, NULL);
332   size = gtk_paper_size_new_custom (name, ppd_display_name, width, height, GTK_UNIT_POINTS);
333   g_free (name);
334
335  out:
336
337   if (size->info == NULL ||
338       size->info->ppd_name == -1 ||
339       strcmp (paper_names + size->info->ppd_name, ppd_name) != 0)
340     size->ppd_name = g_strdup (ppd_name);
341
342   g_free (freeme);
343
344   return size;
345 }
346
347 /**
348  * gtk_paper_size_new_custom:
349  * @name: the paper name
350  * @display_name: the human-readable name
351  * @width: the paper width, in units of @unit
352  * @height: the paper height, in units of @unit
353  * @unit: the unit for @width and @height
354  *
355  * Creates a new #GtkPaperSize object with the
356  * given parameters.
357  *
358  * Return value: a new #GtkPaperSize object, use gtk_paper_size_free()
359  * to free it
360  *
361  * Since: 2.10
362  */
363 GtkPaperSize *
364 gtk_paper_size_new_custom (const gchar *name,
365                            const gchar *display_name,
366                            gdouble      width,
367                            gdouble      height,
368                            GtkUnit      unit)
369 {
370   GtkPaperSize *size;
371   g_return_val_if_fail (name != NULL, NULL);
372   g_return_val_if_fail (unit != GTK_UNIT_PIXEL, NULL);
373
374   size = g_slice_new0 (GtkPaperSize);
375
376   size->name = g_strdup (name);
377   size->display_name = g_strdup (display_name);
378   size->is_custom = TRUE;
379
380   size->width = _gtk_print_convert_to_mm (width, unit);
381   size->height = _gtk_print_convert_to_mm (height, unit);
382
383   return size;
384 }
385
386 /**
387  * gtk_paper_size_copy:
388  * @other: a #GtkPaperSize
389  *
390  * Copies an existing #GtkPaperSize.
391  *
392  * Return value: a copy of @other
393  *
394  * Since: 2.10
395  */
396 GtkPaperSize *
397 gtk_paper_size_copy (GtkPaperSize *other)
398 {
399   GtkPaperSize *size;
400
401   size = g_slice_new0 (GtkPaperSize);
402
403   size->info = other->info;
404   if (other->name)
405     size->name = g_strdup (other->name);
406   if (other->display_name)
407     size->display_name = g_strdup (other->display_name);
408   if (other->ppd_name)
409     size->ppd_name = g_strdup (other->ppd_name);
410
411   size->width = other->width;
412   size->height = other->height;
413   size->is_custom = other->is_custom;
414
415   return size;
416 }
417
418 /**
419  * gtk_paper_size_free:
420  * @size: a #GtkPaperSize
421  *
422  * Free the given #GtkPaperSize object.
423  *
424  * Since: 2.10
425  */
426 void
427 gtk_paper_size_free (GtkPaperSize *size)
428 {
429   g_free (size->name);
430   g_free (size->display_name);
431   g_free (size->ppd_name);
432
433   g_slice_free (GtkPaperSize, size);
434 }
435
436 /**
437  * gtk_paper_size_is_equal:
438  * @size1: a #GtkPaperSize object
439  * @size2: another #GtkPaperSize object
440  *
441  * Compares two #GtkPaperSize objects.
442  *
443  * Return value: %TRUE, if @size1 and @size2
444  * represent the same paper size
445  *
446  * Since: 2.10
447  */
448 gboolean
449 gtk_paper_size_is_equal (GtkPaperSize *size1,
450                          GtkPaperSize *size2)
451 {
452   if (size1->info != NULL && size2->info != NULL)
453     return size1->info == size2->info;
454
455   return strcmp (gtk_paper_size_get_name (size1),
456                  gtk_paper_size_get_name (size2)) == 0;
457 }
458
459 GList * _gtk_load_custom_papers (void);
460
461 /**
462  * gtk_paper_size_get_paper_sizes:
463  * @include_custom: whether to include custom paper sizes
464  *     as defined in the page setup dialog
465  *
466  * Creates a list of known paper sizes.
467  *
468  * Return value:  (element-type GtkPaperSize) (transfer full): a newly allocated list of newly
469  *    allocated #GtkPaperSize objects
470  *
471  * Since: 2.12
472  */
473 GList *
474 gtk_paper_size_get_paper_sizes (gboolean include_custom)
475 {
476   GList *list = NULL;
477   guint i;
478 #ifdef G_OS_UNIX                /* _gtk_load_custom_papers() only on Unix so far  */
479   if (include_custom)
480     {
481       GList *page_setups, *l;
482
483       page_setups = _gtk_load_custom_papers ();
484       for (l = page_setups; l != NULL; l = l->next)
485         {
486           GtkPageSetup *setup = (GtkPageSetup *) l->data;
487           GtkPaperSize *size;
488
489           size = gtk_page_setup_get_paper_size (setup);
490           list = g_list_prepend (list, gtk_paper_size_copy (size));
491         }
492
493       g_list_foreach (page_setups, (GFunc) g_object_unref, NULL);
494       g_list_free (page_setups);
495     }
496 #endif
497   for (i = 0; i < G_N_ELEMENTS (standard_names_offsets); ++i)
498     {
499        GtkPaperSize *size;
500
501        size = gtk_paper_size_new_from_info (&standard_names_offsets[i]);
502        list = g_list_prepend (list, size);
503     }
504
505   return g_list_reverse (list);
506 }
507
508
509 /**
510  * gtk_paper_size_get_name:
511  * @size: a #GtkPaperSize object
512  *
513  * Gets the name of the #GtkPaperSize.
514  *
515  * Return value: the name of @size
516  *
517  * Since: 2.10
518  */
519 const gchar *
520 gtk_paper_size_get_name (GtkPaperSize *size)
521 {
522   if (size->name)
523     return size->name;
524   g_assert (size->info != NULL);
525   return paper_names + size->info->name;
526 }
527
528 /**
529  * gtk_paper_size_get_display_name:
530  * @size: a #GtkPaperSize object
531  *
532  * Gets the human-readable name of the #GtkPaperSize.
533  *
534  * Return value: the human-readable name of @size
535  *
536  * Since: 2.10
537  */
538 const gchar *
539 gtk_paper_size_get_display_name (GtkPaperSize *size)
540 {
541   const gchar *display_name;
542
543   if (size->display_name)
544     return size->display_name;
545
546   g_assert (size->info != NULL);
547
548   display_name = paper_names + size->info->display_name;
549   return g_dpgettext2 (GETTEXT_PACKAGE, "paper size", display_name);
550 }
551
552 /**
553  * gtk_paper_size_get_ppd_name:
554  * @size: a #GtkPaperSize object
555  *
556  * Gets the PPD name of the #GtkPaperSize, which
557  * may be %NULL.
558  *
559  * Return value: the PPD name of @size
560  *
561  * Since: 2.10
562  */
563 const gchar *
564 gtk_paper_size_get_ppd_name (GtkPaperSize *size)
565 {
566   if (size->ppd_name)
567     return size->ppd_name;
568   if (size->info)
569     return paper_names + size->info->ppd_name;
570   return NULL;
571 }
572
573 /**
574  * gtk_paper_size_get_width:
575  * @size: a #GtkPaperSize object
576  * @unit: the unit for the return value
577  *
578  * Gets the paper width of the #GtkPaperSize, in
579  * units of @unit.
580  *
581  * Return value: the paper width
582  *
583  * Since: 2.10
584  */
585 gdouble
586 gtk_paper_size_get_width (GtkPaperSize *size,
587                           GtkUnit       unit)
588 {
589   return _gtk_print_convert_from_mm (size->width, unit);
590 }
591
592 /**
593  * gtk_paper_size_get_height:
594  * @size: a #GtkPaperSize object
595  * @unit: the unit for the return value
596  *
597  * Gets the paper height of the #GtkPaperSize, in
598  * units of @unit.
599  *
600  * Return value: the paper height
601  *
602  * Since: 2.10
603  */
604 gdouble
605 gtk_paper_size_get_height (GtkPaperSize *size,
606                            GtkUnit       unit)
607 {
608   return _gtk_print_convert_from_mm (size->height, unit);
609 }
610
611 /**
612  * gtk_paper_size_is_custom:
613  * @size: a #GtkPaperSize object
614  *
615  * Returns %TRUE if @size is not a standard paper size.
616  *
617  * Return value: whether @size is a custom paper size.
618  **/
619 gboolean
620 gtk_paper_size_is_custom (GtkPaperSize *size)
621 {
622   return size->is_custom;
623 }
624
625 /**
626  * gtk_paper_size_set_size:
627  * @size: a custom #GtkPaperSize object
628  * @width: the new width in units of @unit
629  * @height: the new height in units of @unit
630  * @unit: the unit for @width and @height
631  *
632  * Changes the dimensions of a @size to @width x @height.
633  *
634  * Since: 2.10
635  */
636 void
637 gtk_paper_size_set_size (GtkPaperSize *size,
638                          gdouble       width,
639                          gdouble       height,
640                          GtkUnit       unit)
641 {
642   g_return_if_fail (size != NULL);
643   g_return_if_fail (size->is_custom);
644
645   size->width = _gtk_print_convert_to_mm (width, unit);
646   size->height = _gtk_print_convert_to_mm (height, unit);
647 }
648
649 #define NL_PAPER_GET(x)         \
650   ((union { char *string; unsigned int word; })nl_langinfo(x)).word
651
652 /**
653  * gtk_paper_size_get_default:
654  *
655  * Returns the name of the default paper size, which
656  * depends on the current locale.
657  *
658  * Return value: the name of the default paper size. The string
659  * is owned by GTK+ and should not be modified.
660  *
661  * Since: 2.10
662  */
663 const gchar *
664 gtk_paper_size_get_default (void)
665 {
666   char *locale, *freeme = NULL;
667   const char *paper_size;
668
669 #if defined(HAVE__NL_PAPER_HEIGHT) && defined(HAVE__NL_PAPER_WIDTH)
670   {
671     int width = NL_PAPER_GET (_NL_PAPER_WIDTH);
672     int height = NL_PAPER_GET (_NL_PAPER_HEIGHT);
673
674     if (width == 210 && height == 297)
675       return GTK_PAPER_NAME_A4;
676
677     if (width == 216 && height == 279)
678       return GTK_PAPER_NAME_LETTER;
679   }
680 #endif
681
682 #ifdef G_OS_WIN32
683   freeme = locale = g_win32_getlocale ();
684 #elif defined(LC_PAPER)
685   locale = setlocale(LC_PAPER, NULL);
686 #else
687   locale = setlocale(LC_MESSAGES, NULL);
688 #endif
689
690   if (!locale)
691     return GTK_PAPER_NAME_A4;
692
693   /* CLDR 1.8.1
694    * http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/territory_language_information.html
695    */
696   if (g_regex_match_simple("[^_.@]{2,3}_(BZ|CA|CL|CO|CR|GT|MX|NI|PA|PH|PR|SV|US|VE)",
697                            locale, G_REGEX_ANCHORED, G_REGEX_MATCH_ANCHORED))
698     paper_size = GTK_PAPER_NAME_LETTER;
699   else
700     paper_size = GTK_PAPER_NAME_A4;
701
702   g_free (freeme);
703   return paper_size;
704 }
705
706 /* These get the default margins used for the paper size. Its
707  * larger than most printers margins, so that it will be within
708  * the imageble area on any printer.
709  *
710  * I've taken the actual values used from the OSX page setup dialog.
711  * I'm not sure exactly where they got these values for, but might
712  * correspond to this (from ghostscript docs):
713  *
714  * All DeskJets have 0.5 inches (1.27cm) of unprintable bottom margin,
715  * due to the mechanical arrangement used to grab the paper. Side margins
716  * are approximately 0.25 inches (0.64cm) for U.S. letter paper, and 0.15
717  * inches (0.38cm) for A4.
718  */
719
720 /**
721  * gtk_paper_size_get_default_top_margin:
722  * @size: a #GtkPaperSize object
723  * @unit: the unit for the return value
724  *
725  * Gets the default top margin for the #GtkPaperSize.
726  *
727  * Return value: the default top margin
728  *
729  * Since: 2.10
730  */
731 gdouble
732 gtk_paper_size_get_default_top_margin (GtkPaperSize *size,
733                                        GtkUnit       unit)
734 {
735   gdouble margin;
736
737   margin = _gtk_print_convert_to_mm (0.25, GTK_UNIT_INCH);
738   return _gtk_print_convert_from_mm (margin, unit);
739 }
740
741 /**
742  * gtk_paper_size_get_default_bottom_margin:
743  * @size: a #GtkPaperSize object
744  * @unit: the unit for the return value
745  *
746  * Gets the default bottom margin for the #GtkPaperSize.
747  *
748  * Return value: the default bottom margin
749  *
750  * Since: 2.10
751  */
752 gdouble
753 gtk_paper_size_get_default_bottom_margin (GtkPaperSize *size,
754                                           GtkUnit       unit)
755 {
756   gdouble margin;
757   const gchar *name;
758
759   margin = _gtk_print_convert_to_mm (0.25, GTK_UNIT_INCH);
760
761   name = gtk_paper_size_get_name (size);
762   if (strcmp (name, "na_letter") == 0 ||
763       strcmp (name, "na_legal") == 0 ||
764       strcmp (name, "iso_a4") == 0)
765     margin = _gtk_print_convert_to_mm (0.56, GTK_UNIT_INCH);
766
767   return _gtk_print_convert_from_mm (margin, unit);
768 }
769
770 /**
771  * gtk_paper_size_get_default_left_margin:
772  * @size: a #GtkPaperSize object
773  * @unit: the unit for the return value
774  *
775  * Gets the default left margin for the #GtkPaperSize.
776  *
777  * Return value: the default left margin
778  *
779  * Since: 2.10
780  */
781 gdouble
782 gtk_paper_size_get_default_left_margin (GtkPaperSize *size,
783                                         GtkUnit       unit)
784 {
785   gdouble margin;
786
787   margin = _gtk_print_convert_to_mm (0.25, GTK_UNIT_INCH);
788   return _gtk_print_convert_from_mm (margin, unit);
789 }
790
791 /**
792  * gtk_paper_size_get_default_right_margin:
793  * @size: a #GtkPaperSize object
794  * @unit: the unit for the return value
795  *
796  * Gets the default right margin for the #GtkPaperSize.
797  *
798  * Return value: the default right margin
799  *
800  * Since: 2.10
801  */
802 gdouble
803 gtk_paper_size_get_default_right_margin (GtkPaperSize *size,
804                                          GtkUnit       unit)
805 {
806   gdouble margin;
807
808   margin = _gtk_print_convert_to_mm (0.25, GTK_UNIT_INCH);
809   return _gtk_print_convert_from_mm (margin, unit);
810 }
811
812 /**
813  * gtk_paper_size_new_from_key_file:
814  * @key_file: the #GKeyFile to retrieve the papersize from
815  * @group_name: the name ofthe group in the key file to read,
816  *     or %NULL to read the first group
817  * @error: (allow-none): return location for an error, or %NULL
818  *
819  * Reads a paper size from the group @group_name in the key file
820  * @key_file.
821  *
822  * Returns: a new #GtkPaperSize object with the restored
823  *     paper size, or %NULL if an error occurred
824  *
825  * Since: 2.12
826  */
827 GtkPaperSize *
828 gtk_paper_size_new_from_key_file (GKeyFile     *key_file,
829                                   const gchar  *group_name,
830                                   GError      **error)
831 {
832   GtkPaperSize *paper_size = NULL;
833   gchar *name = NULL;
834   gchar *ppd_name = NULL;
835   gchar *display_name = NULL;
836   gchar *freeme = NULL;
837   gdouble width, height;
838   GError *err = NULL;
839
840   g_return_val_if_fail (key_file != NULL, NULL);
841
842   if (!group_name)
843     group_name = freeme = g_key_file_get_start_group (key_file);
844   if (!group_name || !g_key_file_has_group (key_file, group_name))
845     {
846       g_set_error_literal (error,
847                            GTK_PRINT_ERROR,
848                            GTK_PRINT_ERROR_INVALID_FILE,
849                            _("Not a valid page setup file"));
850       goto out;
851     }
852
853 #define GET_DOUBLE(kf, group, name, v) \
854   v = g_key_file_get_double (kf, group, name, &err); \
855   if (err != NULL) \
856     {\
857       g_propagate_error (error, err);\
858       goto out;\
859     }
860
861   GET_DOUBLE (key_file, group_name, "Width", width);
862   GET_DOUBLE (key_file, group_name, "Height", height);
863
864 #undef GET_DOUBLE
865
866   name = g_key_file_get_string (key_file, group_name,
867                                 "Name", NULL);
868   ppd_name = g_key_file_get_string (key_file, group_name,
869                                     "PPDName", NULL);
870   display_name = g_key_file_get_string (key_file, group_name,
871                                         "DisplayName", NULL);
872   /* Fallback for old ~/.gtk-custom-paper entries */
873   if (!display_name)
874     display_name = g_strdup (name);
875
876   if (ppd_name != NULL)
877     paper_size = gtk_paper_size_new_from_ppd (ppd_name,
878                                               display_name,
879                                               _gtk_print_convert_from_mm (width, GTK_UNIT_POINTS),
880                                               _gtk_print_convert_from_mm (height, GTK_UNIT_POINTS));
881   else if (name != NULL)
882     paper_size = gtk_paper_size_new_custom (name, display_name,
883                                             width, height, GTK_UNIT_MM);
884   else
885     {
886       g_set_error_literal (error,
887                            GTK_PRINT_ERROR,
888                            GTK_PRINT_ERROR_INVALID_FILE,
889                            _("Not a valid page setup file"));
890       goto out;
891     }
892
893   g_assert (paper_size != NULL);
894
895 out:
896   g_free (ppd_name);
897   g_free (name);
898   g_free (display_name);
899   g_free (freeme);
900
901   return paper_size;
902 }
903
904 /**
905  * gtk_paper_size_to_key_file:
906  * @size: a #GtkPaperSize
907  * @key_file: the #GKeyFile to save the paper size to
908  * @group_name: the group to add the settings to in @key_file
909  *
910  * This function adds the paper size from @size to @key_file.
911  *
912  * Since: 2.12
913  */
914 void
915 gtk_paper_size_to_key_file (GtkPaperSize *size,
916                             GKeyFile     *key_file,
917                             const gchar  *group_name)
918 {
919   const char *name, *ppd_name, *display_name;
920
921   g_return_if_fail (size != NULL);
922   g_return_if_fail (key_file != NULL);
923
924   name = gtk_paper_size_get_name (size);
925   display_name = gtk_paper_size_get_display_name (size);
926   ppd_name = gtk_paper_size_get_ppd_name (size);
927
928   if (ppd_name != NULL)
929     g_key_file_set_string (key_file, group_name,
930                            "PPDName", ppd_name);
931   else
932     g_key_file_set_string (key_file, group_name,
933                            "Name", name);
934
935   if (display_name)
936     g_key_file_set_string (key_file, group_name,
937                            "DisplayName", display_name);
938
939   g_key_file_set_double (key_file, group_name,
940                          "Width", gtk_paper_size_get_width (size, GTK_UNIT_MM));
941   g_key_file_set_double (key_file, group_name,
942                          "Height", gtk_paper_size_get_height (size, GTK_UNIT_MM));
943 }