]> Pileus Git - ~andy/gtk/blob - gtk/gtkpapersize.c
Custom tab label
[~andy/gtk] / gtk / gtkpapersize.c
1 /* GTK - The GIMP Toolkit
2  * gtkpapersize.c: Paper Size
3  * Copyright (C) 2006, Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  */
20
21 #include "config.h"
22 #include <string.h>
23 #include <stdlib.h>
24 #include <locale.h>
25 #if defined(HAVE__NL_PAPER_HEIGHT) && defined(HAVE__NL_PAPER_WIDTH)
26 #include <langinfo.h>
27 #endif
28
29 #include "gtkpapersize.h"
30 #include "gtkprintutils.h"
31 #include "gtkintl.h"
32 #include "gtkalias.h"
33
34 #include "paper_names_offsets.c"
35
36 struct _GtkPaperSize
37 {
38   const PaperInfo *info;
39
40   /* If these are not set we fall back to info */
41   gchar *name;
42   gchar *display_name;
43   gchar *ppd_name;
44   
45   gdouble width, height; /* Stored in mm */
46   gboolean is_custom;
47 };
48
49 GType
50 gtk_paper_size_get_type (void)
51 {
52   static GType our_type = 0;
53   
54   if (our_type == 0)
55     our_type = g_boxed_type_register_static ("GtkPaperSize",
56                                              (GBoxedCopyFunc)gtk_paper_size_copy,
57                                              (GBoxedFreeFunc)gtk_paper_size_free);
58   return our_type;
59 }
60
61 static const PaperInfo *
62 lookup_paper_info (const gchar *name)
63 {
64   int lower = 0;
65   int upper = G_N_ELEMENTS (standard_names_offsets) - 1;
66   int mid;
67   int cmp;
68
69   do 
70     {
71        mid = (lower + upper) / 2;
72        cmp = strcmp (name, paper_names + standard_names_offsets[mid].name);
73        if (cmp < 0)
74          upper = mid - 1;
75        else if (cmp > 0)
76          lower = mid + 1;
77        else
78          return &standard_names_offsets[mid];
79     }
80   while (lower <= upper);
81
82   return NULL;
83 }
84
85 static gboolean
86 parse_media_size (const gchar *size,
87                   gdouble     *width_mm, 
88                   gdouble     *height_mm)
89 {
90   const char *p;
91   char *e;
92   double short_dim, long_dim;
93
94   p = size;
95   
96   short_dim = g_ascii_strtod (p, &e);
97
98   if (p == e || *e != 'x')
99     return FALSE;
100
101   p = e + 1; /* Skip x */
102
103   long_dim = g_ascii_strtod (p, &e);
104
105   if (p == e)
106     return FALSE;
107
108   p = e;
109
110   if (strcmp (p, "in") == 0)
111     {
112       short_dim = short_dim * MM_PER_INCH;
113       long_dim = long_dim * MM_PER_INCH;
114     }
115   else if (strcmp (p, "mm") != 0)
116     return FALSE;
117
118   if (width_mm)
119     *width_mm = short_dim;
120   if (height_mm)
121     *height_mm = long_dim;
122   
123   return TRUE;  
124 }
125
126 static gboolean
127 parse_full_media_size_name (const gchar  *full_name,
128                             gchar       **name,
129                             gdouble      *width_mm, 
130                             gdouble      *height_mm)
131 {
132   const char *p;
133   const char *end_of_name;
134   
135   /* From the spec:
136    media-size-self-describing-name =
137         ( class-in "_" size-name "_" short-dim "x" long-dim "in" ) |
138         ( class-mm "_" size-name "_" short-dim "x" long-dim "mm" )
139    class-in = "custom" | "na" | "asme" | "roc" | "oe"
140    class-mm = "custom" | "iso" | "jis" | "jpn" | "prc" | "om"
141    size-name = ( lowalpha | digit ) *( lowalpha | digit | "-" )
142    short-dim = dim
143    long-dim = dim
144    dim = integer-part [fraction-part] | "0" fraction-part
145    integer-part = non-zero-digit *digit
146    fraction-part = "." *digit non-zero-digit
147    lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
148               "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
149               "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
150    non-zero-digit = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
151    digit    = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
152  */
153
154   p = strchr (full_name, '_');
155   if (p == NULL)
156     return FALSE;
157
158   p++; /* Skip _ */
159   
160   p = strchr (p, '_');
161   if (p == NULL)
162     return FALSE;
163
164   end_of_name = p;
165
166   p++; /* Skip _ */
167
168   if (!parse_media_size (p, width_mm, height_mm))
169     return FALSE;
170
171   if (name)
172     *name = g_strndup (full_name, end_of_name - full_name);
173   
174   return TRUE;  
175 }
176
177 static GtkPaperSize *
178 gtk_paper_size_new_from_info (const PaperInfo *info)
179 {
180   GtkPaperSize *size;
181   
182   size = g_new0 (GtkPaperSize, 1);
183   size->info = info;
184   size->width = info->width;
185   size->height = info->height;
186   
187   return size;
188 }
189
190 /**
191  * gtk_paper_size_new:
192  * @name: a paper size name, or %NULL
193  * 
194  * Creates a new #GtkPaperSize object by parsing a 
195  * PWG 5101.1-2002 PWG <!-- FIXME link here -->
196  * paper name. 
197  *
198  * If @name is %NULL, the default paper size is returned,
199  * see gtk_paper_size_get_default().
200  * 
201  * Return value: a new #GtkPaperSize, use gtk_paper_size_free()
202  * to free it
203  *
204  * Since: 2.10
205  */
206 GtkPaperSize *
207 gtk_paper_size_new (const gchar *name)
208 {
209   GtkPaperSize *size;
210   char *short_name;
211   double width, height;
212   const PaperInfo *info;
213
214   if (name == NULL)
215     name = gtk_paper_size_get_default ();
216   
217   if (parse_full_media_size_name (name, &short_name, &width, &height))
218     {
219       size = g_new0 (GtkPaperSize, 1);
220
221       size->width = width;
222       size->height = height;
223       size->name = short_name;
224       size->display_name = g_strdup (short_name);
225     }
226   else
227     {
228       info = lookup_paper_info (name);
229       if (info != NULL)
230         size = gtk_paper_size_new_from_info (info);
231       else
232         {
233           g_warning ("Unknown paper size %s\n", name);
234           size = g_new0 (GtkPaperSize, 1);
235           size->name = g_strdup (name);
236           size->display_name = g_strdup (name);
237           /* Default to A4 size */
238           size->width = 210;
239           size->height = 297;
240         }
241     }
242   
243   return size;
244 }
245
246 /**
247  * gtk_paper_size_new_from_ppd:
248  * @ppd_name: a PPD paper name
249  * @ppd_display_name: the corresponding human-readable name
250  * @width: the paper width, in points
251  * @height: the paper height in points
252  * 
253  * Creates a new #GtkPaperSize object by using 
254  * PPD information. 
255  * 
256  * If @ppd_name is not a recognized PPD paper name, 
257  * @ppd_display_name, @width and @height are used to 
258  * construct a custom #GtkPaperSize object.
259  *
260  * Return value: a new #GtkPaperSize, use gtk_paper_size_free()
261  * to free it
262  *
263  * Since: 2.10
264  */
265 GtkPaperSize *
266 gtk_paper_size_new_from_ppd (const gchar *ppd_name,
267                              const gchar *ppd_display_name,
268                              gdouble      width,
269                              gdouble      height)
270 {
271   char *name;
272   const char *lookup_ppd_name;
273   char *freeme;
274   GtkPaperSize *size;
275   int i;
276
277   lookup_ppd_name = ppd_name;
278   
279   freeme = NULL;
280   /* Strip out Traverse suffix in matching. */
281   if (g_str_has_suffix (ppd_name, ".Transverse"))
282     {
283       lookup_ppd_name = freeme =
284         g_strndup (ppd_name, strlen (ppd_name) - strlen (".Transverse"));
285     }
286   
287   for (i = 0; i < G_N_ELEMENTS(standard_names_offsets); i++)
288     {
289       if (standard_names_offsets[i].ppd_name != -1 &&
290           strcmp (paper_names + standard_names_offsets[i].ppd_name, lookup_ppd_name) == 0)
291         {
292           size = gtk_paper_size_new_from_info (&standard_names_offsets[i]);
293           goto out;
294         }
295     }
296   
297   for (i = 0; i < G_N_ELEMENTS(extra_ppd_names_offsets); i++)
298     {
299       if (strcmp (paper_names + extra_ppd_names_offsets[i].ppd_name, lookup_ppd_name) == 0)
300         {
301           size = gtk_paper_size_new (paper_names + extra_ppd_names_offsets[i].standard_name);
302           goto out;
303         }
304     }
305
306   name = g_strconcat ("ppd_", ppd_name, NULL);
307   size = gtk_paper_size_new_custom (name, ppd_display_name, width, height, GTK_UNIT_POINTS);
308   g_free (name);
309
310  out:
311
312   if (size->info == NULL ||
313       size->info->ppd_name == -1 ||
314       strcmp (paper_names + size->info->ppd_name, ppd_name) != 0)
315     size->ppd_name = g_strdup (ppd_name);
316   
317   g_free (freeme);
318   
319   return size;
320 }
321
322 /**
323  * gtk_paper_size_new_custom:
324  * @name: the paper name 
325  * @display_name: the human-readable name
326  * @width: the paper width, in units of @unit
327  * @height: the paper height, in units of @unit
328  * @unit: the unit for @width and @height
329  * 
330  * Creates a new #GtkPaperSize object with the
331  * given parameters.
332  * 
333  * Return value: a new #GtkPaperSize object, use gtk_paper_size_free()
334  * to free it
335  *
336  * Since: 2.10
337  */
338 GtkPaperSize *
339 gtk_paper_size_new_custom (const gchar *name, 
340                            const gchar *display_name,
341                            gdouble      width, 
342                            gdouble      height, 
343                            GtkUnit      unit)
344 {
345   GtkPaperSize *size;
346   g_return_val_if_fail (name != NULL, NULL);
347   g_return_val_if_fail (unit != GTK_UNIT_PIXEL, NULL);
348
349   size = g_new0 (GtkPaperSize, 1);
350   
351   size->name = g_strdup (name);
352   size->display_name = g_strdup (display_name);
353   size->is_custom = TRUE;
354   
355   size->width = _gtk_print_convert_to_mm (width, unit);
356   size->height = _gtk_print_convert_to_mm (height, unit);
357   
358   return size;
359 }
360
361 /**
362  * gtk_paper_size_copy:
363  * @other: a #GtkPaperSize
364  * 
365  * Copies an existing #GtkPaperSize.
366  * 
367  * Return value: a copy of @other
368  *
369  * Since: 2.10
370  */
371 GtkPaperSize *
372 gtk_paper_size_copy (GtkPaperSize *other)
373 {
374   GtkPaperSize *size;
375
376   size = g_new0 (GtkPaperSize, 1);
377
378   size->info = other->info;
379   if (other->name)
380     size->name = g_strdup (other->name);
381   if (other->display_name)
382     size->display_name = g_strdup (other->display_name);
383   if (other->ppd_name)
384     size->ppd_name = g_strdup (other->ppd_name);
385   
386   size->width = other->width;
387   size->height = other->height;
388   size->is_custom = other->is_custom;
389
390   return size;
391 }
392
393 /**
394  * gtk_paper_size_free:
395  * @size: a #GtkPaperSize
396  * 
397  * Free the given #GtkPaperSize object.
398  *
399  * Since: 2.10
400  */
401 void
402 gtk_paper_size_free (GtkPaperSize *size)
403 {
404   g_free (size->name);
405   g_free (size->display_name);
406   g_free (size->ppd_name);
407   g_free (size);
408 }
409
410 /**
411  * gtk_paper_size_is_equal:
412  * @size1: a #GtkPaperSize object
413  * @size2: another @GtkPaperSize object
414  * 
415  * Compares two #GtkPaperSize objects.
416  * 
417  * Return value: %TRUE, if @size1 and @size2 
418  * represent the same paper size
419  *
420  * Since: 2.10
421  */
422 gboolean
423 gtk_paper_size_is_equal (GtkPaperSize *size1,
424                          GtkPaperSize *size2)
425 {
426   if (size1->info != NULL && size2->info != NULL)
427     return size1->info == size2->info;
428   
429   return strcmp (gtk_paper_size_get_name (size1),
430                  gtk_paper_size_get_name (size2)) == 0;
431 }
432
433 /**
434  * gtk_paper_size_get_name:
435  * @size: a #GtkPaperSize object
436  * 
437  * Gets the name of the #GtkPaperSize.
438  * 
439  * Return value: the name of @size
440  *
441  * Since: 2.10
442  */
443 G_CONST_RETURN gchar *
444 gtk_paper_size_get_name (GtkPaperSize *size)
445 {
446   if (size->name)
447     return size->name;
448   g_assert (size->info != NULL);
449   return paper_names + size->info->name;
450 }
451
452 /**
453  * gtk_paper_size_get_display_name:
454  * @size: a #GtkPaperSize object
455  * 
456  * Gets the human-readable name of the #GtkPaperSize.
457  * 
458  * Return value: the human-readable name of @size
459  *
460  * Since: 2.10
461  */
462 G_CONST_RETURN gchar *
463 gtk_paper_size_get_display_name (GtkPaperSize *size)
464 {
465   const gchar *display_name;
466
467   if (size->display_name)
468     return size->display_name;
469
470   g_assert (size->info != NULL);
471
472   display_name = paper_names + size->info->display_name;
473   return g_strip_context (display_name, gettext (display_name));
474 }
475
476 /**
477  * gtk_paper_size_get_ppd_name:
478  * @size: a #GtkPaperSize object
479  * 
480  * Gets the PPD name of the #GtkPaperSize, which
481  * may be %NULL.
482  * 
483  * Return value: the PPD name of @size
484  *
485  * Since: 2.10
486  */
487 G_CONST_RETURN gchar *
488 gtk_paper_size_get_ppd_name (GtkPaperSize *size)
489 {
490   if (size->ppd_name)
491     return size->ppd_name;
492   if (size->info)
493     return paper_names + size->info->ppd_name;
494   return NULL;
495 }
496
497 /**
498  * gtk_paper_size_get_width:
499  * @size: a #GtkPaperSize object
500  * @unit: the unit for the return value
501  * 
502  * Gets the paper width of the #GtkPaperSize, in 
503  * units of @unit.
504  * 
505  * Return value: the paper width 
506  *
507  * Since: 2.10
508  */
509 gdouble
510 gtk_paper_size_get_width (GtkPaperSize *size, 
511                           GtkUnit       unit)
512 {
513   return _gtk_print_convert_from_mm (size->width, unit);
514 }
515
516 /**
517  * gtk_paper_size_get_height:
518  * @size: a #GtkPaperSize object
519  * @unit: the unit for the return value
520  * 
521  * Gets the paper height of the #GtkPaperSize, in 
522  * units of @unit.
523  * 
524  * Return value: the paper height 
525  *
526  * Since: 2.10
527  */
528 gdouble
529 gtk_paper_size_get_height (GtkPaperSize *size, 
530                            GtkUnit       unit)
531 {
532   return _gtk_print_convert_from_mm (size->height, unit);
533 }
534
535 /**
536  * gtk_paper_size_is_custom:
537  * @size: a #GtkPaperSize object
538  * 
539  * Returns %TRUE if @size is not a standard paper size.
540  * 
541  * Return value: whether @size is a custom paper size.
542  **/
543 gboolean
544 gtk_paper_size_is_custom (GtkPaperSize *size)
545 {
546   return size->is_custom;
547 }
548
549 /**
550  * gtk_paper_size_set_size:
551  * @size: a custom #GtkPaperSize object
552  * @width: the new width in units of @unit
553  * @height: the new height in units of @unit
554  * @unit: the unit for @width and @height
555  * 
556  * Changes the dimensions of a @size to @width x @height.
557  *
558  * Since: 2.10
559  */
560 void
561 gtk_paper_size_set_size (GtkPaperSize *size, 
562                          gdouble       width, 
563                          gdouble       height, 
564                          GtkUnit       unit)
565 {
566   g_return_if_fail (size != NULL);
567   g_return_if_fail (size->is_custom);
568
569   size->width = _gtk_print_convert_to_mm (width, unit);
570   size->height = _gtk_print_convert_to_mm (height, unit);
571 }
572
573 #define NL_PAPER_GET(x)         \
574   ((union { char *string; unsigned int word; })nl_langinfo(x)).word
575
576 /**
577  * gtk_paper_size_get_default:
578  *
579  * Returns the name of the default paper size, which 
580  * depends on the current locale.  
581  * 
582  * Return value: the name of the default paper size. The string
583  * is owned by GTK+ and should not be modified.
584  * 
585  * Since: 2.10
586  */
587 G_CONST_RETURN gchar *
588 gtk_paper_size_get_default (void)
589 {
590   char *locale, *freeme = NULL;
591   const char *paper_size;
592
593 #if defined(HAVE__NL_PAPER_HEIGHT) && defined(HAVE__NL_PAPER_WIDTH)
594   {
595     int width = NL_PAPER_GET (_NL_PAPER_WIDTH);
596     int height = NL_PAPER_GET (_NL_PAPER_HEIGHT);
597     
598     if (width == 210 && height == 297)
599       return GTK_PAPER_NAME_A4;
600     
601     if (width == 216 && height == 279)
602       return GTK_PAPER_NAME_LETTER;
603   }
604 #endif
605
606 #ifdef G_OS_WIN32
607   freeme = locale = g_win32_getlocale ();
608 #elif defined(LC_PAPER)
609   locale = setlocale(LC_PAPER, NULL);
610 #else
611   locale = setlocale(LC_MESSAGES, NULL);
612 #endif
613
614   if (!locale)
615     return GTK_PAPER_NAME_A4;
616
617   if (g_str_has_prefix (locale, "en_CA") ||
618       g_str_has_prefix (locale, "en_US") ||
619       g_str_has_prefix (locale, "es_PR") ||
620       g_str_has_prefix (locale, "es_US"))
621     paper_size = GTK_PAPER_NAME_LETTER;
622   else
623     paper_size = GTK_PAPER_NAME_A4;
624
625   g_free (freeme);
626   return paper_size;
627 }
628
629 /* These get the default margins used for the paper size. Its
630  * larger than most printers margins, so that it will be within
631  * the imageble area on any printer.
632  *
633  * I've taken the actual values used from the OSX page setup dialog.
634  * I'm not sure exactly where they got these values for, but might
635  * correspond to this (from ghostscript docs):
636  * 
637  * All DeskJets have 0.5 inches (1.27cm) of unprintable bottom margin,
638  * due to the mechanical arrangement used to grab the paper. Side margins
639  * are approximately 0.25 inches (0.64cm) for U.S. letter paper, and 0.15
640  * inches (0.38cm) for A4.
641  */
642
643 /**
644  * gtk_paper_size_get_default_top_margin:
645  * @size: a #GtkPaperSize object
646  * @unit: the unit for the return value
647  * 
648  * Gets the default top margin for the #GtkPaperSize.
649  * 
650  * Return value: the default top margin
651  *
652  * Since: 2.10
653  */
654 gdouble
655 gtk_paper_size_get_default_top_margin (GtkPaperSize *size, 
656                                        GtkUnit       unit)
657 {
658   gdouble margin;
659
660   margin = _gtk_print_convert_to_mm (0.25, GTK_UNIT_INCH);
661   return _gtk_print_convert_from_mm (margin, unit);
662 }
663
664 /**
665  * gtk_paper_size_get_default_bottom_margin:
666  * @size: a #GtkPaperSize object
667  * @unit: the unit for the return value
668  * 
669  * Gets the default bottom margin for the #GtkPaperSize.
670  * 
671  * Return value: the default bottom margin
672  *
673  * Since: 2.10
674  */
675 gdouble
676 gtk_paper_size_get_default_bottom_margin (GtkPaperSize *size, 
677                                           GtkUnit       unit)
678 {
679   gdouble margin;
680   const gchar *name;
681
682   margin = _gtk_print_convert_to_mm (0.25, GTK_UNIT_INCH);
683
684   name = gtk_paper_size_get_name (size);
685   if (strcmp (name, "na_letter") == 0 ||
686       strcmp (name, "na_legal") == 0 ||
687       strcmp (name, "iso_a4") == 0)
688     margin = _gtk_print_convert_to_mm (0.56, GTK_UNIT_INCH);
689   
690   return _gtk_print_convert_from_mm (margin, unit);
691 }
692
693 /**
694  * gtk_paper_size_get_default_left_margin:
695  * @size: a #GtkPaperSize object
696  * @unit: the unit for the return value
697  * 
698  * Gets the default left margin for the #GtkPaperSize.
699  * 
700  * Return value: the default left margin
701  *
702  * Since: 2.10
703  */
704 gdouble
705 gtk_paper_size_get_default_left_margin (GtkPaperSize *size, 
706                                         GtkUnit       unit)
707 {
708   gdouble margin;
709
710   margin = _gtk_print_convert_to_mm (0.25, GTK_UNIT_INCH);
711   return _gtk_print_convert_from_mm (margin, unit);
712 }
713
714 /**
715  * gtk_paper_size_get_default_right_margin:
716  * @size: a #GtkPaperSize object
717  * @unit: the unit for the return value
718  * 
719  * Gets the default right margin for the #GtkPaperSize.
720  * 
721  * Return value: the default right margin
722  *
723  * Since: 2.10
724  */
725 gdouble
726 gtk_paper_size_get_default_right_margin (GtkPaperSize *size, 
727                                          GtkUnit       unit)
728 {
729   gdouble margin;
730
731   margin = _gtk_print_convert_to_mm (0.25, GTK_UNIT_INCH);
732   return _gtk_print_convert_from_mm (margin, unit);
733 }
734
735
736 #define __GTK_PAPER_SIZE_C__
737 #include "gtkaliasdef.c"