1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "output/measure.h"
21 #include <gl/c-strtod.h>
29 #include "data/file-name.h"
30 #include "libpspp/str.h"
35 #define _(msgid) gettext (msgid)
37 static double parse_unit (const char *);
38 static bool parse_paper_size (const char *, int *h, int *v);
39 static bool get_standard_paper_size (struct substring name, int *h, int *v);
40 static bool read_paper_conf (const char *file_name, int *h, int *v);
41 static bool get_default_paper_size (int *h, int *v);
43 /* Determines the size of a dimensional measurement and returns
44 the size in units of 1/72000". Units are assumed to be
45 millimeters unless otherwise specified. Returns -1 on
48 measure_dimension (const char *dimen)
54 raw = c_strtod (dimen, &tail);
59 factor = parse_unit (tail);
66 error (0, 0, _("`%s' is not a valid length."), dimen);
70 /* Stores the dimensions, in 1/72000" units, of paper identified
71 by SIZE into *H and *V. SIZE can be the name of a kind of
72 paper ("a4", "letter", ...) or a pair of dimensions
73 ("210x297", "8.5x11in", ...). Returns true on success, false
74 on failure. On failure, *H and *V are set for A4 paper. */
76 measure_paper (const char *size, int *h, int *v)
82 ss_trim (&s, ss_cstr (CC_SPACES));
86 /* Treat empty string as default paper size. */
87 ok = get_default_paper_size (h, v);
89 else if (isdigit (ss_first (s)))
91 /* Treat string that starts with digit as explicit size. */
92 ok = parse_paper_size (size, h, v);
94 error (0, 0, _("syntax error in paper size `%s'"), size);
98 /* Check against standard paper sizes. */
99 ok = get_standard_paper_size (s, h, v);
102 /* Default to A4 on error. */
105 *h = 210 * (72000 / 25.4);
106 *v = 297 * (72000 / 25.4);
111 /* Parses UNIT as a dimensional unit. Returns the multiplicative
112 factor needed to change a quantity measured in that unit into
113 1/72000" units. If UNIT is empty, it is treated as
114 millimeters. If the unit is unrecognized, returns 0. */
116 parse_unit (const char *unit)
124 static const struct unit units[] =
127 {"pc", 72000 / 72 * 12.0},
129 {"cm", 72000 / 2.54},
130 {"mm", 72000 / 25.4},
134 const struct unit *p;
136 unit += strspn (unit, CC_SPACES);
137 for (p = units; p < units + sizeof units / sizeof *units; p++)
138 if (!strcasecmp (unit, p->name))
143 /* Stores the dimensions in 1/72000" units of paper identified by
144 SIZE, which is of form `HORZ x VERT [UNIT]' where HORZ and
145 VERT are numbers and UNIT is an optional unit of measurement,
146 into *H and *V. Return true on success. */
148 parse_paper_size (const char *size, int *h, int *v)
150 double raw_h, raw_v, factor;
154 raw_h = c_strtod (size, &tail);
159 tail += strspn (tail, CC_SPACES "x,");
162 raw_v = c_strtod (tail, &tail);
167 factor = parse_unit (tail);
171 *h = raw_h * factor + .5;
172 *v = raw_v * factor + .5;
177 get_standard_paper_size (struct substring name, int *h, int *v)
179 static const char *sizes[][2] =
181 {"a0", "841 x 1189 mm"},
182 {"a1", "594 x 841 mm"},
183 {"a2", "420 x 594 mm"},
184 {"a3", "297 x 420 mm"},
185 {"a4", "210 x 297 mm"},
186 {"a5", "148 x 210 mm"},
187 {"b5", "176 x 250 mm"},
188 {"a6", "105 x 148 mm"},
189 {"a7", "74 x 105 mm"},
190 {"a8", "52 x 74 mm"},
191 {"a9", "37 x 52 mm"},
192 {"a10", "26 x 37 mm"},
193 {"b0", "1000 x 1414 mm"},
194 {"b1", "707 x 1000 mm"},
195 {"b2", "500 x 707 mm"},
196 {"b3", "353 x 500 mm"},
197 {"b4", "250 x 353 mm"},
198 {"letter", "612 x 792 pt"},
199 {"legal", "612 x 1008 pt"},
200 {"executive", "522 x 756 pt"},
201 {"note", "612 x 792 pt"},
202 {"11x17", "792 x 1224 pt"},
203 {"tabloid", "792 x 1224 pt"},
204 {"statement", "396 x 612 pt"},
205 {"halfletter", "396 x 612 pt"},
206 {"halfexecutive", "378 x 522 pt"},
207 {"folio", "612 x 936 pt"},
208 {"quarto", "610 x 780 pt"},
209 {"ledger", "1224 x 792 pt"},
210 {"archA", "648 x 864 pt"},
211 {"archB", "864 x 1296 pt"},
212 {"archC", "1296 x 1728 pt"},
213 {"archD", "1728 x 2592 pt"},
214 {"archE", "2592 x 3456 pt"},
215 {"flsa", "612 x 936 pt"},
216 {"flse", "612 x 936 pt"},
217 {"csheet", "1224 x 1584 pt"},
218 {"dsheet", "1584 x 2448 pt"},
219 {"esheet", "2448 x 3168 pt"},
224 for (i = 0; i < sizeof sizes / sizeof *sizes; i++)
225 if (ss_equals_case (ss_cstr (sizes[i][0]), name))
227 bool ok = parse_paper_size (sizes[i][1], h, v);
231 error (0, 0, _("unknown paper type `%.*s'"),
232 (int) ss_length (name), ss_data (name));
236 /* Reads file FILE_NAME to find a paper size. Stores the
237 dimensions, in 1/72000" units, into *H and *V. Returns true
238 on success, false on failure. */
240 read_paper_conf (const char *file_name, int *h, int *v)
242 struct string line = DS_EMPTY_INITIALIZER;
246 file = fopen (file_name, "r");
249 error (0, errno, _("error opening input file `%s'"), file_name);
255 struct substring name;
257 if (!ds_read_config_line (&line, &line_number, file))
260 error (0, errno, _("error reading file `%s'"), file_name);
264 name = ds_ss (&line);
265 ss_trim (&name, ss_cstr (CC_SPACES));
266 if (!ss_is_empty (name))
268 bool ok = get_standard_paper_size (name, h, v);
277 error (0, 0, _("paper size file `%s' does not state a paper size"),
282 /* The user didn't specify a paper size, so let's choose a
283 default based on his environment. Stores the
284 dimensions, in 1/72000" units, into *H and *V. Returns true
285 on success, false on failure. */
287 get_default_paper_size (int *h, int *v)
289 /* libpaper in Debian (and other distributions?) allows the
290 paper size to be specified in $PAPERSIZE or in a file
291 specified in $PAPERCONF. */
292 if (getenv ("PAPERSIZE") != NULL)
293 return get_standard_paper_size (ss_cstr (getenv ("PAPERSIZE")), h, v);
294 if (getenv ("PAPERCONF") != NULL)
295 return read_paper_conf (getenv ("PAPERCONF"), h, v);
298 /* LC_PAPER is a non-standard glibc extension. */
299 *h = (int) nl_langinfo(_NL_PAPER_WIDTH) * (72000 / 25.4);
300 *v = (int) nl_langinfo(_NL_PAPER_HEIGHT) * (72000 / 25.4);
301 if (*h > 0 && *v > 0)
305 /* libpaper defaults to /etc/papersize. */
306 if (fn_exists ("/etc/papersize"))
307 return read_paper_conf ("/etc/papersize", h, v);
309 /* Can't find a default. */