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