Line data Source code
1 : /* Copyright 1989-2025 Free Software Foundation, Inc.
2 : Written by James Clark (jjc@jclark.com)
3 :
4 : This file is part of groff, the GNU roff typesetting system.
5 :
6 : groff is free software; you can redistribute it and/or modify it under
7 : the terms of the GNU General Public License as published by the Free
8 : Software Foundation, either version 3 of the License, or
9 : (at your option) any later version.
10 :
11 : groff is distributed in the hope that it will be useful, but WITHOUT ANY
12 : WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 : for more details.
15 :
16 : You should have received a copy of the GNU General Public License
17 : along with this program. If not, see <http://www.gnu.org/licenses/>. */
18 :
19 : #ifdef HAVE_CONFIG_H
20 : #include <config.h>
21 : #endif
22 :
23 : #include <assert.h>
24 : #include <ctype.h>
25 : #include <errno.h>
26 : #include <limits.h> // INT_MAX, INT_MIN, LONG_MAX
27 : #include <math.h>
28 : #include <stdcountof.h>
29 : #include <stdlib.h>
30 : #include <string.h> // strerror()
31 : #include <wchar.h>
32 :
33 : #include "lib.h"
34 :
35 : #include "errarg.h"
36 : #include "error.h"
37 : #include "cset.h"
38 : #include "font.h"
39 : #include "unicode.h"
40 : #include "paper.h"
41 :
42 : const char *const WS = " \t\n\r";
43 :
44 : struct font_char_metric {
45 : char type;
46 : int code;
47 : int width;
48 : int height;
49 : int depth;
50 : int pre_math_space;
51 : int italic_correction;
52 : int subscript_correction;
53 : char *special_device_coding;
54 : struct font_char_metric *next;
55 : int end_code;
56 : };
57 :
58 : struct font_kern_list {
59 : glyph *glyph1;
60 : glyph *glyph2;
61 : int amount;
62 : font_kern_list *next;
63 :
64 : font_kern_list(glyph *, glyph *, int,
65 : font_kern_list * = 0 /* nullptr */);
66 : };
67 :
68 : struct font_widths_cache {
69 : font_widths_cache *next;
70 : int point_size;
71 : int *width;
72 :
73 : font_widths_cache(int, int, font_widths_cache * = 0 /* nullptr */);
74 : ~font_widths_cache();
75 : };
76 :
77 : /* text_file */
78 :
79 : struct text_file {
80 : FILE *fp;
81 : char *path;
82 : int lineno;
83 : int linebufsize;
84 : bool recognize_comments;
85 : bool silent;
86 : char *buf;
87 : text_file(FILE *fp, char *p);
88 : ~text_file();
89 : bool next_line();
90 : void error(const char *format,
91 : const errarg &arg1 = empty_errarg,
92 : const errarg &arg2 = empty_errarg,
93 : const errarg &arg3 = empty_errarg);
94 : void fatal(const char *format,
95 : const errarg &arg1 = empty_errarg,
96 : const errarg &arg2 = empty_errarg,
97 : const errarg &arg3 = empty_errarg);
98 : };
99 :
100 24353 : text_file::text_file(FILE *p, char *s) : fp(p), path(s), lineno(0),
101 : linebufsize(128), recognize_comments(true), silent(false),
102 24353 : buf(0 /* nullptr */)
103 : {
104 24353 : }
105 :
106 48706 : text_file::~text_file()
107 : {
108 24353 : delete[] buf;
109 24353 : free(path);
110 24353 : if (fp)
111 24353 : fclose(fp);
112 24353 : }
113 :
114 8191050 : bool text_file::next_line()
115 : {
116 8191050 : if (0 /* nullptr */ == fp)
117 0 : return false;
118 8191050 : if (0 /* nullptr */ == buf)
119 24353 : buf = new char[linebufsize];
120 : for (;;) {
121 8396559 : lineno++;
122 8396559 : int length = 0;
123 : for (;;) {
124 147389771 : int c = getc(fp);
125 147389771 : if (EOF == c)
126 18940 : break;
127 147370831 : if (is_invalid_input_char(c))
128 0 : error("invalid input character code %1", int(c));
129 : else {
130 147370831 : if (length + 1 >= linebufsize) {
131 9830 : int newbufsize = linebufsize * 2;
132 9830 : if (newbufsize < 0) // integer multiplication wrapped
133 0 : fatal("line length exceeds %1 bytes; aborting",
134 0 : linebufsize);
135 9830 : char *old_buf = buf;
136 9830 : buf = new char[newbufsize];
137 9830 : memcpy(buf, old_buf, linebufsize);
138 9830 : delete[] old_buf;
139 9830 : linebufsize = newbufsize;
140 : }
141 147370831 : buf[length++] = c;
142 147370831 : if ('\n' == c)
143 8377619 : break;
144 : }
145 138993212 : }
146 8396559 : if (0 == length)
147 18940 : break;
148 8377619 : buf[length] = '\0';
149 8377619 : char *ptr = buf;
150 8413705 : while (csspace(*ptr))
151 36086 : ptr++;
152 8377619 : if (*ptr != 0 /* nullptr */ && (!recognize_comments || *ptr != '#'))
153 8172110 : return true;
154 205509 : }
155 18940 : return false;
156 : }
157 :
158 30 : void text_file::error(const char *format,
159 : const errarg &arg1,
160 : const errarg &arg2,
161 : const errarg &arg3)
162 : {
163 30 : if (!silent)
164 0 : error_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
165 30 : }
166 :
167 0 : void text_file::fatal(const char *format,
168 : const errarg &arg1,
169 : const errarg &arg2,
170 : const errarg &arg3)
171 : {
172 0 : if (!silent)
173 0 : fatal_with_file_and_line(path, lineno, format, arg1, arg2, arg3);
174 0 : }
175 :
176 27559191 : static int glyph_to_ucs_codepoint(glyph *g)
177 : {
178 27559191 : const char *nm = glyph_to_name(g);
179 27559191 : if (nm != 0 /* nullptr */) {
180 27430180 : if ((valid_unicode_code_sequence(nm) != 0 /* nullptr */)
181 27430180 : && (strchr(nm, '_') == 0)) {
182 : char *ignore;
183 1550 : return static_cast<int>(strtol(nm + 1, &ignore, 16));
184 : }
185 : }
186 27557641 : return -1;
187 : }
188 :
189 7390576 : int glyph_to_unicode(glyph *g)
190 : {
191 7390576 : const char *nm = glyph_to_name(g);
192 7390576 : if (nm != 0 /* nullptr */) {
193 : // ASCII character?
194 7363422 : if (nm[0] == 'c' && nm[1] == 'h' && nm[2] == 'a' && nm[3] == 'r'
195 7251377 : && (nm[4] >= '0' && nm[4] <= '9')) {
196 7251377 : int n = (nm[4] - '0');
197 7251377 : if (nm[5] == '\0')
198 7361237 : return n;
199 7251355 : if (n > 0 && (nm[5] >= '0' && nm[5] <= '9')) {
200 7251355 : n = 10*n + (nm[5] - '0');
201 7251355 : if (nm[6] == '\0')
202 1741365 : return n;
203 5509990 : if (nm[6] >= '0' && nm[6] <= '9') {
204 5509990 : n = 10*n + (nm[6] - '0');
205 5509990 : if (nm[7] == '\0' && n < 128)
206 5509990 : return n;
207 : }
208 : }
209 : }
210 : // Unicode character?
211 112045 : if (valid_unicode_code_sequence(nm) != 0 /* nullptr */) {
212 : char *ignore;
213 18970 : return (int)strtol(nm + 1, &ignore, 16);
214 : }
215 : // If 'nm' is a single letter 'x', the glyph name is '\x'.
216 93075 : char buf[] = { '\\', '\0', '\0' };
217 93075 : if (nm[1] == '\0') {
218 2175 : buf[1] = nm[0];
219 2175 : nm = buf;
220 : }
221 : // groff glyphs that map to Unicode?
222 93075 : const char *unicode = glyph_name_to_unicode(nm);
223 93075 : if ((unicode != 0 /* nullptr */)
224 91026 : && (strchr(unicode, '_') == 0 /* nullptr */)) {
225 : char *ignore;
226 90890 : return (int)strtol(unicode, &ignore, 16);
227 : }
228 : }
229 29339 : return -1;
230 : }
231 :
232 : /* font functions */
233 :
234 21902 : font::font(const char *fn) : ligatures(0),
235 : kern_hash_table(0 /* nullptr */),
236 : space_width(0), special(false), internalname(0 /* nullptr */),
237 : slant(0.0), zoom(0), ch_index(0 /* nullptr */), nindices(0),
238 : ch(0 /* nullptr */), wch(0 /* nullptr */), ch_used(0), ch_size(0),
239 21902 : widths_cache(0 /* nullptr */)
240 : {
241 21902 : filename = new char[strlen(fn) + 1];
242 21902 : strcpy(filename, fn);
243 21902 : }
244 :
245 5876 : font::~font()
246 : {
247 269673 : for (int i = 0; i < ch_used; i++)
248 266753 : if (ch[i].special_device_coding)
249 31933 : delete[] ch[i].special_device_coding;
250 2920 : delete[] ch;
251 2920 : delete[] ch_index;
252 2920 : if (kern_hash_table) {
253 55944 : for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++) {
254 55833 : font_kern_list *kerns = kern_hash_table[i];
255 118113 : while (kerns) {
256 62280 : font_kern_list *tem = kerns;
257 62280 : kerns = kerns->next;
258 62280 : delete tem;
259 : }
260 : }
261 111 : delete[] kern_hash_table;
262 : }
263 2920 : delete[] filename;
264 2920 : delete[] internalname;
265 3134 : while (widths_cache) {
266 214 : font_widths_cache *tem = widths_cache;
267 214 : widths_cache = widths_cache->next;
268 214 : delete tem;
269 : }
270 : struct font_char_metric *wcp, *nwcp;
271 2958 : for (wcp = wch; wcp != 0 /* nullptr */; wcp = nwcp) {
272 38 : nwcp = wcp->next;
273 38 : if (wcp->special_device_coding)
274 38 : delete[] wcp->special_device_coding;
275 38 : delete wcp;
276 : }
277 2956 : }
278 :
279 6163818 : static int scale_round(int n, int x, int y)
280 : {
281 6163818 : assert(x >= 0 && y > 0);
282 6163818 : int y2 = y/2;
283 6163818 : if (0 == x)
284 0 : return 0;
285 6163818 : if (n >= 0) {
286 5710933 : if (n <= (INT_MAX - y2) / x)
287 5710933 : return (n * x + y2) / y;
288 0 : return int(n * double(x) / double(y) + .5);
289 : }
290 : else {
291 452885 : if (-(unsigned)n <= (-(unsigned)INT_MIN - y2) / x)
292 452885 : return (n * x - y2) / y;
293 0 : return int(n * double(x) / double(y) - .5);
294 : }
295 : }
296 :
297 0 : static int scale_round(int n, int x, int y, int z)
298 : {
299 0 : assert(x >= 0 && y > 0 && z > 0);
300 0 : if (0 == x)
301 0 : return 0;
302 0 : if (n >= 0)
303 0 : return int((n * double(x) / double(y)) * (double(z) / 1000.0) + .5);
304 : else
305 0 : return int((n * double(x) / double(y)) * (double(z) / 1000.0) - .5);
306 : }
307 :
308 6932330 : inline int font::scale(int w, int sz)
309 : {
310 6932330 : if (zoom)
311 0 : return scale_round(w, sz, unitwidth, zoom);
312 : else
313 6932330 : return sz == unitwidth ? w : scale_round(w, sz, unitwidth);
314 : }
315 :
316 : // Returns whether scaling by arguments was successful. Used for paper
317 : // size conversions.
318 0 : bool font::unit_scale(double *value, char unit)
319 : {
320 : // Paper sizes are handled in inches.
321 0 : double divisor = 0;
322 0 : switch (unit) {
323 0 : case 'i':
324 0 : divisor = 1;
325 0 : break;
326 0 : case 'p':
327 0 : divisor = 72;
328 0 : break;
329 0 : case 'P':
330 0 : divisor = 6;
331 0 : break;
332 0 : case 'c':
333 0 : divisor = 2.54;
334 0 : break;
335 0 : default:
336 0 : assert(0 == "unit not in [cipP]");
337 : break;
338 : }
339 0 : if (divisor) {
340 0 : *value /= divisor;
341 0 : return true;
342 : }
343 0 : return false;
344 : }
345 :
346 20778 : int font::get_skew(glyph *g, int point_size, int sl)
347 : {
348 20778 : int h = get_height(g, point_size);
349 20778 : return int(h * tan((slant + sl) * PI / 180.0) + .5);
350 : }
351 :
352 14512011 : bool font::contains(glyph *g)
353 : {
354 14512011 : int idx = glyph_to_index(g);
355 14512011 : assert(idx >= 0);
356 : // Explicitly enumerated glyph?
357 14512011 : if (idx < nindices && ch_index[idx] >= 0)
358 11828112 : return true;
359 2683899 : int uc = glyph_to_ucs_codepoint(g);
360 2683899 : if (uc > 0) {
361 515 : font_char_metric *wcp = get_font_wchar_metric(uc);
362 515 : if (wcp != 0 /* nullptr */)
363 12 : return true;
364 : }
365 2683887 : if (is_unicode) {
366 : // Unicode font
367 : // ASCII or Unicode character, or groff glyph name that maps to Unicode?
368 2648631 : if (glyph_to_unicode(g) >= 0)
369 2637486 : return true;
370 : // Numbered character?
371 11145 : if (glyph_to_number(g) >= 0)
372 9282 : return true;
373 : }
374 37119 : return false;
375 : }
376 :
377 84600 : bool font::is_special()
378 : {
379 84600 : return special;
380 : }
381 :
382 2288 : font_widths_cache::font_widths_cache(int ps, int ch_size,
383 2288 : font_widths_cache *p)
384 2288 : : next(p), point_size(ps)
385 : {
386 2288 : width = new int[ch_size];
387 525414 : for (int i = 0; i < ch_size; i++)
388 523126 : width[i] = -1;
389 2288 : }
390 :
391 428 : font_widths_cache::~font_widths_cache()
392 : {
393 214 : delete[] width;
394 214 : }
395 :
396 1550 : struct font_char_metric *font::get_font_wchar_metric(int uc)
397 : {
398 : struct font_char_metric *wcp;
399 2108 : for (wcp = wch; wcp != 0 /* nullptr */; wcp = wcp->next) {
400 620 : if (wcp->code <= uc && uc <= wcp->end_code) {
401 62 : return wcp;
402 : }
403 : }
404 1488 : return 0 /* nullptr */;
405 : }
406 :
407 16885971 : int font::get_width(glyph *g, int point_size)
408 : {
409 16885971 : int idx = glyph_to_index(g);
410 16885971 : assert(idx >= 0);
411 : int real_size;
412 16885971 : if (0 == zoom) // 0 means "don't zoom"
413 16885971 : real_size = point_size;
414 : else
415 : {
416 0 : if (point_size <= (INT_MAX - 500) / zoom)
417 0 : real_size = (point_size * zoom + 500) / 1000;
418 : else
419 0 : real_size = int(point_size * double(zoom) / 1000.0 + .5);
420 : }
421 16885971 : int uc = glyph_to_ucs_codepoint(g);
422 16885971 : font_char_metric *wcp = 0 /* nullptr */;
423 16885971 : if (uc > 0)
424 374 : wcp = get_font_wchar_metric(uc);
425 16885971 : if (wcp != 0 && !(idx < nindices && ch_index[idx] >= 0)) {
426 22 : return scale(wcp->width, point_size);
427 : }
428 16885949 : if (idx < nindices && ch_index[idx] >= 0) {
429 : // Explicitly enumerated glyph
430 13299342 : int i = ch_index[idx];
431 13299342 : if (real_size == unitwidth || font::use_unscaled_charwidths)
432 438919 : return ch[i].width;
433 :
434 12860423 : if (!widths_cache)
435 688 : widths_cache = new font_widths_cache(real_size, ch_size);
436 12859735 : else if (widths_cache->point_size != real_size) {
437 : font_widths_cache **p;
438 204934 : for (p = &widths_cache; *p; p = &(*p)->next)
439 203334 : if ((*p)->point_size == real_size)
440 4624 : break;
441 6224 : if (*p) {
442 4624 : font_widths_cache *tem = *p;
443 4624 : *p = (*p)->next;
444 4624 : tem->next = widths_cache;
445 4624 : widths_cache = tem;
446 : }
447 : else
448 1600 : widths_cache = new font_widths_cache(real_size, ch_size,
449 1600 : widths_cache);
450 : }
451 12860423 : int &w = widths_cache->width[i];
452 12860423 : if (w < 0)
453 65205 : w = scale(ch[i].width, point_size);
454 12860423 : return w;
455 : }
456 3586607 : if (is_unicode) {
457 : // Unicode font
458 3586607 : int width = 24; // XXX: Add a request to override this.
459 3586607 : int w = wcwidth(get_code(g));
460 3586607 : if (w > 1 && !font::use_unscaled_charwidths)
461 169 : width *= w;
462 3586607 : if (real_size == unitwidth || font::use_unscaled_charwidths)
463 3586607 : return width;
464 : else
465 0 : return scale(width, point_size);
466 : }
467 0 : assert(0 == "glyph is not indexed and device lacks Unicode support");
468 : abort(); // -Wreturn-type
469 : }
470 :
471 2722069 : int font::get_height(glyph *g, int point_size)
472 : {
473 2722069 : int idx = glyph_to_index(g);
474 2722069 : assert(idx >= 0);
475 2722069 : if (idx < nindices && ch_index[idx] >= 0) {
476 : // Explicitly enumerated glyph
477 1636828 : return scale(ch[ch_index[idx]].height, point_size);
478 : }
479 1085241 : int uc = glyph_to_ucs_codepoint(g);
480 1085241 : font_char_metric *wcp = 0 /* nullptr */;
481 1085241 : if (uc > 0)
482 102 : wcp = get_font_wchar_metric(uc);
483 1085241 : if (wcp != 0 /* nullptr */) {
484 8 : return scale(wcp->height, point_size);
485 : }
486 1085233 : if (is_unicode) {
487 : // Unicode font
488 1085233 : return 0;
489 : }
490 0 : assert(0 == "glyph is not indexed and device lacks Unicode support");
491 : abort(); // -Wreturn-type
492 : }
493 :
494 2701291 : int font::get_depth(glyph *g, int point_size)
495 : {
496 2701291 : int idx = glyph_to_index(g);
497 2701291 : assert(idx >= 0);
498 2701291 : if (idx < nindices && ch_index[idx] >= 0) {
499 : // Explicitly enumerated glyph
500 1621173 : return scale(ch[ch_index[idx]].depth, point_size);
501 : }
502 1080118 : int uc = glyph_to_ucs_codepoint(g);
503 1080118 : font_char_metric *wcp = 0 /* nullptr */;
504 1080118 : if (uc > 0)
505 91 : wcp = get_font_wchar_metric(uc);
506 1080118 : if (wcp != 0 /* nullptr */) {
507 8 : return scale(wcp->depth, point_size);
508 : }
509 1080110 : if (is_unicode) {
510 : // Unicode font
511 1080110 : return 0;
512 : }
513 0 : assert(0 == "glyph is not indexed and device lacks Unicode support");
514 : abort(); // -Wreturn-type
515 : }
516 :
517 2539271 : int font::get_italic_correction(glyph *g, int point_size)
518 : {
519 2539271 : int idx = glyph_to_index(g);
520 2539271 : assert(idx >= 0);
521 2539271 : if (idx < nindices && ch_index[idx] >= 0) {
522 : // Explicitly enumerated glyph
523 1493217 : return scale(ch[ch_index[idx]].italic_correction, point_size);
524 : }
525 1046054 : int uc = glyph_to_ucs_codepoint(g);
526 1046054 : font_char_metric *wcp = 0 /* nullptr */;
527 1046054 : if (uc > 0)
528 78 : wcp = get_font_wchar_metric(uc);
529 1046054 : if (wcp != 0 /* nullptr */) {
530 8 : return scale(wcp->italic_correction, point_size);
531 : }
532 1046046 : if (is_unicode) {
533 : // Unicode font
534 1046046 : return 0;
535 : }
536 0 : assert(0 == "glyph is not indexed and device lacks Unicode support");
537 : abort(); // -Wreturn-type
538 : }
539 :
540 32205 : int font::get_left_italic_correction(glyph *g, int point_size)
541 : {
542 32205 : int idx = glyph_to_index(g);
543 32205 : assert(idx >= 0 /* nullptr */);
544 32205 : if (idx < nindices && ch_index[idx] >= 0) {
545 : // Explicitly enumerated glyph
546 22570 : return scale(ch[ch_index[idx]].pre_math_space, point_size);
547 : }
548 9635 : int uc = glyph_to_ucs_codepoint(g);
549 9635 : font_char_metric *wcp = 0 /* nullptr */;
550 9635 : if (uc > 0 )
551 0 : wcp = get_font_wchar_metric(uc);
552 9635 : if (wcp != 0 /* nullptr */) {
553 0 : return scale(wcp->pre_math_space, point_size);
554 : }
555 9635 : if (is_unicode) {
556 : // Unicode font
557 9635 : return 0;
558 : }
559 0 : assert(0 == "glyph is not indexed and device lacks Unicode support");
560 : abort(); // -Wreturn-type
561 : }
562 :
563 21035 : int font::get_subscript_correction(glyph *g, int point_size)
564 : {
565 21035 : int idx = glyph_to_index(g);
566 21035 : assert(idx >= 0);
567 21035 : if (idx < nindices && ch_index[idx] >= 0) {
568 : // Explicitly enumerated glyph
569 15912 : return scale(ch[ch_index[idx]].subscript_correction, point_size);
570 : }
571 5123 : int uc = glyph_to_ucs_codepoint(g);
572 5123 : font_char_metric *wcp = 0 /* nullptr */;
573 5123 : if (uc > 0)
574 11 : wcp = get_font_wchar_metric(uc);
575 5123 : if (wcp != 0 /* nullptr */) {
576 0 : return scale(wcp->subscript_correction, point_size);
577 : }
578 5123 : if (is_unicode) {
579 : // Unicode font
580 5123 : return 0;
581 : }
582 0 : assert(0 == "glyph is not indexed and device lacks Unicode support");
583 : abort(); // -Wreturn-type
584 : }
585 :
586 0 : void font::set_zoom(int factor)
587 : {
588 0 : assert(factor >= 0);
589 0 : if (factor == 1000)
590 0 : zoom = 0;
591 : else
592 0 : zoom = factor;
593 0 : }
594 :
595 136167 : int font::get_zoom()
596 : {
597 136167 : return zoom;
598 : }
599 :
600 1591979 : int font::get_space_width(int point_size)
601 : {
602 1591979 : return scale(space_width, point_size);
603 : }
604 :
605 4278019 : font_kern_list::font_kern_list(glyph *g1, glyph *g2, int n, font_kern_list *p)
606 4278019 : : glyph1(g1), glyph2(g2), amount(n), next(p)
607 : {
608 4278019 : }
609 :
610 13134674 : inline int font::hash_kern(glyph *g1, glyph *g2)
611 : {
612 13134674 : int n = ((glyph_to_index(g1) << 10) + glyph_to_index(g2))
613 13134674 : % KERN_HASH_TABLE_SIZE;
614 13134674 : return n < 0 ? -n : n;
615 : }
616 :
617 4278019 : void font::add_kern(glyph *g1, glyph *g2, int amount)
618 : {
619 4278019 : if (!kern_hash_table) {
620 5874 : kern_hash_table = new font_kern_list *[int(KERN_HASH_TABLE_SIZE)];
621 2960496 : for (int i = 0; i < KERN_HASH_TABLE_SIZE; i++)
622 2954622 : kern_hash_table[i] = 0;
623 : }
624 4278019 : font_kern_list **p = kern_hash_table + hash_kern(g1, g2);
625 4278019 : *p = new font_kern_list(g1, g2, amount, *p);
626 4278019 : }
627 :
628 10448388 : int font::get_kern(glyph *g1, glyph *g2, int point_size)
629 : {
630 10448388 : if (kern_hash_table) {
631 15059991 : for (font_kern_list *p = kern_hash_table[hash_kern(g1, g2)]; p;
632 6203336 : p = p->next)
633 6688736 : if (g1 == p->glyph1 && g2 == p->glyph2)
634 485400 : return scale(p->amount, point_size);
635 : }
636 9962988 : return 0;
637 : }
638 :
639 153805 : bool font::has_ligature(int mask)
640 : {
641 153805 : return (bool) (mask & ligatures);
642 : }
643 :
644 183183 : int font::get_character_type(glyph *g)
645 : {
646 183183 : int idx = glyph_to_index(g);
647 183183 : assert(idx >= 0);
648 183183 : if (idx < nindices && ch_index[idx] >= 0) {
649 : // Explicitly enumerated glyph
650 139475 : return ch[ch_index[idx]].type;
651 : }
652 43708 : int uc = glyph_to_ucs_codepoint(g);
653 43708 : font_char_metric *wcp = 0 /* nullptr */;
654 43708 : if (uc > 0)
655 13 : wcp = get_font_wchar_metric(uc);
656 43708 : if (wcp != 0 /* nullptr */) {
657 0 : return wcp->type;
658 : }
659 43708 : if (is_unicode) {
660 : // Unicode font
661 43708 : return 0;
662 : }
663 0 : assert(0 == "glyph is not indexed and device lacks Unicode support");
664 : abort(); // -Wreturn-type
665 : }
666 :
667 5156757 : int font::get_code(glyph *g)
668 : {
669 5156757 : int idx = glyph_to_index(g);
670 5156757 : assert(idx >= 0);
671 5156757 : if (idx < nindices && ch_index[idx] >= 0) {
672 : // Explicitly enumerated glyph
673 437315 : return ch[ch_index[idx]].code;
674 : }
675 4719442 : int uc = glyph_to_ucs_codepoint(g);
676 4719442 : font_char_metric *wcp = 0 /* nullptr */;
677 4719442 : if (uc > 0)
678 366 : wcp = get_font_wchar_metric(uc);
679 4719442 : if (wcp != 0 /* nullptr */) {
680 4 : return uc;
681 : }
682 4719438 : if (is_unicode) {
683 : // Unicode font
684 : // ASCII or Unicode character, or groff glyph name that maps to Unicode?
685 4719438 : int uni = glyph_to_unicode(g);
686 4719438 : if (uni >= 0)
687 4701574 : return uni;
688 : // Numbered character?
689 17864 : int n = glyph_to_number(g);
690 17864 : if (n >= 0)
691 17864 : return n;
692 : }
693 : // The caller must check 'contains(g)' before calling get_code(g).
694 0 : assert(0 == "glyph is not indexed and device lacks Unicode support");
695 : abort(); // -Wreturn-type
696 : }
697 :
698 31521 : const char *font::get_filename()
699 : {
700 31521 : return filename;
701 : }
702 :
703 397174 : const char *font::get_internal_name()
704 : {
705 397174 : return internalname;
706 : }
707 :
708 68 : const char *font::get_special_device_encoding(glyph *g)
709 : {
710 68 : int idx = glyph_to_index(g);
711 68 : assert(idx >= 0);
712 68 : if (idx < nindices && ch_index[idx] >= 0) {
713 : // Explicitly enumerated glyph
714 68 : return ch[ch_index[idx]].special_device_coding;
715 : }
716 0 : int uc = glyph_to_ucs_codepoint(g);
717 0 : font_char_metric *wcp = 0 /* nullptr */;
718 0 : if (uc > 0)
719 0 : wcp = get_font_wchar_metric(uc);
720 0 : if (wcp != 0 /* nullptr */)
721 0 : return wcp->special_device_coding;
722 0 : if (is_unicode) {
723 : // Unicode font
724 0 : return 0;
725 : }
726 0 : assert(0 == "glyph is not indexed and device lacks Unicode support");
727 : abort(); // -Wreturn-type
728 : }
729 :
730 0 : const char *font::get_image_generator()
731 : {
732 0 : return image_generator;
733 : }
734 :
735 33831 : void font::alloc_ch_index(int idx)
736 : {
737 33831 : if (0 == nindices) {
738 14205 : nindices = 128;
739 14205 : if (idx >= nindices)
740 11960 : nindices = idx + 10;
741 14205 : ch_index = new int[nindices];
742 8148147 : for (int i = 0; i < nindices; i++)
743 8133942 : ch_index[i] = -1;
744 : }
745 : else {
746 19626 : int old_nindices = nindices;
747 19626 : nindices *= 2;
748 19626 : if (idx >= nindices)
749 1014 : nindices = idx + 10;
750 19626 : int *old_ch_index = ch_index;
751 19626 : ch_index = new int[nindices];
752 19626 : memcpy(ch_index, old_ch_index, sizeof(int) * old_nindices);
753 12373148 : for (int i = old_nindices; i < nindices; i++)
754 12353522 : ch_index[i] = -1;
755 19626 : delete[] old_ch_index;
756 : }
757 33831 : }
758 :
759 67655 : void font::extend_ch()
760 : {
761 67655 : if (0 /* nullptr */ == ch)
762 14205 : ch = new font_char_metric[ch_size = 16];
763 : else {
764 53450 : int old_ch_size = ch_size;
765 53450 : ch_size *= 2;
766 53450 : font_char_metric *old_ch = ch;
767 53450 : ch = new font_char_metric[ch_size];
768 53450 : memcpy(ch, old_ch, old_ch_size * sizeof(font_char_metric));
769 53450 : delete[] old_ch;
770 : }
771 67655 : }
772 :
773 15049 : void font::compact()
774 : {
775 : int i;
776 8311003 : for (i = nindices - 1; i >= 0; i--)
777 8310159 : if (ch_index[i] >= 0)
778 14205 : break;
779 15049 : i++;
780 15049 : if (i < nindices) {
781 14205 : int *old_ch_index = ch_index;
782 14205 : ch_index = new int[i];
783 14205 : memcpy(ch_index, old_ch_index, i*sizeof(int));
784 14205 : delete[] old_ch_index;
785 14205 : nindices = i;
786 : }
787 15049 : if (ch_used < ch_size) {
788 14205 : font_char_metric *old_ch = ch;
789 14205 : ch = new font_char_metric[ch_used];
790 14205 : memcpy(ch, old_ch, ch_used*sizeof(font_char_metric));
791 14205 : delete[] old_ch;
792 14205 : ch_size = ch_used;
793 : }
794 15049 : }
795 :
796 3284662 : void font::add_entry(glyph *g, const font_char_metric &metric)
797 : {
798 3284662 : int idx = glyph_to_index(g);
799 3284662 : assert(idx >= 0);
800 3284662 : if (idx >= nindices)
801 28287 : alloc_ch_index(idx);
802 3284662 : assert(idx < nindices);
803 3284662 : if (ch_used + 1 >= ch_size)
804 67655 : extend_ch();
805 3284662 : assert(ch_used + 1 < ch_size);
806 3284662 : ch_index[idx] = ch_used;
807 3284662 : ch[ch_used++] = metric;
808 3284662 : }
809 :
810 3489775 : void font::copy_entry(glyph *new_glyph, glyph *old_glyph)
811 : {
812 3489775 : int new_index = glyph_to_index(new_glyph);
813 3489775 : int old_index = glyph_to_index(old_glyph);
814 3489775 : assert(new_index >= 0 && old_index >= 0 && old_index < nindices);
815 3489775 : if (new_index >= nindices)
816 5544 : alloc_ch_index(new_index);
817 3489775 : ch_index[new_index] = ch_index[old_index];
818 3489775 : }
819 :
820 19018 : font *font::load_font(const char *fn, bool load_header_only)
821 : {
822 19018 : font *f = new font(fn);
823 19018 : if (!f->load(load_header_only)) {
824 36 : delete f;
825 36 : return 0 /* nullptr */;
826 : }
827 18982 : return f;
828 : }
829 :
830 18307 : static char *trim_arg(char *p)
831 : {
832 18307 : if (0 /* nullptr */ == p)
833 680 : return 0 /* nullptr */;
834 17726 : while (csspace(*p))
835 99 : p++;
836 17627 : char *q = strchr(p, '\0');
837 17635 : while (q > p && csspace(q[-1]))
838 8 : q--;
839 17627 : *q = '\0';
840 17627 : return p;
841 : }
842 :
843 551 : bool font::scan_papersize(const char *p, const char **size,
844 : double *length, double *width)
845 : {
846 : double l, w;
847 : char lu[2], wu[2];
848 551 : const char *pp = p;
849 551 : bool attempt_file_open = true;
850 : char line[255];
851 551 : again:
852 551 : if (csdigit(*pp)) {
853 0 : if (sscanf(pp, "%lf%1[ipPc],%lf%1[ipPc]", &l, lu, &w, wu) == 4
854 0 : && l > 0 && w > 0
855 0 : && unit_scale(&l, lu[0]) && unit_scale(&w, wu[0])) {
856 0 : if (length)
857 0 : *length = l;
858 0 : if (width)
859 0 : *width = w;
860 0 : if (size)
861 0 : *size = "custom";
862 0 : return true;
863 : }
864 : }
865 : else {
866 : int i;
867 18183 : for (i = 0; i < NUM_PAPERSIZES; i++)
868 18183 : if (strcasecmp(papersizes[i].name, pp) == 0) {
869 551 : if (length)
870 551 : *length = papersizes[i].length;
871 551 : if (width)
872 551 : *width = papersizes[i].width;
873 551 : if (size)
874 534 : *size = papersizes[i].name;
875 551 : return true;
876 : }
877 0 : if (attempt_file_open) {
878 0 : FILE *fp = fopen(p, "r");
879 0 : if (fp != 0 /* nullptr */) {
880 0 : if (fgets(line, 254, fp)) {
881 : // Don't recurse on file names.
882 0 : attempt_file_open = false;
883 0 : char *linep = strchr(line, '\0');
884 : // skip final newline, if any
885 0 : if (*(--linep) == '\n')
886 0 : *linep = '\0';
887 0 : pp = line;
888 : }
889 0 : fclose(fp);
890 0 : goto again;
891 : }
892 : }
893 : }
894 0 : return false;
895 : }
896 :
897 20482 : bool font::load(bool load_header_only)
898 : {
899 : char *path;
900 20482 : FILE *fp = open_file(filename, &path);
901 20482 : if (0 /* nullptr */ == fp)
902 6 : return false;
903 40952 : text_file t(fp, path);
904 20476 : t.silent = load_header_only;
905 20476 : char *p = 0 /* nullptr */;
906 20476 : bool saw_name_directive = false;
907 113464 : while (t.next_line()) {
908 113434 : p = strtok(t.buf, WS);
909 113434 : if (strcmp(p, "name") == 0) {
910 20446 : p = strtok(0 /* nullptr */, WS);
911 20446 : if (0 /* nullptr */ == p) {
912 0 : t.error("'name' directive requires an argument");
913 0 : return false;
914 : }
915 20446 : if (strcmp(p, filename) != 0) {
916 0 : t.error("font description file name '%1' does not match 'name'"
917 0 : " argument '%2'", filename, p);
918 0 : return false;
919 : }
920 20446 : saw_name_directive = true;
921 : }
922 92988 : else if (strcmp(p, "spacewidth") == 0) {
923 20446 : p = strtok(0 /* nullptr */, WS);
924 : int n;
925 20446 : if (0 /* nullptr */ == p) {
926 0 : t.error("missing argument to 'spacewidth' directive");
927 0 : return false;
928 : }
929 20446 : if (sscanf(p, "%d", &n) != 1) {
930 0 : t.error("invalid argument '%1' to 'spacewidth' directive", p);
931 0 : return false;
932 : }
933 20446 : if (n <= 0) {
934 0 : t.error("'spacewidth' argument '%1' out of range", n);
935 0 : return false;
936 : }
937 20446 : space_width = n;
938 : }
939 72542 : else if (strcmp(p, "slant") == 0) {
940 6385 : p = strtok(0 /* nullptr */, WS);
941 : double n;
942 6385 : if (0 /* nullptr */ == p) {
943 0 : t.error("missing argument to 'slant' directive");
944 0 : return false;
945 : }
946 6385 : if (sscanf(p, "%lf", &n) != 1) {
947 0 : t.error("invalid argument '%1' to 'slant' directive", p);
948 0 : return false;
949 : }
950 6385 : if (n >= 90.0 || n <= -90.0) {
951 0 : t.error("'slant' directive argument '%1' out of range", n);
952 0 : return false;
953 : }
954 6385 : slant = n;
955 : }
956 66157 : else if (strcmp(p, "ligatures") == 0) {
957 : for (;;) {
958 34164 : p = strtok(0 /* nullptr */, WS);
959 34164 : if (0 /* nullptr */ == p || strcmp(p, "0") == 0)
960 : break;
961 22925 : if (strcmp(p, "ff") == 0)
962 149 : ligatures |= LIG_ff;
963 22776 : else if (strcmp(p, "fi") == 0)
964 11239 : ligatures |= LIG_fi;
965 11537 : else if (strcmp(p, "fl") == 0)
966 11239 : ligatures |= LIG_fl;
967 298 : else if (strcmp(p, "ffi") == 0)
968 149 : ligatures |= LIG_ffi;
969 149 : else if (strcmp(p, "ffl") == 0)
970 149 : ligatures |= LIG_ffl;
971 : else {
972 0 : t.error("unrecognized ligature '%1'", p);
973 0 : return false;
974 : }
975 : }
976 : }
977 54918 : else if (strcmp(p, "internalname") == 0) {
978 17179 : p = strtok(0 /* nullptr */, WS);
979 17179 : if (0 /* nullptr */ == p) {
980 0 : t.error("missing argument to 'internalname' directive");
981 0 : return false;
982 : }
983 17179 : internalname = new char[strlen(p) + 1];
984 17179 : strcpy(internalname, p);
985 : }
986 37739 : else if (strcmp(p, "special") == 0) {
987 967 : special = true;
988 : }
989 36772 : else if (strcmp(p, "kernpairs") != 0 && strcmp(p, "charset") != 0 &&
990 17468 : strcmp(p, "charset-range") != 0) {
991 16326 : char *directive = p;
992 16326 : p = strtok(0 /* nullptr */, "\n");
993 16326 : handle_unknown_font_command(directive, trim_arg(p), t.path,
994 16326 : t.lineno);
995 : }
996 : else
997 : break;
998 : }
999 20476 : bool saw_charset_directive = false;
1000 20476 : char *directive = p;
1001 20476 : t.recognize_comments = false;
1002 41665 : while (directive) {
1003 26616 : if (strcmp(directive, "kernpairs") == 0) {
1004 11251 : if (load_header_only)
1005 5377 : return true;
1006 : for (;;) {
1007 4283893 : if (!t.next_line()) {
1008 13 : directive = 0 /* nullptr */;
1009 5874 : break;
1010 : }
1011 4283880 : char *c1 = strtok(t.buf, WS);
1012 4283880 : if (0 /* nullptr */ == c1)
1013 0 : continue;
1014 4283880 : char *c2 = strtok(0 /* nullptr */, WS);
1015 4283880 : if (0 /* nullptr */ == c2) {
1016 5861 : directive = c1;
1017 5861 : break;
1018 : }
1019 4278019 : p = strtok(0 /* nullptr */, WS);
1020 4278019 : if (0 /* nullptr */ == p) {
1021 0 : t.error("missing kern amount for kerning pair '%1 %2'", c1,
1022 0 : c2);
1023 0 : return false;
1024 : }
1025 : int n;
1026 4278019 : if (sscanf(p, "%d", &n) != 1) {
1027 0 : t.error("invalid kern amount '%1' for kerning pair '%2 %3'",
1028 0 : p, c1, c2);
1029 0 : return false;
1030 : }
1031 4278019 : glyph *g1 = name_to_glyph(c1);
1032 4278019 : glyph *g2 = name_to_glyph(c2);
1033 4278019 : add_kern(g1, g2, n);
1034 4278019 : }
1035 : }
1036 : // TODO: Rename this directive to "ranged-charset".
1037 15365 : else if (strcmp(directive, "charset-range") == 0) {
1038 1142 : if (load_header_only)
1039 32 : return true;
1040 1110 : saw_charset_directive = true;
1041 1110 : bool had_range = false;
1042 : for (;;) {
1043 19164 : if (!t.next_line()) {
1044 828 : directive = 0 /* nullptr */;
1045 828 : break;
1046 : }
1047 18336 : char *nm = strtok(t.buf, WS);
1048 18336 : assert(nm != 0 /* nullptr */);
1049 18336 : p = strtok(0 /* nullptr */, WS);
1050 18336 : if (0 /* nullptr */ == p) {
1051 282 : directive = nm;
1052 282 : break;
1053 : }
1054 18054 : unsigned int start_code = 0;
1055 18054 : unsigned int end_code = 0;
1056 18054 : int nrange = sscanf(nm, "u%X..u%X", &start_code, &end_code);
1057 : // TODO: Check for backwards range: end_code < start_code.
1058 18054 : if (2 == nrange) {
1059 18054 : had_range = true;
1060 18054 : font_char_metric *wcp = new font_char_metric;
1061 18054 : wcp->code = start_code;
1062 18054 : wcp->end_code = end_code;
1063 18054 : wcp->height = 0;
1064 18054 : wcp->depth = 0;
1065 18054 : wcp->pre_math_space = 0;
1066 18054 : wcp->italic_correction = 0;
1067 18054 : wcp->subscript_correction = 0;
1068 18054 : int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
1069 : &wcp->width, &wcp->height, &wcp->depth,
1070 : &wcp->italic_correction,
1071 : &wcp->pre_math_space,
1072 : &wcp->subscript_correction);
1073 18054 : if (nparms < 1) {
1074 0 : t.error("missing or invalid width for character range '%1'",
1075 0 : nm);
1076 0 : return false;
1077 : }
1078 18054 : p = strtok(0 /* nullptr */, WS);
1079 18054 : if (0 /* nullptr */ == p) {
1080 0 : t.error("missing character type for '%1'", nm);
1081 0 : return false;
1082 : }
1083 : int type;
1084 18054 : if (sscanf(p, "%d", &type) != 1) {
1085 0 : t.error("invalid character type for '%1'", nm);
1086 0 : return false;
1087 : }
1088 18054 : if ((type < 0) || (type > 255)) {
1089 0 : t.error("character type '%1' out of range for '%2'", type,
1090 0 : nm);
1091 0 : return false;
1092 : }
1093 18054 : wcp->type = type;
1094 :
1095 18054 : p = strtok(0 /* nullptr */, WS);
1096 18054 : if ((0 /* nullptr */ == p) || (strcmp(p, "--") == 0)) {
1097 0 : wcp->special_device_coding = 0 /* nullptr */;
1098 : }
1099 : else {
1100 18054 : wcp->special_device_coding = new char[strlen(p) + 1];
1101 18054 : strcpy(wcp->special_device_coding, p);
1102 : }
1103 18054 : wcp->next = wch;
1104 18054 : wch = wcp;
1105 18054 : p = 0 /* nullptr */;
1106 : }
1107 18054 : }
1108 : // TODO: Parallelize wording of "charset"'s diagnostic.
1109 1110 : if (!had_range) {
1110 0 : t.error("no glyphs described after 'charset-range' directive");
1111 0 : return false;
1112 : }
1113 : }
1114 14223 : else if (strcmp(directive, "charset") == 0) {
1115 14209 : if (load_header_only)
1116 4 : return true;
1117 14205 : saw_charset_directive = true;
1118 14205 : glyph *last_glyph = 0 /* nullptr */;
1119 : for (;;) {
1120 3735768 : if (!t.next_line()) {
1121 14192 : directive = 0 /* nullptr */;
1122 14192 : break;
1123 : }
1124 3721576 : char *nm = strtok(t.buf, WS);
1125 3721576 : assert(nm != 0 /* nullptr */);
1126 3721576 : p = strtok(0 /* nullptr */, WS);
1127 3721576 : if (0 /* nullptr */ == p) {
1128 13 : directive = nm;
1129 13 : break;
1130 : }
1131 3721563 : if (p[0] == '"') {
1132 436901 : if (0 /* nullptr */ == last_glyph) {
1133 0 : t.error("the first entry ('%1') in 'charset' subsection"
1134 0 : " cannot be an alias", nm);
1135 0 : return false;
1136 : }
1137 436901 : if (strcmp(nm, "---") == 0) {
1138 0 : t.error("an unnamed character ('---') cannot be an alias");
1139 0 : return false;
1140 : }
1141 436901 : glyph *g = name_to_glyph(nm);
1142 436901 : copy_entry(g, last_glyph);
1143 : }
1144 : else {
1145 : font_char_metric metric;
1146 3284662 : metric.height = 0;
1147 3284662 : metric.depth = 0;
1148 3284662 : metric.pre_math_space = 0;
1149 3284662 : metric.italic_correction = 0;
1150 3284662 : metric.subscript_correction = 0;
1151 3284662 : int nparms = sscanf(p, "%d,%d,%d,%d,%d,%d",
1152 : &metric.width, &metric.height,
1153 : &metric.depth,
1154 : &metric.italic_correction,
1155 : &metric.pre_math_space,
1156 : &metric.subscript_correction);
1157 3284662 : if (nparms < 1) {
1158 0 : t.error("missing or invalid width for glyph '%1'", nm);
1159 0 : return false;
1160 : }
1161 3284662 : p = strtok(0 /* nullptr */, WS);
1162 3284662 : if (0 /* nullptr */ == p) {
1163 0 : t.error("missing character type for '%1'", nm);
1164 0 : return false;
1165 : }
1166 : int type;
1167 3284662 : if (sscanf(p, "%d", &type) != 1) {
1168 0 : t.error("invalid character type for '%1'", nm);
1169 0 : return false;
1170 : }
1171 3284662 : if (type < 0 || type > 255) {
1172 0 : t.error("character type '%1' out of range for '%2'", type,
1173 0 : nm);
1174 0 : return false;
1175 : }
1176 3284662 : metric.type = type;
1177 3284662 : p = strtok(0 /* nullptr */, WS);
1178 3284662 : if (0 /* nullptr */ == p) {
1179 0 : t.error("missing index for '%1'", nm);
1180 0 : return false;
1181 : }
1182 : char *ptr;
1183 : long index;
1184 3284662 : errno = 0;
1185 3284662 : index = strtol(p, &ptr, 0);
1186 3284662 : if (ptr == p) {
1187 0 : t.error("invalid index '%1' for character '%2'", p, nm);
1188 0 : return false;
1189 : }
1190 : if (INT_MAX != LONG_MAX) {
1191 3284662 : if ((index > INT_MAX) || (index < INT_MIN)) {
1192 : // This is a fib since INT_MIN's absolute value is one
1193 : // greater than INT_MAX's (on two's complement machines),
1194 : // but we can pass 3 arguments at most to the error()
1195 : // function. Also, 31 bits ought to be enough for anyone.
1196 0 : t.error("index %1 for character '%2' is out of range;"
1197 0 : " must be within +/-%3", p, nm, INT_MAX);
1198 0 : return false;
1199 : }
1200 : }
1201 3284662 : if (errno != 0) {
1202 0 : t.error("cannot convert index '%1' to integer for character"
1203 0 : " '%2': %3", p, nm, strerror(errno));
1204 0 : return false;
1205 : }
1206 3284662 : metric.code = static_cast<int>(index);
1207 3284662 : p = strtok(0 /* nullptr */, WS);
1208 3284662 : if ((0 /* nullptr */ == p) || (strcmp(p, "--") == 0)) {
1209 1632360 : metric.special_device_coding = 0;
1210 : }
1211 : else {
1212 1652302 : char *nam = new char[strlen(p) + 1];
1213 1652302 : strcpy(nam, p);
1214 1652302 : metric.special_device_coding = nam;
1215 : }
1216 3284662 : if (strcmp(nm, "---") == 0) {
1217 231788 : last_glyph = number_to_glyph(metric.code);
1218 231788 : add_entry(last_glyph, metric);
1219 : }
1220 : else {
1221 3052874 : last_glyph = name_to_glyph(nm);
1222 3052874 : add_entry(last_glyph, metric);
1223 3052874 : copy_entry(number_to_glyph(metric.code), last_glyph);
1224 : }
1225 : }
1226 3721563 : }
1227 14205 : if (0 /* nullptr */ == last_glyph) {
1228 0 : t.error("no glyphs defined in font description");
1229 0 : return false;
1230 : }
1231 : }
1232 : else {
1233 14 : t.error("unrecognized font description directive '%1' (missing"
1234 14 : " 'kernpairs' or 'charset'?)", directive);
1235 14 : return false;
1236 : }
1237 : }
1238 15049 : compact();
1239 15049 : t.lineno = 0;
1240 15049 : if (!saw_name_directive) {
1241 16 : t.error("font description 'name' directive missing");
1242 16 : return false;
1243 : }
1244 15033 : if (!is_unicode && !saw_charset_directive) {
1245 0 : t.error("font description 'charset' subsection missing");
1246 0 : return false;
1247 : }
1248 15033 : if (space_width == 0) {
1249 0 : t.error("font description 'spacewidth' directive missing");
1250 : // _Don't_ return false; compute a typical one for Western glyphs.
1251 0 : if (zoom)
1252 0 : space_width = scale_round(unitwidth, res, 72 * 3 * sizescale,
1253 : zoom);
1254 : else
1255 0 : space_width = scale_round(unitwidth, res, 72 * 3 * sizescale);
1256 : }
1257 15033 : return true;
1258 : }
1259 :
1260 : static struct numeric_directive {
1261 : const char *name;
1262 : int *ptr;
1263 : } numeric_directive_table[] = {
1264 : { "res", &font::res },
1265 : { "hor", &font::hor },
1266 : { "vert", &font::vert },
1267 : { "unitwidth", &font::unitwidth },
1268 : { "paperwidth", &font::paperwidth },
1269 : { "paperlength", &font::paperlength },
1270 : { "spare1", &font::biggestfont },
1271 : { "biggestfont", &font::biggestfont },
1272 : { "spare2", &font::spare2 },
1273 : { "sizescale", &font::sizescale },
1274 : };
1275 :
1276 : // Return file specification of DESC file for selected output device if
1277 : // it can be located and is valid, and a null pointer otherwise.
1278 3877 : const char *font::load_desc()
1279 : {
1280 3877 : int nfonts = 0;
1281 : char *path;
1282 3877 : FILE *fp = open_file("DESC", &path);
1283 3877 : if (0 /* nullptr */ == fp)
1284 0 : return 0 /* nullptr */;
1285 7754 : text_file t(fp, path);
1286 38761 : while (t.next_line()) {
1287 34884 : char *p = strtok(t.buf, WS);
1288 34884 : assert(p != 0 /* nullptr */);
1289 34884 : bool numeric_directive_found = false;
1290 : size_t idx;
1291 267789 : for (idx = 0;
1292 267789 : !numeric_directive_found && (idx < countof(numeric_directive_table));
1293 : idx++)
1294 232905 : if (strcmp(numeric_directive_table[idx].name, p) == 0)
1295 15967 : numeric_directive_found = true;
1296 34884 : if (numeric_directive_found) {
1297 15967 : char *q = strtok(0 /* nullptr */, WS);
1298 15967 : if (0 /* nullptr */ == q) {
1299 0 : t.error("missing value for directive '%1'", p);
1300 0 : return 0 /* nullptr */;
1301 : }
1302 : int val;
1303 15967 : if (sscanf(q, "%d", &val) != 1) {
1304 0 : t.error("'%1' directive given invalid number '%2'", p, q);
1305 0 : return 0 /* nullptr */;
1306 : }
1307 15967 : if ((strcmp(p, "res") == 0
1308 12090 : || strcmp(p, "hor") == 0
1309 8238 : || strcmp(p, "vert") == 0
1310 4386 : || strcmp(p, "unitwidth") == 0
1311 509 : || strcmp(p, "paperwidth") == 0
1312 509 : || strcmp(p, "paperlength") == 0
1313 509 : || strcmp(p, "sizescale") == 0)
1314 15967 : && val < 1) {
1315 0 : t.error("expected argument to '%1' directive to be a"
1316 0 : " positive number, got '%2'", p, val);
1317 0 : return 0 /* nullptr */;
1318 : }
1319 15967 : *(numeric_directive_table[idx - 1].ptr) = val;
1320 : }
1321 18917 : else if (strcmp("family", p) == 0) {
1322 534 : p = strtok(0 /* nullptr */, WS);
1323 534 : if (0 /* nullptr */ == p) {
1324 0 : t.error("'family' directive requires an argument");
1325 0 : return 0 /* nullptr */;
1326 : }
1327 534 : char *tem = new char[strlen(p)+1];
1328 534 : strcpy(tem, p);
1329 534 : family = tem;
1330 : }
1331 18383 : else if (strcmp("fonts", p) == 0) {
1332 3877 : p = strtok(0 /* nullptr */, WS);
1333 3877 : if (0 /* nullptr */ == p) {
1334 0 : t.error("'fonts' directive requires arguments");
1335 0 : return 0 /* nullptr */;
1336 : }
1337 3877 : if (sscanf(p, "%d", &nfonts) != 1 || nfonts <= 0) {
1338 0 : t.error("expected first argument to 'fonts' directive to be a"
1339 0 : " non-negative number, got '%1'", p);
1340 0 : return 0 /* nullptr */;
1341 : }
1342 3877 : font_name_table = (const char **)new char *[nfonts+1];
1343 25764 : for (int i = 0; i < nfonts; i++) {
1344 21887 : p = strtok(0 /* nullptr */, WS);
1345 21887 : while (0 /* nullptr */ == p) {
1346 0 : if (!t.next_line()) {
1347 0 : t.error("unexpected end of file while reading font list");
1348 0 : return 0 /* nullptr */;
1349 : }
1350 0 : p = strtok(t.buf, WS);
1351 : }
1352 21887 : char *temp = new char[strlen(p)+1];
1353 21887 : strcpy(temp, p);
1354 21887 : font_name_table[i] = temp;
1355 : }
1356 3877 : p = strtok(0 /* nullptr */, WS);
1357 3877 : if (p != 0 /* nullptr */) {
1358 0 : t.error("font count does not match declared number of fonts"
1359 0 : " ('%1')", nfonts);
1360 0 : return 0 /* nullptr */;
1361 : }
1362 3877 : font_name_table[nfonts] = 0 /* nullptr */;
1363 : }
1364 14506 : else if (strcmp("papersize", p) == 0) {
1365 534 : if (0 /* nullptr */ == res) {
1366 0 : t.error("'res' directive must precede 'papersize' in device"
1367 : " description file");
1368 0 : return 0 /* nullptr */;
1369 : }
1370 534 : p = strtok(0 /* nullptr */, WS);
1371 534 : if (0 /* nullptr */ == p) {
1372 0 : t.error("'papersize' directive requires an argument");
1373 0 : return 0 /* nullptr */;
1374 : }
1375 534 : bool found_paper = false;
1376 534 : char *savedp = strdup(p);
1377 534 : if (0 /* nullptr */ == savedp)
1378 0 : t.fatal("memory allocation failure while processing 'papersize'"
1379 : " directive");
1380 534 : while (p) {
1381 : double unscaled_paperwidth, unscaled_paperlength;
1382 534 : if (scan_papersize(p, &papersize, &unscaled_paperlength,
1383 : &unscaled_paperwidth)) {
1384 534 : paperwidth = int(unscaled_paperwidth * res + 0.5);
1385 534 : paperlength = int(unscaled_paperlength * res + 0.5);
1386 534 : found_paper = true;
1387 534 : break;
1388 : }
1389 0 : p = strtok(0 /* nullptr */, WS);
1390 : }
1391 534 : assert(savedp != 0 /* nullptr */);
1392 534 : if (!found_paper) {
1393 0 : t.error("unable to determine a paper format from '%1'", savedp);
1394 0 : free(savedp);
1395 0 : return 0 /* nullptr */;
1396 : }
1397 534 : free(savedp);
1398 : }
1399 13972 : else if (strcmp("unscaled_charwidths", p) == 0)
1400 127 : use_unscaled_charwidths = true;
1401 13845 : else if (strcmp("pass_filenames", p) == 0)
1402 127 : pass_filenames = true;
1403 13718 : else if (strcmp("sizes", p) == 0) {
1404 3877 : int n = 16;
1405 3877 : sizes = new int[n];
1406 3877 : int i = 0;
1407 : for (;;) {
1408 8154 : p = strtok(0 /* nullptr */, WS);
1409 8154 : while (0 /* nullptr */ == p) {
1410 0 : if (!t.next_line()) {
1411 0 : t.error("list of sizes must be terminated by '0'");
1412 0 : return 0 /* nullptr */;
1413 : }
1414 0 : p = strtok(t.buf, WS);
1415 : }
1416 : int lower, upper;
1417 8154 : switch (sscanf(p, "%d-%d", &lower, &upper)) {
1418 7493 : case 1:
1419 7493 : upper = lower;
1420 : // fall through
1421 8154 : case 2:
1422 8154 : if (lower <= upper && lower >= 0)
1423 8154 : break;
1424 : // fall through
1425 : default:
1426 0 : t.error("invalid size range '%1'", p);
1427 0 : return 0 /* nullptr */;
1428 : }
1429 8154 : if (i + 2 > n) {
1430 0 : int *old_sizes = sizes;
1431 0 : sizes = new int[n*2];
1432 0 : memcpy(sizes, old_sizes, n*sizeof(int));
1433 0 : n *= 2;
1434 0 : delete[] old_sizes;
1435 : }
1436 8154 : sizes[i++] = lower;
1437 8154 : if (0 == lower)
1438 3877 : break;
1439 4277 : sizes[i++] = upper;
1440 4277 : }
1441 3877 : if (i == 1) {
1442 0 : t.error("must have some sizes");
1443 0 : return 0 /* nullptr */;
1444 : }
1445 : }
1446 9841 : else if (strcmp("styles", p) == 0) {
1447 614 : int style_table_size = 5;
1448 614 : style_table = (const char **)new char *[style_table_size];
1449 : int j;
1450 3684 : for (j = 0; j < style_table_size; j++)
1451 3070 : style_table[j] = 0;
1452 614 : int i = 0;
1453 : for (;;) {
1454 3070 : p = strtok(0 /* nullptr */, WS);
1455 3070 : if (0 /* nullptr */ == p)
1456 614 : break;
1457 : // leave room for terminating 0
1458 2456 : if (i + 1 >= style_table_size) {
1459 0 : const char **old_style_table = style_table;
1460 0 : style_table_size *= 2;
1461 0 : style_table = (const char **)new char*[style_table_size];
1462 0 : for (j = 0; j < i; j++)
1463 0 : style_table[j] = old_style_table[j];
1464 0 : for (; j < style_table_size; j++)
1465 0 : style_table[j] = 0;
1466 0 : delete[] old_style_table;
1467 : }
1468 2456 : char *tem = new char[strlen(p) + 1];
1469 2456 : strcpy(tem, p);
1470 2456 : style_table[i++] = tem;
1471 2456 : }
1472 : }
1473 9227 : else if (strcmp("tcommand", p) == 0)
1474 3797 : has_tcommand = true;
1475 5430 : else if (strcmp("use_charnames_in_special", p) == 0)
1476 127 : use_charnames_in_special = true;
1477 5303 : else if (strcmp("unicode", p) == 0)
1478 340 : is_unicode = true;
1479 4963 : else if (strcmp("image_generator", p) == 0) {
1480 127 : p = strtok(0 /* nullptr */, WS);
1481 127 : if (0 /* nullptr */ == p) {
1482 0 : t.error("'image_generator' directive requires an argument");
1483 0 : return 0 /* nullptr */;
1484 : }
1485 127 : image_generator = strsave(p);
1486 : }
1487 4836 : else if (strcmp("charset", p) == 0)
1488 0 : break;
1489 4836 : else if (unknown_desc_command_handler) {
1490 1981 : char *directive = p;
1491 1981 : p = strtok(0 /* nullptr */, "\n");
1492 1981 : (*unknown_desc_command_handler)(directive, trim_arg(p), t.path,
1493 : t.lineno);
1494 : }
1495 : }
1496 3877 : t.lineno = 0;
1497 3877 : if (0 /* nullptr */ == res) {
1498 0 : t.error("device description file missing 'res' directive");
1499 0 : return 0 /* nullptr */;
1500 : }
1501 3877 : if (0 == unitwidth) {
1502 0 : t.error("device description file missing 'unitwidth' directive");
1503 0 : return 0 /* nullptr */;
1504 : }
1505 3877 : if (0 /* nullptr */ == font_name_table) {
1506 0 : t.error("device description file missing 'fonts' directive");
1507 0 : return 0 /* nullptr */;
1508 : }
1509 3877 : if (0 /* nullptr */ == sizes) {
1510 0 : t.error("device description file missing 'sizes' directive");
1511 0 : return 0 /* nullptr */;
1512 : }
1513 3877 : return path;
1514 : }
1515 :
1516 16198 : void font::handle_unknown_font_command(const char *, const char *,
1517 : const char *, int)
1518 : {
1519 16198 : }
1520 :
1521 : FONT_COMMAND_HANDLER
1522 1498 : font::set_unknown_desc_command_handler(FONT_COMMAND_HANDLER func)
1523 : {
1524 1498 : FONT_COMMAND_HANDLER prev = unknown_desc_command_handler;
1525 1498 : unknown_desc_command_handler = func;
1526 1498 : return prev;
1527 : }
1528 :
1529 : // Local Variables:
1530 : // fill-column: 72
1531 : // mode: C++
1532 : // End:
1533 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|