1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2007, 2009, 2010, 2011, 2012, 2014, 2016 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"
22 #include <gl/c-strtod.h>
31 #include "libpspp/str.h"
33 #include "gl/c-strcase.h"
34 #include "libpspp/message.h"
37 #define _(msgid) gettext (msgid)
39 static double parse_unit (const char *);
40 static bool parse_paper_size (const char *, int *h
, int *v
);
41 static bool get_standard_paper_size (struct substring name
, int *h
, int *v
);
42 static bool read_paper_conf (const char *file_name
, int *h
, int *v
);
43 static bool get_default_paper_size (int *h
, int *v
);
45 /* Determines the size of a dimensional measurement and returns
46 the size in units of 1/72000". Units are assumed to be
47 millimeters unless otherwise specified. Returns -1 on
50 measure_dimension (const char *dimen
)
56 raw
= c_strtod (dimen
, &tail
);
61 factor
= parse_unit (tail
);
68 msg (ME
, _("`%s' is not a valid length."), dimen
);
72 /* Stores the dimensions, in 1/72000" units, of paper identified
73 by SIZE into *H and *V. SIZE can be the name of a kind of
74 paper ("a4", "letter", ...) or a pair of dimensions
75 ("210x297", "8.5x11in", ...). Returns true on success, false
76 on failure. On failure, *H and *V are set for A4 paper. */
78 measure_paper (const char *size
, int *h
, int *v
)
84 ss_trim (&s
, ss_cstr (CC_SPACES
));
88 /* Treat empty string as default paper size. */
89 ok
= get_default_paper_size (h
, v
);
91 else if (isdigit (ss_first (s
)))
93 /* Treat string that starts with digit as explicit size. */
94 ok
= parse_paper_size (size
, h
, v
);
96 msg (ME
, _("syntax error in paper size `%s'"), size
);
100 /* Check against standard paper sizes. */
101 ok
= get_standard_paper_size (s
, h
, v
);
104 /* Default to A4 on error. */
107 *h
= 210 * (72000 / 25.4);
108 *v
= 297 * (72000 / 25.4);
113 /* Parses UNIT as a dimensional unit. Returns the multiplicative
114 factor needed to change a quantity measured in that unit into
115 1/72000" units. If UNIT is empty, it is treated as
116 millimeters. If the unit is unrecognized, returns 0. */
118 parse_unit (const char *unit
)
126 static const struct unit units
[] =
129 {"pc", 72000 / 72 * 12.0},
131 {"cm", 72000 / 2.54},
132 {"mm", 72000 / 25.4},
136 const struct unit
*p
;
138 unit
+= strspn (unit
, CC_SPACES
);
139 for (p
= units
; p
< units
+ sizeof units
/ sizeof *units
; p
++)
140 if (!c_strcasecmp (unit
, p
->name
))
145 /* Stores the dimensions in 1/72000" units of paper identified by
146 SIZE, which is of form `HORZ x VERT [UNIT]' where HORZ and
147 VERT are numbers and UNIT is an optional unit of measurement,
148 into *H and *V. Return true on success. */
150 parse_paper_size (const char *size
, int *h
, int *v
)
152 double raw_h
, raw_v
, factor
;
156 raw_h
= c_strtod (size
, &tail
);
161 tail
+= strspn (tail
, CC_SPACES
"x,");
164 raw_v
= c_strtod (tail
, &tail
);
169 factor
= parse_unit (tail
);
173 *h
= raw_h
* factor
+ .5;
174 *v
= raw_v
* factor
+ .5;
179 get_standard_paper_size (struct substring name
, int *h
, int *v
)
181 static const char *sizes
[][2] =
183 {"a0", "841 x 1189 mm"},
184 {"a1", "594 x 841 mm"},
185 {"a2", "420 x 594 mm"},
186 {"a3", "297 x 420 mm"},
187 {"a4", "210 x 297 mm"},
188 {"a5", "148 x 210 mm"},
189 {"b5", "176 x 250 mm"},
190 {"a6", "105 x 148 mm"},
191 {"a7", "74 x 105 mm"},
192 {"a8", "52 x 74 mm"},
193 {"a9", "37 x 52 mm"},
194 {"a10", "26 x 37 mm"},
195 {"b0", "1000 x 1414 mm"},
196 {"b1", "707 x 1000 mm"},
197 {"b2", "500 x 707 mm"},
198 {"b3", "353 x 500 mm"},
199 {"b4", "250 x 353 mm"},
200 {"letter", "612 x 792 pt"},
201 {"legal", "612 x 1008 pt"},
202 {"executive", "522 x 756 pt"},
203 {"note", "612 x 792 pt"},
204 {"11x17", "792 x 1224 pt"},
205 {"tabloid", "792 x 1224 pt"},
206 {"statement", "396 x 612 pt"},
207 {"halfletter", "396 x 612 pt"},
208 {"halfexecutive", "378 x 522 pt"},
209 {"folio", "612 x 936 pt"},
210 {"quarto", "610 x 780 pt"},
211 {"ledger", "1224 x 792 pt"},
212 {"archA", "648 x 864 pt"},
213 {"archB", "864 x 1296 pt"},
214 {"archC", "1296 x 1728 pt"},
215 {"archD", "1728 x 2592 pt"},
216 {"archE", "2592 x 3456 pt"},
217 {"flsa", "612 x 936 pt"},
218 {"flse", "612 x 936 pt"},
219 {"csheet", "1224 x 1584 pt"},
220 {"dsheet", "1584 x 2448 pt"},
221 {"esheet", "2448 x 3168 pt"},
226 for (i
= 0; i
< sizeof sizes
/ sizeof *sizes
; i
++)
227 if (ss_equals_case (ss_cstr (sizes
[i
][0]), name
))
229 bool ok
= parse_paper_size (sizes
[i
][1], h
, v
);
233 msg (ME
, _("unknown paper type `%.*s'"),
234 (int) ss_length (name
), ss_data (name
));
238 /* Reads file FILE_NAME to find a paper size. Stores the
239 dimensions, in 1/72000" units, into *H and *V. Returns true
240 on success, false on failure. */
242 read_paper_conf (const char *file_name
, int *h
, int *v
)
244 struct string line
= DS_EMPTY_INITIALIZER
;
248 file
= fopen (file_name
, "r");
251 msg_error (errno
, _("error opening input file `%s'"), file_name
);
257 struct substring name
;
259 if (!ds_read_config_line (&line
, &line_number
, file
))
262 msg_error (errno
, _("error reading file `%s'"), file_name
);
266 name
= ds_ss (&line
);
267 ss_trim (&name
, ss_cstr (CC_SPACES
));
268 if (!ss_is_empty (name
))
270 bool ok
= get_standard_paper_size (name
, h
, v
);
279 msg (ME
, _("file `%s' does not state a paper size"), file_name
);
283 /* The user didn't specify a paper size, so let's choose a
284 default based on his environment. Stores the
285 dimensions, in 1/72000" units, into *H and *V. Returns true
286 on success, false on failure. */
288 get_default_paper_size (int *h
, int *v
)
290 /* libpaper in Debian (and other distributions?) allows the
291 paper size to be specified in $PAPERSIZE or in a file
292 specified in $PAPERCONF. */
293 if (getenv ("PAPERSIZE") != NULL
)
294 return get_standard_paper_size (ss_cstr (getenv ("PAPERSIZE")), h
, v
);
295 if (getenv ("PAPERCONF") != NULL
)
296 return read_paper_conf (getenv ("PAPERCONF"), h
, v
);
299 /* LC_PAPER is a non-standard glibc extension.
300 The (int)(intptr_t) cast is for 64 Bit Systems where intptr_t
301 translates to 64 Bit long int but the upper 32 Bits have wrong
302 values. The result from nl_langinfo is integer (32 Bit) */
303 *h
= (int)(intptr_t) nl_langinfo(_NL_PAPER_WIDTH
) * (72000 / 25.4);
304 *v
= (int)(intptr_t) nl_langinfo(_NL_PAPER_HEIGHT
) * (72000 / 25.4);
305 if (*h
> 0 && *v
> 0)
309 /* libpaper defaults to /etc/papersize. */
310 if (0 == access ("/etc/papersize", R_OK
))
311 return read_paper_conf ("/etc/papersize", h
, v
);
313 /* Can't find a default. */