Line data Source code
1 : /* Copyright 1989-2023 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 : /*
20 : * PostScript documentation:
21 : * http://www.adobe.com/products/postscript/pdfs/PLRM.pdf
22 : * http://partners.adobe.com/public/developer/en/ps/5001.DSC_Spec.pdf
23 : */
24 :
25 : #ifdef HAVE_CONFIG_H
26 : #include <config.h>
27 : #endif
28 :
29 : #include <assert.h>
30 : #include <errno.h>
31 : #include <locale.h> // setlocale()
32 : #include <math.h> // atan2(), sqrt(), tan()
33 : #include <stdcountof.h>
34 : #include <stdint.h> // uint16_t
35 : #include <stdio.h> // EOF, FILE, fclose(), fgets(), fileno(), fseek(),
36 : // getc(), SEEK_SET, setbuf(), stderr, stdout
37 : #include <stdlib.h> // exit(), EXIT_SUCCESS, putenv(), strtol()
38 : #include <string.h> // strchr(), strcmp(), strcpy(), strerror(),
39 : // strlen(), strncmp(), strstr(), strtok()
40 : #include <time.h> // asctime()
41 :
42 : #include <getopt.h> // getopt_long()
43 :
44 : // needed for SET_BINARY()
45 : #include "posix.h"
46 : #include "nonposix.h"
47 :
48 : #include "lib.h" // PI
49 :
50 : #include "cset.h"
51 : #include "curtime.h"
52 : #include "driver.h"
53 : #include "paper.h"
54 : #include "stringclass.h"
55 :
56 : #include "ps.h"
57 :
58 : extern "C" const char *Version_string;
59 :
60 : // Initialize inclusion search path with only the current directory.
61 : search_path include_search_path(0 /* nullptr */, 0 /* nullptr */, 0, 1);
62 :
63 : static int landscape_flag = 0;
64 : static int manual_feed_flag = 0;
65 : static int ncopies = 1;
66 : static int linewidth = -1;
67 : // Non-zero means generate PostScript code that guesses the paper
68 : // length using the imageable area.
69 : static int guess_flag = 0;
70 : static double user_paper_length = 0;
71 : static double user_paper_width = 0;
72 :
73 : // Non-zero if -b was specified on the command line.
74 : static int bflag = 0;
75 : unsigned broken_flags = 0;
76 :
77 : // Non-zero means we need the CMYK extension for PostScript Level 1
78 : static int cmyk_flag = 0;
79 :
80 : #define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
81 : #define MAX_LINE_LENGTH 72
82 : #define FILL_MAX 1000
83 :
84 : const char *const dict_name = "grops";
85 : const char *const defs_dict_name = "DEFS";
86 : const int DEFS_DICT_SPARE = 50;
87 :
88 280 : double degrees(double r)
89 : {
90 280 : return r*180.0/PI;
91 : }
92 :
93 0 : double radians(double d)
94 : {
95 0 : return (d * PI) / 180.0;
96 : }
97 :
98 : // This is used for testing whether a character should be output in the
99 : // PostScript file using \nnn, so we really want the character to be
100 : // less than 0200.
101 :
102 713476 : inline int is_ascii(char c)
103 : {
104 713476 : return (unsigned char)c < 0200;
105 : }
106 :
107 40 : ps_output::ps_output(FILE *f, int n)
108 40 : : fp(f), col(0), max_line_length(n), need_space(0), fixed_point(0)
109 : {
110 40 : }
111 :
112 80 : ps_output &ps_output::set_file(FILE *f)
113 : {
114 80 : fp = f;
115 80 : col = 0;
116 80 : return *this;
117 : }
118 :
119 10363855 : ps_output &ps_output::copy_file(FILE *infp)
120 : {
121 : int c;
122 10363855 : while ((c = getc(infp)) != EOF)
123 10363815 : putc(c, fp);
124 40 : return *this;
125 : }
126 :
127 83 : ps_output &ps_output::end_line()
128 : {
129 83 : if (col != 0) {
130 3 : putc('\n', fp);
131 3 : col = 0;
132 3 : need_space = 0;
133 : }
134 83 : return *this;
135 : }
136 :
137 60476 : ps_output &ps_output::special(const char *s)
138 : {
139 60476 : if (s == 0 || *s == '\0')
140 40 : return *this;
141 60436 : if (col != 0) {
142 60436 : putc('\n', fp);
143 60436 : col = 0;
144 : }
145 60436 : fputs(s, fp);
146 60436 : if (strchr(s, '\0')[-1] != '\n')
147 60436 : putc('\n', fp);
148 60436 : need_space = 0;
149 60436 : return *this;
150 : }
151 :
152 1060 : ps_output &ps_output::simple_comment(const char *s)
153 : {
154 1060 : if (col != 0)
155 450 : putc('\n', fp);
156 1060 : putc('%', fp);
157 1060 : putc('%', fp);
158 1060 : fputs(s, fp);
159 1060 : putc('\n', fp);
160 1060 : col = 0;
161 1060 : need_space = 0;
162 1060 : return *this;
163 : }
164 :
165 530 : ps_output &ps_output::begin_comment(const char *s)
166 : {
167 530 : if (col != 0)
168 290 : putc('\n', fp);
169 530 : putc('%', fp);
170 530 : putc('%', fp);
171 530 : fputs(s, fp);
172 530 : col = 2 + strlen(s);
173 530 : return *this;
174 : }
175 :
176 530 : ps_output &ps_output::end_comment()
177 : {
178 530 : if (col != 0) {
179 530 : putc('\n', fp);
180 530 : col = 0;
181 : }
182 530 : need_space = 0;
183 530 : return *this;
184 : }
185 :
186 980 : ps_output &ps_output::comment_arg(const char *s)
187 : {
188 980 : size_t len = strlen(s);
189 980 : if (col + len + 1 > max_line_length) {
190 0 : putc('\n', fp);
191 0 : fputs("%%+", fp);
192 0 : col = 3;
193 : }
194 980 : putc(' ', fp);
195 980 : fputs(s, fp);
196 980 : col += len + 1;
197 980 : return *this;
198 : }
199 :
200 40 : ps_output &ps_output::set_fixed_point(int n)
201 : {
202 40 : assert(n >= 0 && n <= 10);
203 40 : fixed_point = n;
204 40 : return *this;
205 : }
206 :
207 262 : ps_output &ps_output::put_delimiter(char c)
208 : {
209 262 : if (col + 1 > max_line_length) {
210 16 : putc('\n', fp);
211 16 : col = 0;
212 : }
213 262 : putc(c, fp);
214 262 : col++;
215 262 : need_space = 0;
216 262 : return *this;
217 : }
218 :
219 51535 : ps_output &ps_output::put_string(const uint16_t *s, size_t n,
220 : bool is_utf16le)
221 : {
222 51535 : size_t len = 0;
223 : size_t i;
224 413636 : for (i = 0; i < n; i++) {
225 362101 : uint16_t c = s[i];
226 362101 : if (is_utf16le) {
227 3 : len = (i + 1) * 4;
228 362098 : } else if (is_ascii(c) && csprint(c)) {
229 348910 : if (c == '(' || c == ')' || c == '\\')
230 2994 : len += 2;
231 : else
232 345916 : len += 1;
233 : }
234 : else
235 13188 : len += 4;
236 : }
237 51535 : if ((len > (n * 2)) || is_utf16le) {
238 10261 : if (((col + (n * 2) + 2) > max_line_length)
239 50 : && (((n * 2) + 2) <= max_line_length)) {
240 50 : putc('\n', fp);
241 50 : col = 0;
242 : }
243 10261 : if ((col + 1) > max_line_length) {
244 0 : putc('\n', fp);
245 0 : col = 0;
246 : }
247 10261 : putc('<', fp);
248 10261 : col++;
249 20984 : for (i = 0; i < n; i++) {
250 10723 : if ((col + 2) > max_line_length) {
251 0 : putc('\n', fp);
252 0 : col = 0;
253 : }
254 10723 : if (is_utf16le) {
255 3 : fprintf(fp, "%04X", s[i] & 0xFFFF);
256 3 : col += 4;
257 : } else {
258 10720 : fprintf(fp, "%02x", s[i] & 0377);
259 10720 : col += 2;
260 : }
261 : }
262 10261 : putc('>', fp);
263 10261 : col++;
264 : }
265 : else {
266 41274 : if (((col + len + 2) > max_line_length)
267 5968 : && ((len + 2) <= max_line_length)) {
268 5618 : putc('\n', fp);
269 5618 : col = 0;
270 : }
271 41274 : if ((col + 2) > max_line_length) {
272 11 : putc('\n', fp);
273 11 : col = 0;
274 : }
275 41274 : putc('(', fp);
276 41274 : col++;
277 392652 : for (i = 0; i < n; i++) {
278 351378 : char c = s[i];
279 351378 : if (is_ascii(c) && csprint(c)) {
280 348476 : if (c == '(' || c == ')' || c == '\\')
281 2961 : len = 2;
282 : else
283 345515 : len = 1;
284 : }
285 : else
286 2902 : len = 4;
287 351378 : if ((col + len + 1) > max_line_length) {
288 437 : putc('\\', fp);
289 437 : putc('\n', fp);
290 437 : col = 0;
291 : }
292 351378 : switch (len) {
293 345515 : case 1:
294 345515 : putc(c, fp);
295 345515 : break;
296 2961 : case 2:
297 2961 : putc('\\', fp);
298 2961 : putc(c, fp);
299 2961 : break;
300 2902 : case 4:
301 2902 : fprintf(fp, "\\%03o", c & 0377);
302 2902 : break;
303 0 : default:
304 0 : assert(0 == "unhandled length of encoded character");
305 : }
306 351378 : col += len;
307 : }
308 41274 : putc(')', fp);
309 41274 : col++;
310 : }
311 51535 : need_space = 0;
312 51535 : return *this;
313 : }
314 :
315 92 : ps_output &ps_output::put_number(int n)
316 : {
317 : char buf[1 + INT_DIGITS + 1];
318 92 : sprintf(buf, "%d", n);
319 92 : size_t len = strlen(buf);
320 92 : if ((col > 0) && ((col + len + need_space) > max_line_length)) {
321 0 : putc('\n', fp);
322 0 : col = 0;
323 0 : need_space = 0;
324 : }
325 92 : if (need_space) {
326 90 : putc(' ', fp);
327 90 : col++;
328 : }
329 92 : fputs(buf, fp);
330 92 : col += len;
331 92 : need_space = 1;
332 92 : return *this;
333 : }
334 :
335 856544 : ps_output &ps_output::put_fix_number(int i)
336 : {
337 856544 : const char *p = if_to_a(i, fixed_point);
338 856544 : size_t len = strlen(p);
339 856544 : if ((col > 0) && ((col + len + need_space) > max_line_length)) {
340 48061 : putc('\n', fp);
341 48061 : col = 0;
342 48061 : need_space = 0;
343 : }
344 856544 : if (need_space) {
345 759516 : putc(' ', fp);
346 759516 : col++;
347 : }
348 856544 : fputs(p, fp);
349 856544 : col += len;
350 856544 : need_space = 1;
351 856544 : return *this;
352 : }
353 :
354 280 : ps_output &ps_output::put_float(double d)
355 : {
356 : char buf[128];
357 280 : sprintf(buf, "%.4f", d);
358 280 : ptrdiff_t last = strlen(buf) - 1;
359 1400 : while (buf[last] == '0')
360 1120 : last--;
361 280 : if (buf[last] == '.')
362 280 : last--;
363 280 : buf[++last] = '\0';
364 280 : if ((col > 0) && ((col + last + need_space) > max_line_length)) {
365 14 : putc('\n', fp);
366 14 : col = 0;
367 14 : need_space = 0;
368 : }
369 280 : if (need_space) {
370 266 : putc(' ', fp);
371 266 : col++;
372 : }
373 280 : fputs(buf, fp);
374 280 : col += last;
375 280 : need_space = 1;
376 280 : return *this;
377 : }
378 :
379 594687 : ps_output &ps_output::put_symbol(const char *s)
380 : {
381 594687 : size_t len = strlen(s);
382 594687 : if ((col > 0) && ((col + len + need_space) > max_line_length)) {
383 25093 : putc('\n', fp);
384 25093 : col = 0;
385 25093 : need_space = 0;
386 : }
387 594687 : if (need_space) {
388 507670 : putc(' ', fp);
389 507670 : col++;
390 : }
391 594687 : fputs(s, fp);
392 594687 : col += len;
393 594687 : need_space = 1;
394 594687 : return *this;
395 : }
396 :
397 265004 : ps_output &ps_output::put_color(unsigned int c)
398 : {
399 : char buf[128];
400 265004 : sprintf(buf, "%.3g", double(c) / double(color::MAX_COLOR_VAL));
401 265004 : size_t len = strlen(buf);
402 265004 : if ((col > 0) && ((col + len + need_space) > max_line_length)) {
403 126 : putc('\n', fp);
404 126 : col = 0;
405 126 : need_space = 0;
406 : }
407 265004 : if (need_space) {
408 264877 : putc(' ', fp);
409 264877 : col++;
410 : }
411 265004 : fputs(buf, fp);
412 265004 : col += len;
413 265004 : need_space = 1;
414 265004 : return *this;
415 : }
416 :
417 72201 : ps_output &ps_output::put_literal_symbol(const char *s)
418 : {
419 72201 : size_t len = strlen(s);
420 72201 : if ((col > 0) && ((col + len + 1) > max_line_length)) {
421 25517 : putc('\n', fp);
422 25517 : col = 0;
423 : }
424 72201 : putc('/', fp);
425 72201 : fputs(s, fp);
426 72201 : col += len + 1;
427 72201 : need_space = 1;
428 72201 : return *this;
429 : }
430 :
431 : class ps_font : public font {
432 : ps_font(const char *);
433 : public:
434 : int encoding_index;
435 : char *encoding;
436 : char *reencoded_name;
437 : ~ps_font();
438 : void handle_unknown_font_command(const char * /* command */,
439 : const char * /* arg */,
440 : const char * /* fn */,
441 : int lineno);
442 : static ps_font *load_ps_font(const char * /* s */);
443 : };
444 :
445 143 : ps_font *ps_font::load_ps_font(const char *s)
446 : {
447 143 : ps_font *f = new ps_font(s);
448 143 : if (!f->load()) {
449 0 : delete f;
450 0 : return 0;
451 : }
452 143 : return f;
453 : }
454 :
455 143 : ps_font::ps_font(const char *nm)
456 143 : : font(nm), encoding_index(-1), encoding(0), reencoded_name(0)
457 : {
458 143 : }
459 :
460 286 : ps_font::~ps_font()
461 : {
462 143 : free(encoding);
463 143 : delete[] reencoded_name;
464 286 : }
465 :
466 122 : void ps_font::handle_unknown_font_command(const char *command, const char *arg,
467 : const char *fn, int lineno)
468 : {
469 122 : if (strcmp(command, "encoding") == 0) {
470 122 : if (arg == 0)
471 0 : error_with_file_and_line(fn, lineno,
472 : "'encoding' command requires an argument");
473 : else
474 122 : encoding = strsave(arg);
475 : }
476 122 : }
477 :
478 120 : static void handle_unknown_desc_command(const char *command, const char *arg,
479 : const char *fn, int lineno)
480 : {
481 120 : if (strcmp(command, "broken") == 0) {
482 40 : if (arg == 0)
483 0 : error_with_file_and_line(fn, lineno,
484 : "'broken' command requires an argument");
485 40 : else if (!bflag)
486 40 : broken_flags = atoi(arg);
487 : }
488 120 : }
489 :
490 : struct subencoding {
491 : font *p;
492 : unsigned int num;
493 : int idx;
494 : char *subfont;
495 : const char *glyphs[256];
496 : subencoding *next;
497 :
498 : subencoding(font * /* f */, unsigned int /* n */, int /* ix */,
499 : subencoding * /* s */);
500 : ~subencoding();
501 : };
502 :
503 68 : subencoding::subencoding(font *f, unsigned int n, int ix, subencoding *s)
504 68 : : p(f), num(n), idx(ix), subfont(0 /* nullptr */), next(s)
505 : {
506 17476 : for (int i = 0; i < 256; i++)
507 17408 : glyphs[i] = 0;
508 68 : }
509 :
510 136 : subencoding::~subencoding()
511 : {
512 68 : delete[] subfont;
513 68 : }
514 :
515 : struct style {
516 : font *f;
517 : subencoding *sub;
518 : int point_size;
519 : int height;
520 : int slant;
521 : style();
522 : style(font * /* p */, subencoding * /* s */, int /* sz */,
523 : int /* h */, int /* sl */);
524 : int operator==(const style &) const;
525 : int operator!=(const style &) const;
526 : };
527 :
528 2080 : style::style() : f(0 /* nullptr */)
529 : {
530 2080 : }
531 :
532 319887 : style::style(font *p, subencoding *s, int sz, int h, int sl)
533 319887 : : f(p), sub(s), point_size(sz), height(h), slant(sl)
534 : {
535 319887 : }
536 :
537 360377 : int style::operator==(const style &s) const
538 : {
539 360377 : return (f == s.f
540 312342 : && sub == s.sub
541 312342 : && point_size == s.point_size
542 310901 : && height == s.height
543 672719 : && slant == s.slant);
544 : }
545 :
546 51535 : int style::operator!=(const style &s) const
547 : {
548 51535 : return !(*this == s);
549 : }
550 :
551 : class ps_printer : public printer {
552 : FILE *tempfp;
553 : ps_output out;
554 : int res;
555 : glyph *space_glyph;
556 : int pages_output;
557 : int paper_length;
558 : int equalise_spaces;
559 : enum { SBUF_SIZE = 256 };
560 : uint16_t sbuf[SBUF_SIZE];
561 : int sbuf_len;
562 : int sbuf_start_hpos;
563 : int sbuf_vpos;
564 : int sbuf_end_hpos;
565 : int sbuf_space_width;
566 : int sbuf_space_count;
567 : int sbuf_space_diff_count;
568 : int sbuf_space_code;
569 : int sbuf_kern;
570 : style sbuf_style;
571 : color sbuf_color; // the current PS color
572 : style output_style;
573 : subencoding *subencodings;
574 : int output_hpos;
575 : int output_vpos;
576 : int output_draw_point_size;
577 : int line_thickness;
578 : int output_line_thickness;
579 : unsigned char output_space_code;
580 : enum { MAX_DEFINED_STYLES = 50 };
581 : style defined_styles[MAX_DEFINED_STYLES];
582 : int ndefined_styles;
583 : int next_encoding_index;
584 : int next_subencoding_index;
585 : string defs;
586 : int ndefs;
587 : resource_manager rm;
588 : int invis_count;
589 :
590 : void flush_sbuf();
591 : void set_style(const style & /* sty */);
592 : void set_space_code(unsigned char /* c */);
593 : int set_encoding_index(ps_font * /* f */);
594 : subencoding *set_subencoding(font * /* f */, glyph * /* g */,
595 : uint16_t * /* code */);
596 : char *get_subfont(subencoding * /* sub */, const char * /* stem */);
597 : void do_exec(char * /* arg */, const environment * /* env */);
598 : void do_import(char * /* arg */, const environment * /* env */);
599 : void do_def(char * /* arg */, const environment * /* env */);
600 : void do_mdef(char * /* arg */, const environment * /* env */);
601 : void do_file(char * /* arg */, const environment * /* env */);
602 : void do_invis(char * /* UNUSED */, const environment * /* UNUSED */);
603 : void do_endinvis(char * /* UNUSED */,
604 : const environment * /* UNUSED */);
605 : void set_line_thickness_and_color(const environment * /* env */);
606 : void fill_path(const environment * /* env */);
607 : void encode_fonts();
608 : void encode_subfont(subencoding * /* sub */);
609 : void define_encoding(const char * /* encoding */,
610 : int /* encoding_index */);
611 : void reencode_font(ps_font * /* f */);
612 : void set_color(color * /* col */, int /* fill */ = 0);
613 :
614 : const char *media_name();
615 : int media_width();
616 : int media_height();
617 : void media_set();
618 :
619 : public:
620 : ps_printer(double);
621 : ~ps_printer();
622 : void set_char(glyph * /* g */,
623 : font * /* f */,
624 : const environment * /* env */,
625 : int /* w */,
626 : const char * /* UNUSED */);
627 : void draw(int /* code */, int * /* p */, int /* np */,
628 : const environment * /* env */);
629 : void begin_page(int /* n */);
630 : void end_page(int /* UNUSED */);
631 : void special(char * /* arg */, const environment * /* env */,
632 : char /* type */);
633 : font *make_font(const char * /* nm */);
634 : void end_of_line();
635 : };
636 :
637 : // 'pl' is in inches
638 40 : ps_printer::ps_printer(double pl)
639 : : out(0, MAX_LINE_LENGTH),
640 : pages_output(0),
641 : sbuf_len(0),
642 : subencodings(0),
643 : output_hpos(-1),
644 : output_vpos(-1),
645 : line_thickness(-1),
646 : ndefined_styles(0),
647 : next_encoding_index(0),
648 : next_subencoding_index(0),
649 : ndefs(0),
650 2040 : invis_count(0)
651 : {
652 40 : tempfp = xtmpfile();
653 40 : out.set_file(tempfp);
654 40 : if (linewidth < 0)
655 40 : linewidth = DEFAULT_LINEWIDTH;
656 40 : if (font::hor != 1)
657 0 : fatal("device horizontal motion quantum must be 1, got %1",
658 0 : font::hor);
659 40 : if (font::vert != 1)
660 0 : fatal("device vertical motion quantum must be 1, got %1",
661 0 : font::vert);
662 40 : if (font::res % (font::sizescale*72) != 0)
663 0 : fatal("device resolution must be a multiple of 72*'sizescale', got"
664 0 : " %1 ('sizescale'=%2)", font::res, font::sizescale);
665 40 : int r = font::res;
666 40 : int point = 0;
667 160 : while (r % 10 == 0) {
668 120 : r /= 10;
669 120 : point++;
670 : }
671 40 : res = r;
672 40 : out.set_fixed_point(point);
673 40 : space_glyph = name_to_glyph("space");
674 40 : if (pl == 0)
675 23 : paper_length = font::paperlength;
676 : else
677 17 : paper_length = int(pl * font::res + 0.5);
678 40 : if (paper_length == 0)
679 0 : paper_length = 11 * font::res;
680 40 : equalise_spaces = font::res >= 72000;
681 40 : }
682 :
683 122 : int ps_printer::set_encoding_index(ps_font *f)
684 : {
685 122 : if (f->encoding_index >= 0)
686 0 : return f->encoding_index;
687 259 : for (font_pointer_list *p = font_list; p; p = p->next)
688 236 : if (p->p != f) {
689 115 : char *encoding = (static_cast<ps_font *>(p->p))->encoding;
690 115 : int encoding_index
691 115 : = (static_cast<ps_font *>(p->p))->encoding_index;
692 115 : if ((encoding != 0 /* nullptr */) && encoding_index >= 0
693 99 : && strcmp(f->encoding, encoding) == 0) {
694 99 : return f->encoding_index = encoding_index;
695 : }
696 : }
697 23 : return f->encoding_index = next_encoding_index++;
698 : }
699 :
700 319887 : subencoding *ps_printer::set_subencoding(font *f, glyph *g,
701 : uint16_t *code)
702 : {
703 319887 : unsigned int idx = f->get_code(g);
704 319887 : const char *psname = f->get_internal_name();
705 :
706 319887 : if (psname && strstr(psname, "-UTF16-")) {
707 : /* Unicode, convert to UTF-16LE */
708 3 : if (idx < 0x10000) {
709 3 : code[0] = idx;
710 3 : code[1] = 0;
711 : } else {
712 : // Encode surrogate pairs.
713 0 : code[0] = (idx - 0x10000) / 0x400 + 0xD800;
714 0 : code[1] = (idx - 0x10000) % 0x400 + 0xDC00;
715 : }
716 3 : return 0 /* nullptr */;
717 : }
718 :
719 319884 : code[0] = idx % 256;
720 319884 : code[1] = 0;
721 319884 : unsigned int num = idx >> 8;
722 319884 : if (num == 0)
723 319816 : return 0 /* nullptr */;
724 68 : subencoding *p = 0 /* nullptr */;
725 1190 : for (p = subencodings; p; p = p->next)
726 1122 : if ((p->p == f) && (p->num == num))
727 0 : break;
728 68 : if (0 /* nullptr */ == p)
729 68 : p = subencodings = new subencoding(f, num, next_subencoding_index++,
730 68 : subencodings);
731 68 : p->glyphs[*code] = f->get_special_device_encoding(g);
732 68 : return p;
733 : }
734 :
735 68 : char *ps_printer::get_subfont(subencoding *sub, const char *stem)
736 : {
737 68 : assert(sub != 0 /* nullptr */);
738 68 : if (!sub->subfont) {
739 68 : char *tem = new char[strlen(stem) + 2 + INT_DIGITS + 1];
740 68 : sprintf(tem, "%s@@%d", stem, sub->idx);
741 68 : sub->subfont = tem;
742 : }
743 68 : return sub->subfont;
744 : }
745 :
746 319908 : void ps_printer::set_char(glyph *g, font *f, const environment *env,
747 : int w, const char *)
748 : {
749 319908 : if (g == space_glyph || invis_count > 0)
750 268373 : return;
751 : uint16_t code[2];
752 319887 : subencoding *sub = set_subencoding(f, g, code);
753 319887 : style sty(f, sub, env->size, env->height, env->slant);
754 319887 : if (sty.slant != 0) {
755 0 : if (sty.slant > 80 || sty.slant < -80) {
756 0 : error("silly slant '%1' degrees", sty.slant);
757 0 : sty.slant = 0;
758 : }
759 : }
760 319887 : if (sbuf_len > 0) {
761 578648 : if (sbuf_len < SBUF_SIZE
762 289324 : && sty == sbuf_style
763 283483 : && sbuf_vpos == env->vpos
764 578648 : && sbuf_color == *env->col) {
765 283409 : if (sbuf_end_hpos == env->hpos) {
766 220304 : sbuf[sbuf_len++] = code[0];
767 220304 : if (code[1] > 0)
768 0 : sbuf[sbuf_len++] = code[1];
769 220304 : sbuf_end_hpos += w + sbuf_kern;
770 220304 : return;
771 : }
772 63105 : if ((sbuf_len == 1) && (sbuf_kern == 0)) {
773 5834 : sbuf_kern = env->hpos - sbuf_end_hpos;
774 5834 : sbuf_end_hpos = env->hpos + sbuf_kern + w;
775 5834 : sbuf[sbuf_len++] = code[0];
776 5834 : if (code[1] > 0)
777 0 : sbuf[sbuf_len++] = code[1];
778 5834 : return;
779 : }
780 : /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
781 : starting a new string. */
782 57271 : if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
783 46136 : && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
784 44094 : if (sbuf_space_code < 0) {
785 13470 : if (f->contains(space_glyph) && !sub) {
786 13470 : sbuf_space_code = f->get_code(space_glyph);
787 13470 : sbuf_space_width = env->hpos - sbuf_end_hpos;
788 13470 : sbuf_end_hpos = env->hpos + w + sbuf_kern;
789 13470 : sbuf[sbuf_len++] = sbuf_space_code;
790 13470 : sbuf[sbuf_len++] = code[0];
791 13470 : if (code[1] > 0)
792 0 : sbuf[sbuf_len++] = code[1];
793 13470 : sbuf_space_count++;
794 13470 : return;
795 : }
796 : }
797 : else {
798 30624 : int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
799 30624 : if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) {
800 28744 : sbuf_end_hpos = env->hpos + w + sbuf_kern;
801 28744 : sbuf[sbuf_len++] = sbuf_space_code;
802 28744 : sbuf[sbuf_len++] = code[0];
803 28744 : if (code[1] > 0)
804 0 : sbuf[sbuf_len++] = code[1];
805 28744 : sbuf_space_count++;
806 28744 : if (diff == 1)
807 1662 : sbuf_space_diff_count++;
808 27082 : else if (diff == -1)
809 1747 : sbuf_space_diff_count--;
810 28744 : return;
811 : }
812 : }
813 : }
814 : }
815 20972 : flush_sbuf();
816 : }
817 51535 : sbuf_len = 1;
818 51535 : sbuf[0] = code[0];
819 51535 : if (code[1] > 0)
820 0 : sbuf[sbuf_len++] = code[1];
821 51535 : sbuf_end_hpos = env->hpos + w;
822 51535 : sbuf_start_hpos = env->hpos;
823 51535 : sbuf_vpos = env->vpos;
824 51535 : sbuf_style = sty;
825 51535 : sbuf_space_code = -1;
826 51535 : sbuf_space_width = 0;
827 51535 : sbuf_space_count = sbuf_space_diff_count = 0;
828 51535 : sbuf_kern = 0;
829 51535 : if (sbuf_color != *env->col)
830 23495 : set_color(env->col);
831 : }
832 :
833 145 : static char *make_encoding_name(int encoding_index)
834 : {
835 : static char buf[3 + INT_DIGITS + 1];
836 145 : sprintf(buf, "ENC%d", encoding_index);
837 145 : return buf;
838 : }
839 :
840 136 : static char *make_subencoding_name(int subencoding_index)
841 : {
842 : static char buf[6 + INT_DIGITS + 1];
843 136 : sprintf(buf, "SUBENC%d", subencoding_index);
844 136 : return buf;
845 : }
846 :
847 : const char *const WS = " \t\n\r";
848 :
849 23 : void ps_printer::define_encoding(const char *encoding,
850 : int encoding_index)
851 : {
852 : char *vec[256];
853 : int i;
854 5911 : for (i = 0; i < 256; i++)
855 5888 : vec[i] = 0;
856 : char *path;
857 23 : if (strchr(encoding, '/') != 0 /* nullptr */)
858 0 : fatal("a '/' is not allowed in encoding file name: '%1'", encoding);
859 23 : FILE *fp = font::open_file(encoding, &path);
860 23 : if (0 /* nullptr */ == fp)
861 0 : fatal("cannot open encoding file '%1'", encoding);
862 23 : int lineno = 1;
863 23 : const int BUFFER_SIZE = 512;
864 : char buf[BUFFER_SIZE];
865 5451 : while (fgets(buf, BUFFER_SIZE, fp) != 0) {
866 5428 : char *p = buf;
867 5428 : while (csspace(*p))
868 0 : p++;
869 5428 : if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) {
870 5336 : char *q = strtok(0, WS);
871 5336 : int n = 0; // pacify compiler
872 5336 : if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256)
873 0 : fatal_with_file_and_line(path, lineno, "invalid encoding file:"
874 : " expected integer in range 0-255 as second word on line,"
875 0 : " got '%1'", q);
876 5336 : vec[n] = new char[strlen(p) + 1];
877 5336 : strcpy(vec[n], p);
878 : }
879 5428 : lineno++;
880 : }
881 23 : free(path);
882 23 : out.put_literal_symbol(make_encoding_name(encoding_index))
883 23 : .put_delimiter('[');
884 5911 : for (i = 0; i < 256; i++) {
885 5888 : if (vec[i] == 0)
886 552 : out.put_literal_symbol(".notdef");
887 : else {
888 5336 : out.put_literal_symbol(vec[i]);
889 5336 : delete[] vec[i];
890 : }
891 : }
892 23 : out.put_delimiter(']')
893 23 : .put_symbol("def");
894 23 : fclose(fp);
895 23 : }
896 :
897 122 : void ps_printer::reencode_font(ps_font *f)
898 : {
899 122 : out.put_literal_symbol(f->reencoded_name)
900 122 : .put_symbol(make_encoding_name(f->encoding_index))
901 122 : .put_literal_symbol(f->get_internal_name())
902 122 : .put_symbol("RE");
903 122 : }
904 :
905 40 : void ps_printer::encode_fonts()
906 : {
907 40 : if (next_encoding_index == 0)
908 17 : return;
909 23 : char *done_encoding = new char[next_encoding_index];
910 46 : for (int i = 0; i < next_encoding_index; i++)
911 23 : done_encoding[i] = 0;
912 165 : for (font_pointer_list *f = font_list; f; f = f->next) {
913 142 : int encoding_index = ((ps_font *)f->p)->encoding_index;
914 142 : if (encoding_index >= 0) {
915 122 : assert(encoding_index < next_encoding_index);
916 122 : if (!done_encoding[encoding_index]) {
917 23 : done_encoding[encoding_index] = 1;
918 23 : define_encoding(((ps_font *)f->p)->encoding, encoding_index);
919 : }
920 122 : reencode_font((ps_font *)f->p);
921 : }
922 : }
923 23 : delete[] done_encoding;
924 : }
925 :
926 68 : void ps_printer::encode_subfont(subencoding *sub)
927 : {
928 68 : assert(sub != 0);
929 68 : out.put_literal_symbol(make_subencoding_name(sub->idx))
930 68 : .put_delimiter('[');
931 17476 : for (int i = 0; i < 256; i++)
932 : {
933 17408 : if (sub->glyphs[i])
934 68 : out.put_literal_symbol(sub->glyphs[i]);
935 : else
936 17340 : out.put_literal_symbol(".notdef");
937 : }
938 68 : out.put_delimiter(']')
939 68 : .put_symbol("def");
940 68 : }
941 :
942 31107 : void ps_printer::set_style(const style &sty)
943 : {
944 : char buf[1 + INT_DIGITS + 1];
945 43635 : for (int i = 0; i < ndefined_styles; i++)
946 19518 : if (sty == defined_styles[i]) {
947 6990 : sprintf(buf, "F%d", i);
948 6990 : out.put_symbol(buf);
949 6990 : return;
950 : }
951 24117 : if (ndefined_styles >= MAX_DEFINED_STYLES)
952 0 : ndefined_styles = 0;
953 24117 : sprintf(buf, "F%d", ndefined_styles);
954 24117 : out.put_literal_symbol(buf);
955 24117 : const char *psname = sty.f->get_internal_name();
956 24117 : if (0 /* nullptr */ == psname)
957 0 : fatal("cannot set style; font description file '%1' lacks an"
958 0 : " 'internalname' directive", sty.f->get_filename());
959 24117 : char *encoding = ((ps_font *)sty.f)->encoding;
960 24117 : if (sty.sub == 0) {
961 24049 : if (encoding != 0 /* nullptr */) {
962 22416 : char *s = ((ps_font *)sty.f)->reencoded_name;
963 22416 : if (0 /* nullptr */ == s) {
964 122 : int ei = set_encoding_index((ps_font *)sty.f);
965 122 : char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1];
966 122 : sprintf(tem, "%s@%d", psname, ei);
967 122 : psname = tem;
968 122 : ((ps_font *)sty.f)->reencoded_name = tem;
969 : }
970 : else
971 22294 : psname = s;
972 : }
973 : }
974 : else
975 68 : psname = get_subfont(sty.sub, psname);
976 24117 : out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size);
977 24117 : if (sty.height != 0 || sty.slant != 0) {
978 0 : int h = sty.height == 0 ? sty.point_size : sty.height;
979 0 : h *= font::res/(72*font::sizescale);
980 0 : int c = int(h*tan(radians(sty.slant)) + .5);
981 0 : out.put_fix_number(c)
982 0 : .put_fix_number(h)
983 0 : .put_literal_symbol(psname)
984 0 : .put_symbol("MF");
985 : }
986 : else {
987 24117 : out.put_literal_symbol(psname)
988 24117 : .put_symbol("SF");
989 : }
990 24117 : defined_styles[ndefined_styles++] = sty;
991 : }
992 :
993 89325 : void ps_printer::set_color(color *col, int fill)
994 : {
995 89325 : sbuf_color = *col;
996 : unsigned int components[4];
997 : char s[3];
998 89325 : color_scheme cs = col->get_components(components);
999 89325 : s[0] = fill ? 'F' : 'C';
1000 89325 : s[2] = 0;
1001 89325 : switch (cs) {
1002 781 : case DEFAULT: // black
1003 781 : out.put_symbol("0");
1004 781 : s[1] = 'g';
1005 781 : break;
1006 88230 : case RGB:
1007 88230 : out.put_color(Red)
1008 88230 : .put_color(Green)
1009 88230 : .put_color(Blue);
1010 88230 : s[1] = 'r';
1011 88230 : break;
1012 0 : case CMY:
1013 0 : col->get_cmyk(&Cyan, &Magenta, &Yellow, &Black);
1014 : // fall through
1015 0 : case CMYK:
1016 0 : out.put_color(Cyan)
1017 0 : .put_color(Magenta)
1018 0 : .put_color(Yellow)
1019 0 : .put_color(Black);
1020 0 : s[1] = 'k';
1021 0 : cmyk_flag = 1;
1022 0 : break;
1023 314 : case GRAY:
1024 314 : out.put_color(Gray);
1025 314 : s[1] = 'g';
1026 314 : break;
1027 : }
1028 89325 : out.put_symbol(s);
1029 89325 : }
1030 :
1031 0 : void ps_printer::set_space_code(unsigned char c)
1032 : {
1033 0 : out.put_literal_symbol("SC")
1034 0 : .put_number(c)
1035 0 : .put_symbol("def");
1036 0 : }
1037 :
1038 131209 : void ps_printer::end_of_line()
1039 : {
1040 131209 : flush_sbuf();
1041 : // this ensures that we do an absolute motion to the beginning of a line
1042 131209 : output_vpos = output_hpos = -1;
1043 131209 : }
1044 :
1045 440513 : void ps_printer::flush_sbuf()
1046 : {
1047 : enum {
1048 : NONE,
1049 : RELATIVE_H,
1050 : RELATIVE_V,
1051 : RELATIVE_HV,
1052 : ABSOLUTE
1053 440513 : } motion = NONE;
1054 440513 : int space_flag = 0;
1055 440513 : if (sbuf_len == 0)
1056 388978 : return;
1057 51535 : if (output_style != sbuf_style) {
1058 31107 : set_style(sbuf_style);
1059 31107 : output_style = sbuf_style;
1060 : }
1061 51535 : int extra_space = 0;
1062 51535 : if (output_hpos < 0 || output_vpos < 0)
1063 30563 : motion = ABSOLUTE;
1064 : else {
1065 20972 : if (output_hpos != sbuf_start_hpos)
1066 19994 : motion = RELATIVE_H;
1067 20972 : if (output_vpos != sbuf_vpos) {
1068 253 : if (motion != NONE)
1069 185 : motion = RELATIVE_HV;
1070 : else
1071 68 : motion = RELATIVE_V;
1072 : }
1073 : }
1074 51535 : if (sbuf_space_code >= 0) {
1075 13470 : int w = sbuf_style.f->get_width(space_glyph, sbuf_style.point_size);
1076 13470 : if (w + sbuf_kern != sbuf_space_width) {
1077 7277 : if (sbuf_space_code != output_space_code) {
1078 0 : set_space_code(sbuf_space_code);
1079 0 : output_space_code = sbuf_space_code;
1080 : }
1081 7277 : space_flag = 1;
1082 7277 : extra_space = sbuf_space_width - w - sbuf_kern;
1083 7277 : if (sbuf_space_diff_count > sbuf_space_count/2)
1084 173 : extra_space++;
1085 7104 : else if (sbuf_space_diff_count < -(sbuf_space_count/2))
1086 200 : extra_space--;
1087 : }
1088 : }
1089 51535 : if (space_flag)
1090 7277 : out.put_fix_number(extra_space);
1091 51535 : if (sbuf_kern != 0)
1092 5834 : out.put_fix_number(sbuf_kern);
1093 51535 : const char *psname = sbuf_style.f->get_internal_name();
1094 51535 : bool is_utf16le = false;
1095 51535 : if ((psname != 0 /* nullptr */) && strstr(psname, "-UTF16-"))
1096 1 : is_utf16le = true;
1097 51535 : out.put_string(sbuf, sbuf_len, is_utf16le);
1098 51535 : char command_array[] = {'A', 'B', 'C', 'D',
1099 : 'E', 'F', 'G', 'H',
1100 : 'I', 'J', 'K', 'L',
1101 : 'M', 'N', 'O', 'P',
1102 : 'Q', 'R', 'S', 'T'};
1103 : char sym[2];
1104 51535 : sym[0] = command_array[motion*4 + space_flag + 2*(sbuf_kern != 0)];
1105 51535 : sym[1] = '\0';
1106 51535 : switch (motion) {
1107 910 : case NONE:
1108 910 : break;
1109 30563 : case ABSOLUTE:
1110 30563 : out.put_fix_number(sbuf_start_hpos)
1111 30563 : .put_fix_number(sbuf_vpos);
1112 30563 : break;
1113 19809 : case RELATIVE_H:
1114 19809 : out.put_fix_number(sbuf_start_hpos - output_hpos);
1115 19809 : break;
1116 68 : case RELATIVE_V:
1117 68 : out.put_fix_number(sbuf_vpos - output_vpos);
1118 68 : break;
1119 185 : case RELATIVE_HV:
1120 185 : out.put_fix_number(sbuf_start_hpos - output_hpos)
1121 185 : .put_fix_number(sbuf_vpos - output_vpos);
1122 185 : break;
1123 0 : default:
1124 0 : assert(0 == "unhandled motion relativity type");
1125 : }
1126 51535 : out.put_symbol(sym);
1127 51535 : output_hpos = sbuf_end_hpos;
1128 51535 : output_vpos = sbuf_vpos;
1129 51535 : sbuf_len = 0;
1130 : }
1131 :
1132 73477 : void ps_printer::set_line_thickness_and_color(const environment *env)
1133 : {
1134 73477 : if (line_thickness < 0) {
1135 6026 : if (output_draw_point_size != env->size) {
1136 : // we ought to check for overflow here
1137 241 : int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
1138 241 : out.put_fix_number(lw)
1139 241 : .put_symbol("LW");
1140 241 : output_draw_point_size = env->size;
1141 241 : output_line_thickness = -1;
1142 : }
1143 : }
1144 : else {
1145 67451 : if (output_line_thickness != line_thickness) {
1146 60700 : out.put_fix_number(line_thickness)
1147 60700 : .put_symbol("LW");
1148 60700 : output_line_thickness = line_thickness;
1149 60700 : output_draw_point_size = -1;
1150 : }
1151 : }
1152 73477 : if (sbuf_color != *env->col)
1153 712 : set_color(env->col);
1154 73477 : }
1155 :
1156 32778 : void ps_printer::fill_path(const environment *env)
1157 : {
1158 32778 : if (sbuf_color == *env->fill)
1159 95 : out.put_symbol("FL");
1160 : else
1161 32683 : set_color(env->fill, 1);
1162 32778 : }
1163 :
1164 227560 : void ps_printer::draw(int code, int *p, int np, const environment *env)
1165 : {
1166 227560 : if (invis_count > 0)
1167 3 : return;
1168 227557 : flush_sbuf();
1169 227557 : int fill_flag = 0;
1170 227557 : switch (code) {
1171 144 : case 'C':
1172 144 : fill_flag = 1;
1173 : // fall through
1174 308 : case 'c':
1175 : // troff adds an extra argument to C
1176 308 : if (np != 1 && !(code == 'C' && np == 2)) {
1177 0 : error("1 argument required for circle");
1178 0 : break;
1179 : }
1180 308 : out.put_fix_number(env->hpos + p[0]/2)
1181 308 : .put_fix_number(env->vpos)
1182 308 : .put_fix_number(p[0]/2)
1183 308 : .put_symbol("DC");
1184 308 : if (fill_flag)
1185 144 : fill_path(env);
1186 : else {
1187 164 : set_line_thickness_and_color(env);
1188 164 : out.put_symbol("ST");
1189 : }
1190 308 : break;
1191 72846 : case 'l':
1192 72846 : if (np != 2) {
1193 0 : error("2 arguments required for line");
1194 0 : break;
1195 : }
1196 72846 : set_line_thickness_and_color(env);
1197 72846 : out.put_fix_number(p[0] + env->hpos)
1198 72846 : .put_fix_number(p[1] + env->vpos)
1199 72846 : .put_fix_number(env->hpos)
1200 72846 : .put_fix_number(env->vpos)
1201 72846 : .put_symbol("DL");
1202 72846 : break;
1203 0 : case 'E':
1204 0 : fill_flag = 1;
1205 : // fall through
1206 26 : case 'e':
1207 26 : if (np != 2) {
1208 0 : error("2 arguments required for ellipse");
1209 0 : break;
1210 : }
1211 26 : out.put_fix_number(p[0])
1212 26 : .put_fix_number(p[1])
1213 26 : .put_fix_number(env->hpos + p[0]/2)
1214 26 : .put_fix_number(env->vpos)
1215 26 : .put_symbol("DE");
1216 26 : if (fill_flag)
1217 0 : fill_path(env);
1218 : else {
1219 26 : set_line_thickness_and_color(env);
1220 26 : out.put_symbol("ST");
1221 : }
1222 26 : break;
1223 32634 : case 'P':
1224 32634 : fill_flag = 1;
1225 : // fall through
1226 32925 : case 'p':
1227 : {
1228 32925 : if (np & 1) {
1229 0 : error("even number of arguments required for polygon");
1230 0 : break;
1231 : }
1232 32925 : if (np == 0) {
1233 0 : error("no arguments for polygon");
1234 0 : break;
1235 : }
1236 32925 : out.put_fix_number(env->hpos)
1237 32925 : .put_fix_number(env->vpos)
1238 32925 : .put_symbol("MT");
1239 131501 : for (int i = 0; i < np; i += 2)
1240 98576 : out.put_fix_number(p[i])
1241 98576 : .put_fix_number(p[i+1])
1242 98576 : .put_symbol("RL");
1243 32925 : out.put_symbol("CL");
1244 32925 : if (fill_flag)
1245 32634 : fill_path(env);
1246 : else {
1247 291 : set_line_thickness_and_color(env);
1248 291 : out.put_symbol("ST");
1249 : }
1250 32925 : break;
1251 : }
1252 10 : case '~':
1253 : {
1254 10 : if (np & 1) {
1255 0 : error("even number of arguments required for spline");
1256 0 : break;
1257 : }
1258 10 : if (np == 0) {
1259 0 : error("no arguments for spline");
1260 0 : break;
1261 : }
1262 10 : out.put_fix_number(env->hpos)
1263 10 : .put_fix_number(env->vpos)
1264 10 : .put_symbol("MT");
1265 10 : out.put_fix_number(p[0]/2)
1266 10 : .put_fix_number(p[1]/2)
1267 10 : .put_symbol("RL");
1268 : /* tnum/tden should be between 0 and 1; the closer it is to 1
1269 : the tighter the curve will be to the guiding lines; 2/3
1270 : is the standard value */
1271 10 : const int tnum = 2;
1272 10 : const int tden = 3;
1273 34 : for (int i = 0; i < np - 2; i += 2) {
1274 24 : out.put_fix_number((p[i]*tnum)/(2*tden))
1275 24 : .put_fix_number((p[i + 1]*tnum)/(2*tden))
1276 24 : .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden))
1277 24 : .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden))
1278 24 : .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2)
1279 24 : .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2)
1280 24 : .put_symbol("RC");
1281 : }
1282 10 : out.put_fix_number(p[np - 2] - p[np - 2]/2)
1283 10 : .put_fix_number(p[np - 1] - p[np - 1]/2)
1284 10 : .put_symbol("RL");
1285 10 : set_line_thickness_and_color(env);
1286 10 : out.put_symbol("ST");
1287 : }
1288 10 : break;
1289 140 : case 'a':
1290 : {
1291 140 : if (np != 4) {
1292 0 : error("4 arguments required for arc");
1293 0 : break;
1294 : }
1295 140 : set_line_thickness_and_color(env);
1296 : double c[2];
1297 140 : if (adjust_arc_center(p, c))
1298 140 : out.put_fix_number(env->hpos + int(c[0]))
1299 140 : .put_fix_number(env->vpos + int(c[1]))
1300 140 : .put_fix_number(int(sqrt(c[0]*c[0] + c[1]*c[1])))
1301 140 : .put_float(degrees(atan2(-c[1], -c[0])))
1302 140 : .put_float(degrees(atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])))
1303 140 : .put_symbol("DA");
1304 : else
1305 0 : out.put_fix_number(p[0] + p[2] + env->hpos)
1306 0 : .put_fix_number(p[1] + p[3] + env->vpos)
1307 0 : .put_fix_number(env->hpos)
1308 0 : .put_fix_number(env->vpos)
1309 0 : .put_symbol("DL");
1310 : }
1311 140 : break;
1312 121302 : case 't':
1313 121302 : if (np == 0)
1314 0 : line_thickness = -1;
1315 : else {
1316 : // troff gratuitously adds an extra 0
1317 121302 : if (np != 1 && np != 2) {
1318 0 : error("0 or 1 argument required for thickness");
1319 0 : break;
1320 : }
1321 121302 : line_thickness = p[0];
1322 : }
1323 121302 : break;
1324 0 : default:
1325 0 : error("unrecognized drawing command '%1'", char(code));
1326 0 : break;
1327 : }
1328 227557 : output_hpos = output_vpos = -1;
1329 : }
1330 :
1331 120 : const char *ps_printer::media_name()
1332 : {
1333 120 : return "Default";
1334 : }
1335 :
1336 80 : int ps_printer::media_width()
1337 : {
1338 : /*
1339 : * NOTE:
1340 : * Although paper dimensions are defined as a pair of real numbers,
1341 : * it seems to be a common convention to round to the nearest
1342 : * PostScript unit. For example, A4 is really 595.276 by 841.89 but
1343 : * we use 595 by 842.
1344 : *
1345 : * This is probably a good compromise, especially since the
1346 : * PostScript definition specifies that media matching should be done
1347 : * within a tolerance of 5 units.
1348 : */
1349 80 : return int(user_paper_width ? user_paper_width*72.0 + 0.5
1350 80 : : font::paperwidth*72.0/font::res + 0.5);
1351 : }
1352 :
1353 80 : int ps_printer::media_height()
1354 : {
1355 80 : return int(user_paper_length ? user_paper_length*72.0 + 0.5
1356 80 : : paper_length*72.0/font::res + 0.5);
1357 : }
1358 :
1359 40 : void ps_printer::media_set()
1360 : {
1361 : /*
1362 : * The setpagedevice implies an erasepage and initgraphics, and
1363 : * must thus precede any descriptions for a particular page.
1364 : *
1365 : * NOTE:
1366 : * This does not work with ps2pdf when there are included eps
1367 : * segments that contain PageSize/setpagedevice.
1368 : * This might be a bug in ghostscript -- must be investigated.
1369 : * Using setpagedevice in an .eps is really the wrong concept, anyway.
1370 : *
1371 : * NOTE:
1372 : * For the future, this is really the place to insert other
1373 : * media selection features, like:
1374 : * MediaColor
1375 : * MediaPosition
1376 : * MediaType
1377 : * MediaWeight
1378 : * MediaClass
1379 : * TraySwitch
1380 : * ManualFeed
1381 : * InsertSheet
1382 : * Duplex
1383 : * Collate
1384 : * ProcessColorModel
1385 : * etc.
1386 : */
1387 40 : if (!(broken_flags & (USE_PS_ADOBE_2_0|NO_PAPERSIZE))) {
1388 40 : out.begin_comment("BeginFeature:")
1389 40 : .comment_arg("*PageSize")
1390 40 : .comment_arg(media_name())
1391 40 : .end_comment();
1392 40 : int w = media_width();
1393 40 : int h = media_height();
1394 40 : if (w > 0 && h > 0)
1395 : // warning to user is done elsewhere
1396 40 : fprintf(out.get_file(),
1397 : "<< /PageSize [ %d %d ] /ImagingBBox null >> setpagedevice\n",
1398 : w, h);
1399 40 : out.simple_comment("EndFeature");
1400 : }
1401 40 : }
1402 :
1403 330 : void ps_printer::begin_page(int n)
1404 : {
1405 330 : out.begin_comment("Page:")
1406 330 : .comment_arg(i_to_a(n));
1407 330 : out.comment_arg(i_to_a(++pages_output))
1408 660 : .end_comment();
1409 330 : output_style.f = 0;
1410 330 : output_space_code = 32;
1411 330 : output_draw_point_size = -1;
1412 330 : output_line_thickness = -1;
1413 330 : output_hpos = output_vpos = -1;
1414 330 : ndefined_styles = 0;
1415 330 : out.simple_comment("BeginPageSetup");
1416 :
1417 : #if 0
1418 : /*
1419 : * NOTE:
1420 : * may decide to do this once per page
1421 : */
1422 : media_set();
1423 : #endif
1424 :
1425 330 : out.put_symbol("BP")
1426 330 : .simple_comment("EndPageSetup");
1427 330 : if (sbuf_color != default_color)
1428 0 : set_color(&sbuf_color);
1429 330 : }
1430 :
1431 330 : void ps_printer::end_page(int)
1432 : {
1433 330 : flush_sbuf();
1434 330 : set_color(&default_color);
1435 330 : out.put_symbol("EP");
1436 330 : if (invis_count != 0) {
1437 0 : error("missing 'endinvis' command");
1438 0 : invis_count = 0;
1439 : }
1440 330 : }
1441 :
1442 143 : font *ps_printer::make_font(const char *nm)
1443 : {
1444 143 : return ps_font::load_ps_font(nm);
1445 : }
1446 :
1447 80 : ps_printer::~ps_printer()
1448 : {
1449 40 : current_lineno = 0; // At this point, we've read all the input.
1450 40 : out.simple_comment("Trailer")
1451 40 : .put_symbol("end")
1452 40 : .simple_comment("EOF");
1453 40 : if (fseek(tempfp, 0L, SEEK_SET) < 0)
1454 0 : fatal("unable to seek within temporary file: %1", strerror(errno));
1455 40 : fputs("%!PS-Adobe-", stdout);
1456 40 : fputs((broken_flags & USE_PS_ADOBE_2_0) ? "2.0" : "3.0", stdout);
1457 40 : putchar('\n');
1458 40 : out.set_file(stdout);
1459 40 : if (cmyk_flag)
1460 0 : out.begin_comment("Extensions:")
1461 0 : .comment_arg("CMYK")
1462 0 : .end_comment();
1463 40 : out.begin_comment("Creator:")
1464 40 : .comment_arg("groff")
1465 40 : .comment_arg("version")
1466 80 : .comment_arg(Version_string)
1467 40 : .end_comment();
1468 : {
1469 40 : fputs("%%CreationDate: ", out.get_file());
1470 40 : struct tm *t = current_time();
1471 40 : fputs(asctime(t), out.get_file());
1472 : }
1473 183 : for (font_pointer_list *f = font_list; f; f = f->next) {
1474 143 : ps_font *psf = (ps_font *)(f->p);
1475 143 : rm.need_font(psf->get_internal_name());
1476 : }
1477 40 : rm.print_header_comments(out);
1478 40 : out.begin_comment("Pages:")
1479 40 : .comment_arg(i_to_a(pages_output))
1480 40 : .end_comment();
1481 40 : out.begin_comment("PageOrder:")
1482 40 : .comment_arg("Ascend")
1483 40 : .end_comment();
1484 40 : if (!(broken_flags & NO_PAPERSIZE)) {
1485 40 : int w = media_width();
1486 40 : int h = media_height();
1487 40 : if (w > 0 && h > 0)
1488 40 : fprintf(out.get_file(),
1489 : "%%%%DocumentMedia: %s %d %d %d %s %s\n",
1490 : media_name(), // tag name of media
1491 : w, // media width
1492 : h, // media height
1493 : 0, // weight in g/m2
1494 : "()", // paper color, e.g. white
1495 : "()" // preprinted form type
1496 : );
1497 : else {
1498 0 : if (h <= 0)
1499 : // see ps_printer::ps_printer
1500 0 : warning("bad paper height, defaulting to 11i");
1501 0 : if (w <= 0)
1502 0 : warning("bad paper width");
1503 : }
1504 : }
1505 40 : out.begin_comment("Orientation:")
1506 40 : .comment_arg(landscape_flag ? "Landscape" : "Portrait")
1507 40 : .end_comment();
1508 40 : if (ncopies != 1) {
1509 0 : out.end_line();
1510 0 : fprintf(out.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies);
1511 : }
1512 40 : out.simple_comment("EndComments");
1513 40 : if (!(broken_flags & NO_PAPERSIZE)) {
1514 : /* gv works fine without this one, but it really should be there. */
1515 40 : out.simple_comment("BeginDefaults");
1516 40 : fprintf(out.get_file(), "%%%%PageMedia: %s\n", media_name());
1517 40 : out.simple_comment("EndDefaults");
1518 : }
1519 40 : out.simple_comment("BeginProlog");
1520 40 : rm.output_prolog(out);
1521 40 : if (!(broken_flags & NO_SETUP_SECTION)) {
1522 40 : out.simple_comment("EndProlog");
1523 40 : out.simple_comment("BeginSetup");
1524 : }
1525 : #if 1
1526 : /*
1527 : * Define paper (i.e., media) size for entire document here.
1528 : * This allows ps2pdf to correctly determine page size, for instance.
1529 : */
1530 40 : media_set();
1531 : #endif
1532 40 : rm.document_setup(out);
1533 40 : out.put_symbol(dict_name)
1534 40 : .put_symbol("begin");
1535 40 : if (ndefs > 0)
1536 10 : ndefs += DEFS_DICT_SPARE;
1537 40 : out.put_literal_symbol(defs_dict_name)
1538 40 : .put_number(ndefs + 1)
1539 40 : .put_symbol("dict")
1540 40 : .put_symbol("def");
1541 40 : out.put_symbol(defs_dict_name)
1542 40 : .put_symbol("begin");
1543 40 : out.put_literal_symbol("u")
1544 40 : .put_delimiter('{')
1545 40 : .put_fix_number(1)
1546 40 : .put_symbol("mul")
1547 40 : .put_delimiter('}')
1548 40 : .put_symbol("bind")
1549 40 : .put_symbol("def");
1550 40 : defs += '\0';
1551 40 : out.special(defs.contents());
1552 40 : out.put_symbol("end");
1553 40 : if (ncopies != 1)
1554 0 : out.put_literal_symbol("#copies")
1555 0 : .put_number(ncopies)
1556 0 : .put_symbol("def");
1557 40 : out.put_literal_symbol("RES")
1558 40 : .put_number(res)
1559 40 : .put_symbol("def");
1560 40 : out.put_literal_symbol("PL");
1561 40 : if (guess_flag)
1562 0 : out.put_symbol("PLG");
1563 : else
1564 40 : out.put_fix_number(paper_length);
1565 40 : out.put_symbol("def");
1566 40 : out.put_literal_symbol("LS")
1567 40 : .put_symbol(landscape_flag ? "true" : "false")
1568 40 : .put_symbol("def");
1569 40 : if (manual_feed_flag) {
1570 0 : out.begin_comment("BeginFeature:")
1571 0 : .comment_arg("*ManualFeed")
1572 0 : .comment_arg("True")
1573 0 : .end_comment()
1574 0 : .put_symbol("MANUAL")
1575 0 : .simple_comment("EndFeature");
1576 : }
1577 40 : encode_fonts();
1578 108 : while (subencodings) {
1579 68 : subencoding *tem = subencodings;
1580 68 : subencodings = subencodings->next;
1581 68 : encode_subfont(tem);
1582 68 : out.put_literal_symbol(tem->subfont)
1583 68 : .put_symbol(make_subencoding_name(tem->idx))
1584 68 : .put_literal_symbol(tem->p->get_internal_name())
1585 68 : .put_symbol("RE");
1586 68 : delete tem;
1587 : }
1588 40 : out.simple_comment((broken_flags & NO_SETUP_SECTION)
1589 : ? "EndProlog"
1590 40 : : "EndSetup");
1591 40 : out.end_line();
1592 40 : out.copy_file(tempfp);
1593 40 : fclose(tempfp);
1594 80 : }
1595 :
1596 : typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *);
1597 :
1598 : struct proc_table_t {
1599 : const char *name;
1600 : SPECIAL_PROCP proc;
1601 : };
1602 :
1603 60445 : void ps_printer::special(char *arg, const environment *env, char type)
1604 : {
1605 60445 : if (type != 'p')
1606 0 : return;
1607 : static const proc_table_t proc_table[] = {
1608 : { "exec", &ps_printer::do_exec },
1609 : { "def", &ps_printer::do_def },
1610 : { "mdef", &ps_printer::do_mdef },
1611 : { "import", &ps_printer::do_import },
1612 : { "file", &ps_printer::do_file },
1613 : { "invis", &ps_printer::do_invis },
1614 : { "endinvis", &ps_printer::do_endinvis },
1615 : };
1616 : char *p;
1617 60445 : for (p = arg; *p == ' ' || *p == '\n'; p++)
1618 : ;
1619 60445 : char *tag = p;
1620 181335 : for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
1621 : ;
1622 60445 : if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) {
1623 0 : error_with_file_and_line(current_filename, (current_lineno - 1),
1624 : "X command without 'ps:' tag ignored");
1625 0 : return;
1626 : }
1627 60445 : p++;
1628 120890 : for (; *p == ' ' || *p == '\n'; p++)
1629 : ;
1630 60445 : char *command = p;
1631 302246 : for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1632 : ;
1633 60445 : if (*command == '\0') {
1634 0 : error("empty X command ignored");
1635 0 : return;
1636 : }
1637 60487 : for (size_t i = 0; i < countof(proc_table); i++)
1638 60487 : if (strncmp(command, proc_table[i].name, p - command) == 0) {
1639 60445 : flush_sbuf();
1640 60445 : if (sbuf_color != *env->col)
1641 32105 : set_color(env->col);
1642 60445 : (this->*(proc_table[i].proc))(p, env);
1643 60445 : return;
1644 : }
1645 0 : error("X command '%1' not recognised", command);
1646 : }
1647 :
1648 : // A conforming PostScript document must not have lines longer
1649 : // than 255 characters (excluding line termination characters).
1650 :
1651 60436 : static int check_line_lengths(const char *p)
1652 : {
1653 : for (;;) {
1654 60436 : const char *end = strchr(p, '\n');
1655 60436 : if (end == 0)
1656 60436 : end = strchr(p, '\0');
1657 60436 : if (end - p > 255)
1658 0 : return 0;
1659 60436 : if (*end == '\0')
1660 60436 : break;
1661 0 : p = end + 1;
1662 0 : }
1663 60436 : return 1;
1664 : }
1665 :
1666 120872 : void ps_printer::do_exec(char *arg, const environment *env)
1667 : {
1668 120872 : while (csspace(*arg))
1669 60436 : arg++;
1670 60436 : if (*arg == '\0') {
1671 0 : error("missing argument to X exec command");
1672 0 : return;
1673 : }
1674 60436 : if (!check_line_lengths(arg))
1675 0 : warning("lines in X exec command should"
1676 : " not be more than 255 characters long");
1677 60436 : out.put_fix_number(env->hpos)
1678 60436 : .put_fix_number(env->vpos)
1679 60436 : .put_symbol("EBEGIN")
1680 60436 : .special(arg)
1681 60436 : .put_symbol("EEND");
1682 60436 : output_hpos = output_vpos = -1;
1683 60436 : output_style.f = 0;
1684 60436 : output_draw_point_size = -1;
1685 60436 : output_line_thickness = -1;
1686 60436 : ndefined_styles = 0;
1687 60436 : if (!ndefs)
1688 10 : ndefs = 1;
1689 : }
1690 :
1691 0 : void ps_printer::do_file(char *arg, const environment *env)
1692 : {
1693 0 : while (csspace(*arg))
1694 0 : arg++;
1695 0 : if (*arg == '\0') {
1696 0 : error("missing argument to X file command");
1697 0 : return;
1698 : }
1699 0 : const char *resource_filename = arg;
1700 0 : do {
1701 0 : ++arg;
1702 0 : } while (*arg != '\0' && *arg != ' ' && *arg != '\n');
1703 0 : out.put_fix_number(env->hpos)
1704 0 : .put_fix_number(env->vpos)
1705 0 : .put_symbol("EBEGIN");
1706 0 : rm.import_file(resource_filename, out);
1707 0 : out.put_symbol("EEND");
1708 0 : output_hpos = output_vpos = -1;
1709 0 : output_style.f = 0;
1710 0 : output_draw_point_size = -1;
1711 0 : output_line_thickness = -1;
1712 0 : ndefined_styles = 0;
1713 0 : if (!ndefs)
1714 0 : ndefs = 1;
1715 : }
1716 :
1717 0 : void ps_printer::do_def(char *arg, const environment *)
1718 : {
1719 0 : while (csspace(*arg))
1720 0 : arg++;
1721 0 : if (!check_line_lengths(arg))
1722 0 : warning("lines in X def command should"
1723 : " not be more than 255 characters long");
1724 0 : defs += arg;
1725 0 : if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1726 0 : defs += '\n';
1727 0 : ndefs++;
1728 0 : }
1729 :
1730 : // Like def, but the first argument says how many definitions it contains.
1731 :
1732 0 : void ps_printer::do_mdef(char *arg, const environment *)
1733 : {
1734 : char *p;
1735 0 : int n = (int)strtol(arg, &p, 10);
1736 0 : if (p == arg) {
1737 0 : error("first argument to X mdef must be an integer");
1738 0 : return;
1739 : }
1740 0 : if (n < 0) {
1741 0 : error("out of range argument '%1' to X mdef command", int(n));
1742 0 : return;
1743 : }
1744 0 : arg = p;
1745 0 : while (csspace(*arg))
1746 0 : arg++;
1747 0 : if (!check_line_lengths(arg))
1748 0 : warning("lines in X mdef command should"
1749 : " not be more than 255 characters long");
1750 0 : defs += arg;
1751 0 : if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1752 0 : defs += '\n';
1753 0 : ndefs += n;
1754 : }
1755 :
1756 6 : void ps_printer::do_import(char *arg, const environment *env)
1757 : {
1758 6 : while (*arg == ' ' || *arg == '\n')
1759 3 : arg++;
1760 : char *p;
1761 24 : for (p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1762 : ;
1763 3 : if (*p != '\0')
1764 3 : *p++ = '\0';
1765 : int parms[6];
1766 3 : int nparms = 0;
1767 20 : while (nparms < 6) {
1768 : char *end;
1769 18 : long n = strtol(p, &end, 10);
1770 18 : if (end == p)
1771 1 : break;
1772 17 : parms[nparms++] = int(n);
1773 17 : p = end;
1774 : }
1775 3 : if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
1776 0 : error("scaling units not allowed in arguments for X import command");
1777 0 : return;
1778 : }
1779 4 : while (*p == ' ' || *p == '\n')
1780 1 : p++;
1781 3 : if (nparms < 5) {
1782 0 : if (*p == '\0')
1783 0 : error("too few arguments for X import command");
1784 : else
1785 0 : error("invalid argument '%1' for X import command", p);
1786 0 : return;
1787 : }
1788 3 : if (*p != '\0') {
1789 0 : error("superfluous argument '%1' for X import command", p);
1790 0 : return;
1791 : }
1792 3 : int llx = parms[0];
1793 3 : int lly = parms[1];
1794 3 : int urx = parms[2];
1795 3 : int ury = parms[3];
1796 3 : int desired_width = parms[4];
1797 3 : int desired_height = parms[5];
1798 3 : if (desired_width <= 0) {
1799 0 : error("bad width argument '%1' for X import command: must be > 0",
1800 0 : desired_width);
1801 0 : return;
1802 : }
1803 3 : if (nparms == 6 && desired_height <= 0) {
1804 0 : error("bad height argument '%1' for X import command: must be > 0",
1805 0 : desired_height);
1806 0 : return;
1807 : }
1808 3 : if (llx == urx) {
1809 0 : error("llx and urx arguments for X import command must not be equal");
1810 0 : return;
1811 : }
1812 3 : if (lly == ury) {
1813 0 : error("lly and ury arguments for X import command must not be equal");
1814 0 : return;
1815 : }
1816 3 : if (nparms == 5) {
1817 1 : int old_wid = urx - llx;
1818 1 : int old_ht = ury - lly;
1819 1 : if (old_wid < 0)
1820 0 : old_wid = -old_wid;
1821 1 : if (old_ht < 0)
1822 0 : old_ht = -old_ht;
1823 1 : desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5);
1824 : }
1825 3 : if (env->vpos - desired_height < 0)
1826 0 : warning("top of imported graphic is above the top of the page");
1827 3 : out.put_number(llx)
1828 3 : .put_number(lly)
1829 3 : .put_fix_number(desired_width)
1830 3 : .put_number(urx - llx)
1831 3 : .put_fix_number(-desired_height)
1832 3 : .put_number(ury - lly)
1833 3 : .put_fix_number(env->hpos)
1834 3 : .put_fix_number(env->vpos)
1835 3 : .put_symbol("PBEGIN");
1836 3 : rm.import_file(arg, out);
1837 : // do this here just in case application defines PEND
1838 3 : out.put_symbol("end")
1839 3 : .put_symbol("PEND");
1840 : }
1841 :
1842 3 : void ps_printer::do_invis(char *, const environment *)
1843 : {
1844 3 : invis_count++;
1845 3 : }
1846 :
1847 3 : void ps_printer::do_endinvis(char *, const environment *)
1848 : {
1849 3 : if (invis_count == 0)
1850 0 : error("unbalanced 'endinvis' command");
1851 : else
1852 3 : --invis_count;
1853 3 : }
1854 :
1855 40 : printer *make_printer()
1856 : {
1857 40 : return new ps_printer(user_paper_length);
1858 : }
1859 :
1860 : static void usage(FILE *stream);
1861 :
1862 60 : int main(int argc, char **argv)
1863 : {
1864 60 : setlocale(LC_NUMERIC, "C");
1865 60 : program_name = argv[0];
1866 60 : string env;
1867 : static char stderr_buf[BUFSIZ];
1868 60 : setbuf(stderr, stderr_buf);
1869 : int c;
1870 : static const struct option long_options[] = {
1871 : { "help", no_argument, 0 /* nullptr */, CHAR_MAX + 1 },
1872 : { "version", no_argument, 0 /* nullptr */, 'v' },
1873 : { 0 /* nullptr */, 0, 0 /* nullptr */, 0 }
1874 : };
1875 102 : while ((c = getopt_long(argc, argv, ":b:c:F:gI:lmp:P:vw:",
1876 : long_options, 0 /* nullptr */))
1877 102 : != EOF)
1878 42 : switch (c) {
1879 0 : case 'b':
1880 : // XXX check this
1881 0 : broken_flags = atoi(optarg);
1882 0 : bflag = 1;
1883 0 : break;
1884 0 : case 'c':
1885 0 : if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
1886 0 : error("expected positive integer argument to '-c' option, got"
1887 0 : " '%1'; ignoring", optarg);
1888 0 : ncopies = 1;
1889 : }
1890 0 : break;
1891 0 : case 'F':
1892 0 : font::command_line_font_dir(optarg);
1893 0 : break;
1894 0 : case 'g':
1895 0 : guess_flag = 1;
1896 0 : break;
1897 25 : case 'I':
1898 25 : include_search_path.command_line_dir(optarg);
1899 25 : break;
1900 0 : case 'l':
1901 0 : landscape_flag = 1;
1902 0 : break;
1903 0 : case 'm':
1904 0 : manual_feed_flag = 1;
1905 0 : break;
1906 17 : case 'p':
1907 17 : if (!font::scan_papersize(optarg, 0,
1908 : &user_paper_length, &user_paper_width))
1909 0 : error("ignoring invalid custom paper format '%1'", optarg);
1910 17 : break;
1911 0 : case 'P':
1912 0 : env = "GROPS_PROLOGUE";
1913 0 : env += '=';
1914 0 : env += optarg;
1915 0 : env += '\0';
1916 0 : if (putenv(strsave(env.contents())) != 0)
1917 0 : fatal("cannot update process environment: %1", strerror(errno));
1918 0 : break;
1919 0 : case 'v':
1920 0 : printf("GNU grops (groff) version %s\n", Version_string);
1921 0 : exit(EXIT_SUCCESS);
1922 : break;
1923 0 : case 'w':
1924 0 : if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
1925 0 : error("invalid line width '%1' ignored", optarg);
1926 0 : linewidth = -1;
1927 : }
1928 0 : break;
1929 0 : case CHAR_MAX + 1: // --help
1930 0 : usage(stdout);
1931 0 : exit(EXIT_SUCCESS);
1932 : break;
1933 0 : case '?':
1934 0 : if (optopt != 0)
1935 0 : error("unrecognized command-line option '%1'", char(optopt));
1936 : else
1937 0 : error("unrecognized command-line option '%1'",
1938 0 : argv[(optind - 1)]);
1939 0 : usage(stderr);
1940 0 : exit(2);
1941 : break;
1942 0 : case ':':
1943 0 : error("command-line option '%1' requires an argument",
1944 0 : char(optopt));
1945 0 : usage(stderr);
1946 0 : exit(2);
1947 : break;
1948 0 : default:
1949 0 : assert(0 == "unhandled getopt_long return value");
1950 : }
1951 60 : font::set_unknown_desc_command_handler(handle_unknown_desc_command);
1952 : SET_BINARY(fileno(stdout));
1953 60 : if (optind >= argc)
1954 60 : do_file("-");
1955 : else {
1956 0 : for (int i = optind; i < argc; i++)
1957 0 : do_file(argv[i]);
1958 : }
1959 60 : return 0;
1960 : }
1961 :
1962 0 : static void usage(FILE *stream)
1963 : {
1964 0 : fprintf(stream,
1965 : "usage: %s [-glm] [-b brokenness-flags] [-c num-copies]"
1966 : " [-F font-directory] [-I inclusion-directory] [-p paper-format]"
1967 : " [-P prologue-file] [-w rule-thickness] [file ...]\n"
1968 : "usage: %s {-v | --version}\n"
1969 : "usage: %s --help\n",
1970 : program_name, program_name, program_name);
1971 0 : if (stdout == stream)
1972 0 : fputs(
1973 : "\n"
1974 : "Translate the output of troff(1) into PostScript. See the grops(1)\n"
1975 : "manual page.\n",
1976 : stream);
1977 0 : }
1978 :
1979 : // Local Variables:
1980 : // fill-column: 72
1981 : // mode: C++
1982 : // End:
1983 : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:
|