]> Pileus Git - ~andy/gtk/blob - gtk/gtkpapersize.c
7dfeb9019a234a2e62c4c5a0b30e9d3da1605c1c
[~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 "gtkalias.h"
31
32 #define MM_PER_INCH 25.4
33 #define POINTS_PER_INCH 72
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   char *name;
43   char *display_name;
44   char *ppd_name;
45   
46   double 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 ("GtkPaperSize",
57                                              (GBoxedCopyFunc)gtk_paper_size_copy,
58                                              (GBoxedFreeFunc)gtk_paper_size_free);
59   return our_type;
60 }
61
62 static PaperInfo *
63 lookup_paper_info (const char *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 double
87 to_mm (double len, GtkUnit unit)
88 {
89   switch (unit)
90     {
91     case GTK_UNIT_MM:
92       return len;
93     case GTK_UNIT_INCH:
94       return len * MM_PER_INCH;
95     default:
96     case GTK_UNIT_PIXEL:
97       g_warning ("Unsupported unit");
98       /* Fall through */
99     case GTK_UNIT_POINTS:
100       return len * (MM_PER_INCH / POINTS_PER_INCH);
101       break;
102     }
103 }
104
105 static double
106 from_mm (double len, GtkUnit unit)
107 {
108   switch (unit)
109     {
110     case GTK_UNIT_MM:
111       return len;
112     case GTK_UNIT_INCH:
113       return len / MM_PER_INCH;
114     default:
115     case GTK_UNIT_PIXEL:
116       g_warning ("Unsupported unit");
117       /* Fall through */
118     case GTK_UNIT_POINTS:
119       return len / (MM_PER_INCH / POINTS_PER_INCH);
120       break;
121     }
122 }
123
124 static gboolean
125 parse_media_size (const char *size,
126                   double *width_mm, double *height_mm)
127 {
128   const char *p;
129   char *e;
130   double short_dim, long_dim;
131
132   p = size;
133   
134   short_dim = g_ascii_strtod (p, &e);
135
136   if (p == e || *e != 'x')
137     return FALSE;
138
139   p = e + 1; /* Skip x */
140
141   long_dim = g_ascii_strtod (p, &e);
142
143   if (p == e)
144     return FALSE;
145
146   p = e;
147
148   if (strcmp (p, "in") == 0)
149     {
150       short_dim = short_dim * MM_PER_INCH;
151       long_dim = long_dim * MM_PER_INCH;
152     }
153   else if (strcmp (p, "mm") != 0)
154     return FALSE;
155
156   if (width_mm)
157     *width_mm = short_dim;
158   if (height_mm)
159     *height_mm = long_dim;
160   
161   return TRUE;  
162 }
163
164 static gboolean
165 parse_full_media_size_name (const char *full_name,
166                             char **name,
167                             double *width_mm, double *height_mm)
168 {
169   const char *p;
170   const char *end_of_name;
171   
172   /* From the spec:
173    media-size-self-describing-name =
174         ( class-in "_" size-name "_" short-dim "x" long-dim "in" ) |
175         ( class-mm "_" size-name "_" short-dim "x" long-dim "mm" )
176    class-in = "custom" | "na" | "asme" | "roc" | "oe"
177    class-mm = "custom" | "iso" | "jis" | "jpn" | "prc" | "om"
178    size-name = ( lowalpha | digit ) *( lowalpha | digit | "-" )
179    short-dim = dim
180    long-dim = dim
181    dim = integer-part [fraction-part] | "0" fraction-part
182    integer-part = non-zero-digit *digit
183    fraction-part = "." *digit non-zero-digit
184    lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" |
185               "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" |
186               "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z"
187    non-zero-digit = "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
188    digit    = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
189  */
190
191   p = strchr (full_name, '_');
192   if (p == NULL)
193     return FALSE;
194
195   p++; /* Skip _ */
196   
197   p = strchr (p, '_');
198   if (p == NULL)
199     return FALSE;
200
201   end_of_name = p;
202
203   p++; /* Skip _ */
204
205   if (!parse_media_size (p, width_mm, height_mm))
206     return FALSE;
207
208   if (name)
209     *name = g_strndup (full_name, end_of_name - full_name);
210   
211   return TRUE;  
212 }
213
214 static GtkPaperSize *
215 gtk_paper_size_new_from_info (const PaperInfo *info)
216 {
217   GtkPaperSize *size;
218   
219   size = g_new0 (GtkPaperSize, 1);
220   size->info = info;
221   size->width = info->width;
222   size->height = info->height;
223   
224   return size;
225 }
226
227 GtkPaperSize *
228 gtk_paper_size_new (const char *name)
229 {
230   GtkPaperSize *size;
231   char *short_name;
232   double width, height;
233   PaperInfo *info;
234
235   if (name == NULL)
236     name = gtk_paper_size_get_default ();
237   
238   if (parse_full_media_size_name (name, &short_name, &width, &height))
239     {
240       size = g_new0 (GtkPaperSize, 1);
241
242       size->width = width;
243       size->height = height;
244       size->name = short_name;
245       size->display_name = g_strdup (short_name);
246     }
247   else
248     {
249       info = lookup_paper_info (name);
250       if (info != NULL)
251         size = gtk_paper_size_new_from_info (info);
252       else
253         {
254           g_warning ("Unknown paper size %s\n", name);
255           size = g_new0 (GtkPaperSize, 1);
256           size->name = g_strdup (name);
257           size->display_name = g_strdup (name);
258           /* Default to A4 size */
259           size->width = 210;
260           size->height = 297;
261         }
262     }
263   
264   return size;
265 }
266
267 GtkPaperSize *
268 gtk_paper_size_new_from_ppd (const char *ppd_name,
269                              const char *ppd_display_name,
270                              double width,
271                              double height)
272 {
273   char *name;
274   const char *lookup_ppd_name;
275   char *freeme;
276   GtkPaperSize *size;
277   int i;
278
279   lookup_ppd_name = ppd_name;
280   
281   freeme = NULL;
282   /* Strip out Traverse suffix in matching. */
283   if (g_str_has_suffix (ppd_name, ".Transverse"))
284     {
285       lookup_ppd_name = freeme =
286         g_strndup (ppd_name, strlen (ppd_name) - strlen (".Transverse"));
287     }
288   
289   for (i = 0; i < G_N_ELEMENTS(standard_names_offsets); i++)
290     {
291       if (standard_names_offsets[i].ppd_name != -1 &&
292           strcmp (paper_names + standard_names_offsets[i].ppd_name, lookup_ppd_name) == 0)
293         {
294           size = gtk_paper_size_new_from_info (&standard_names_offsets[i]);
295           goto out;
296         }
297     }
298   
299   for (i = 0; i < G_N_ELEMENTS(extra_ppd_names_offsets); i++)
300     {
301       if (strcmp (paper_names + extra_ppd_names_offsets[i].ppd_name, lookup_ppd_name) == 0)
302         {
303           size = gtk_paper_size_new (paper_names + extra_ppd_names_offsets[i].standard_name);
304           goto out;
305         }
306     }
307
308   name = g_strdup_printf ("ppd_%s", ppd_name);
309   size = gtk_paper_size_new_custom (name, ppd_display_name, width, height, GTK_UNIT_POINTS);
310   g_free (name);
311
312  out:
313
314   if (size->info == NULL ||
315       size->info->ppd_name == -1 ||
316       strcmp (paper_names + size->info->ppd_name, ppd_name) != 0)
317     size->ppd_name = g_strdup (ppd_name);
318   
319   g_free (freeme);
320   
321   return size;
322 }
323
324
325 GtkPaperSize *
326 gtk_paper_size_new_custom (const char *name, 
327                            const char *display_name,
328                            double      width, 
329                            double      height, 
330                            GtkUnit     unit)
331 {
332   GtkPaperSize *size;
333   g_return_val_if_fail (name != NULL, NULL);
334   g_return_val_if_fail (unit != GTK_UNIT_PIXEL, NULL);
335
336   size = g_new0 (GtkPaperSize, 1);
337   
338   size->name = g_strdup (name);
339   size->display_name = g_strdup (display_name);
340   size->is_custom = TRUE;
341   
342   size->width = to_mm (width, unit);
343   size->height = to_mm (height, unit);
344   
345   return size;
346 }
347
348 GtkPaperSize *
349 gtk_paper_size_copy (GtkPaperSize *other)
350 {
351   GtkPaperSize *size;
352
353   size = g_new0 (GtkPaperSize, 1);
354
355   size->info = other->info;
356   if (other->name)
357     size->name = g_strdup (other->name);
358   if (other->display_name)
359     size->display_name = g_strdup (other->display_name);
360   if (other->ppd_name)
361     size->ppd_name = g_strdup (other->ppd_name);
362   
363   size->width = other->width;
364   size->height = other->height;
365   size->is_custom = other->is_custom;
366
367   return size;
368 }
369
370 void
371 gtk_paper_size_free (GtkPaperSize *size)
372 {
373   g_free (size->name);
374   g_free (size->display_name);
375   g_free (size->ppd_name);
376   g_free (size);
377 }
378
379 gboolean
380 gtk_paper_size_is_equal (GtkPaperSize *size1,
381                          GtkPaperSize *size2)
382 {
383   if (size1->info != NULL && size2->info != NULL)
384     return size1->info == size2->info;
385   
386   return strcmp (gtk_paper_size_get_name (size1),
387                  gtk_paper_size_get_name (size2)) == 0;
388 }
389  
390 G_CONST_RETURN char *
391 gtk_paper_size_get_name (GtkPaperSize *size)
392 {
393   if (size->name)
394     return size->name;
395   g_assert (size->info != NULL);
396   return paper_names + size->info->name;
397 }
398
399 G_CONST_RETURN char *
400 gtk_paper_size_get_display_name (GtkPaperSize *size)
401 {
402   if (size->display_name)
403     return size->display_name;
404   g_assert (size->info != NULL);
405   return gettext (paper_names + size->info->display_name);
406 }
407
408 G_CONST_RETURN char *
409 gtk_paper_size_get_ppd_name (GtkPaperSize *size)
410 {
411   if (size->ppd_name)
412     return size->ppd_name;
413   if (size->info)
414     return paper_names + size->info->ppd_name;
415   return NULL;
416 }
417
418 double
419 gtk_paper_size_get_width (GtkPaperSize *size, GtkUnit unit)
420 {
421   return from_mm (size->width, unit);
422 }
423 double
424 gtk_paper_size_get_height (GtkPaperSize *size, GtkUnit unit)
425 {
426   return from_mm (size->height, unit);
427 }
428
429 gboolean
430 gtk_paper_size_is_custom (GtkPaperSize *size)
431 {
432   return size->is_custom;
433 }
434
435 /* Only for custom sizes: */
436 void
437 gtk_paper_size_set_size (GtkPaperSize *size, double width, double height, GtkUnit unit)
438 {
439   g_return_if_fail (size != NULL);
440   g_return_if_fail (size->is_custom);
441
442   size->width = to_mm (width, unit);
443   size->height = to_mm (height, unit);
444 }
445
446 #define NL_PAPER_GET(x)         \
447   ((union { char *string; unsigned int word; })nl_langinfo(x)).word
448
449 G_CONST_RETURN char *
450 gtk_paper_size_get_default (void)
451 {
452   char *locale, *freeme = NULL;
453   const char *paper_size;
454
455 #if defined(HAVE__NL_PAPER_HEIGHT) && defined(HAVE__NL_PAPER_WIDTH)
456   {
457     int width = NL_PAPER_GET (_NL_PAPER_WIDTH);
458     int height = NL_PAPER_GET (_NL_PAPER_HEIGHT);
459     
460     if (width == 210 && height == 297)
461       return GTK_PAPER_NAME_A4;
462     
463     if (width == 216 && height == 279)
464       return GTK_PAPER_NAME_LETTER;
465   }
466 #endif
467
468 #ifdef G_OS_WIN32
469   freeme = locale = g_win32_getlocale ();
470 #elif defined(LC_PAPER)
471   locale = setlocale(LC_PAPER, NULL);
472 #else
473   locale = setlocale(LC_MESSAGES, NULL);
474 #endif
475
476   if (!locale)
477     return GTK_PAPER_NAME_A4;
478
479   if (g_str_has_prefix (locale, "en_CA") ||
480       g_str_has_prefix (locale, "en_US") ||
481       g_str_has_prefix (locale, "es_PR") ||
482       g_str_has_prefix (locale, "es_US"))
483     paper_size = GTK_PAPER_NAME_LETTER;
484   else
485     paper_size = GTK_PAPER_NAME_A4;
486
487   g_free (freeme);
488   return paper_size;
489 }
490
491 /* These get the default margins used for the paper size. Its
492  * larger than most printers margins, so that it will be within
493  * the imageble area on any printer.
494  *
495  * I've taken the actual values used from the OSX page setup dialog.
496  * I'm not sure exactly where they got these values for, but might
497  * correspond to this (from ghostscript docs):
498  * 
499  * All DeskJets have 0.5 inches (1.27cm) of unprintable bottom margin,
500  * due to the mechanical arrangement used to grab the paper. Side margins
501  * are approximately 0.25 inches (0.64cm) for U.S. letter paper, and 0.15
502  * inches (0.38cm) for A4.
503  */
504
505 double
506 gtk_paper_size_get_default_top_margin (GtkPaperSize *size, GtkUnit unit)
507 {
508   double margin;
509
510   margin = to_mm (0.25, GTK_UNIT_INCH);
511   return from_mm (margin, unit);
512 }
513
514 double
515 gtk_paper_size_get_default_bottom_margin (GtkPaperSize *size, GtkUnit unit)
516 {
517   double margin;
518   const char *name;
519
520   margin = to_mm (0.25, GTK_UNIT_INCH);
521
522   name = gtk_paper_size_get_name (size);
523   if (strcmp (name, "na_letter") == 0 ||
524       strcmp (name, "na_legal") == 0 ||
525       strcmp (name, "iso_a4") == 0)
526     margin = to_mm (0.56, GTK_UNIT_INCH);
527   
528   return from_mm (margin, unit);
529 }
530
531 double
532 gtk_paper_size_get_default_left_margin (GtkPaperSize *size, GtkUnit unit)
533 {
534   double margin;
535
536   margin = to_mm (0.25, GTK_UNIT_INCH);
537   return from_mm (margin, unit);
538 }
539
540 double
541 gtk_paper_size_get_default_right_margin (GtkPaperSize *size, GtkUnit unit)
542 {
543   double margin;
544
545   margin = to_mm (0.25, GTK_UNIT_INCH);
546   return from_mm (margin, unit);
547 }
548
549
550 #define __GTK_PAPER_SIZE_C__
551 #include "gtkaliasdef.c"