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