1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
4
5 This file is part of groff.
6
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
20
21 #include "driver.h"
22 #include "stringclass.h"
23 #include "cset.h"
24
25 #include "ps.h"
26
27 static int landscape_flag = 0;
28 static int ncopies = 1;
29 static int linewidth = -1;
30 // Non-zero means generate PostScript code that guesses the paper
31 // length using the imageable area.
32 static int guess_flag = 0;
33
34 // Non-zero if -b was specified on the command line.
35 static int bflag = 0;
36 unsigned broken_flags = 0;
37
38 #define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
39 #define FILL_MAX 1000
40
41 const char *const dict_name = "grops";
42 const char *const defs_dict_name = "DEFS";
43 const int DEFS_DICT_SPARE = 50;
44
degrees(double r)45 double degrees(double r)
46 {
47 return r*180.0/M_PI;
48 }
49
radians(double d)50 double radians(double d)
51 {
52 return d*M_PI/180.0;
53 }
54
transform_fill(int fill)55 inline double transform_fill(int fill)
56 {
57 return 1 - fill/double(FILL_MAX);
58 }
59
ps_output(FILE * f,int n)60 ps_output::ps_output(FILE *f, int n)
61 : fp(f), max_line_length(n), col(0), need_space(0), fixed_point(0)
62 {
63 }
64
set_file(FILE * f)65 ps_output &ps_output::set_file(FILE *f)
66 {
67 fp = f;
68 col = 0;
69 return *this;
70 }
71
copy_file(FILE * infp)72 ps_output &ps_output::copy_file(FILE *infp)
73 {
74 int c;
75 while ((c = getc(infp)) != EOF)
76 putc(c, fp);
77 return *this;
78 }
79
end_line()80 ps_output &ps_output::end_line()
81 {
82 if (col != 0) {
83 putc('\n', fp);
84 col = 0;
85 need_space = 0;
86 }
87 return *this;
88 }
89
special(const char * s)90 ps_output &ps_output::special(const char *s)
91 {
92 if (s == 0 || *s == '\0')
93 return *this;
94 if (col != 0) {
95 putc('\n', fp);
96 col = 0;
97 }
98 fputs(s, fp);
99 if (strchr(s, '\0')[-1] != '\n')
100 putc('\n', fp);
101 need_space = 0;
102 return *this;
103 }
104
simple_comment(const char * s)105 ps_output &ps_output::simple_comment(const char *s)
106 {
107 if (col != 0)
108 putc('\n', fp);
109 putc('%', fp);
110 putc('%', fp);
111 fputs(s, fp);
112 putc('\n', fp);
113 col = 0;
114 need_space = 0;
115 return *this;
116 }
117
begin_comment(const char * s)118 ps_output &ps_output::begin_comment(const char *s)
119 {
120 if (col != 0)
121 putc('\n', fp);
122 putc('%', fp);
123 putc('%', fp);
124 fputs(s, fp);
125 col = 2 + strlen(s);
126 return *this;
127 }
128
end_comment()129 ps_output &ps_output::end_comment()
130 {
131 if (col != 0) {
132 putc('\n', fp);
133 col = 0;
134 }
135 need_space = 0;
136 return *this;
137 }
138
comment_arg(const char * s)139 ps_output &ps_output::comment_arg(const char *s)
140 {
141 int len = strlen(s);
142 if (col + len + 1 > max_line_length) {
143 putc('\n', fp);
144 fputs("%%+", fp);
145 col = 3;
146 }
147 putc(' ', fp);
148 fputs(s, fp);
149 col += len + 1;
150 return *this;
151 }
152
set_fixed_point(int n)153 ps_output &ps_output::set_fixed_point(int n)
154 {
155 assert(n >= 0 && n <= 10);
156 fixed_point = n;
157 return *this;
158 }
159
put_delimiter(char c)160 ps_output &ps_output::put_delimiter(char c)
161 {
162 if (col + 1 > max_line_length) {
163 putc('\n', fp);
164 col = 0;
165 }
166 putc(c, fp);
167 col++;
168 need_space = 0;
169 return *this;
170 }
171
put_string(const char * s,int n)172 ps_output &ps_output::put_string(const char *s, int n)
173 {
174 int len = 0;
175 for (int i = 0; i < n; i++) {
176 char c = s[i];
177 if (isascii(c) && isprint(c)) {
178 if (c == '(' || c == ')' || c == '\\')
179 len += 2;
180 else
181 len += 1;
182 }
183 else
184 len += 4;
185 }
186 if (len > n*2) {
187 if (col + n*2 + 2 > max_line_length && n*2 + 2 <= max_line_length) {
188 putc('\n', fp);
189 col = 0;
190 }
191 if (col + 1 > max_line_length) {
192 putc('\n', fp);
193 col = 0;
194 }
195 putc('<', fp);
196 col++;
197 for (i = 0; i < n; i++) {
198 if (col + 2 > max_line_length) {
199 putc('\n', fp);
200 col = 0;
201 }
202 fprintf(fp, "%02x", s[i] & 0377);
203 col += 2;
204 }
205 putc('>', fp);
206 col++;
207 }
208 else {
209 if (col + len + 2 > max_line_length && len + 2 <= max_line_length) {
210 putc('\n', fp);
211 col = 0;
212 }
213 if (col + 2 > max_line_length) {
214 putc('\n', fp);
215 col = 0;
216 }
217 putc('(', fp);
218 col++;
219 for (i = 0; i < n; i++) {
220 char c = s[i];
221 if (isascii(c) && isprint(c)) {
222 if (c == '(' || c == ')' || c == '\\')
223 len = 2;
224 else
225 len = 1;
226 }
227 else
228 len = 4;
229 if (col + len + 1 > max_line_length) {
230 putc('\\', fp);
231 putc('\n', fp);
232 col = 0;
233 }
234 switch (len) {
235 case 1:
236 putc(c, fp);
237 break;
238 case 2:
239 putc('\\', fp);
240 putc(c, fp);
241 break;
242 case 4:
243 fprintf(fp, "\\%03o", c & 0377);
244 break;
245 default:
246 assert(0);
247 }
248 col += len;
249 }
250 putc(')', fp);
251 col++;
252 }
253 need_space = 0;
254 return *this;
255 }
256
put_number(int n)257 ps_output &ps_output::put_number(int n)
258 {
259 char buf[1 + INT_DIGITS + 1];
260 sprintf(buf, "%d", n);
261 int len = strlen(buf);
262 if (col > 0 && col + len + need_space > max_line_length) {
263 putc('\n', fp);
264 col = 0;
265 need_space = 0;
266 }
267 if (need_space) {
268 putc(' ', fp);
269 col++;
270 }
271 fputs(buf, fp);
272 col += len;
273 need_space = 1;
274 return *this;
275 }
276
put_fix_number(int i)277 ps_output &ps_output::put_fix_number(int i)
278 {
279 const char *p = iftoa(i, fixed_point);
280 int len = strlen(p);
281 if (col > 0 && col + len + need_space > max_line_length) {
282 putc('\n', fp);
283 col = 0;
284 need_space = 0;
285 }
286 if (need_space) {
287 putc(' ', fp);
288 col++;
289 }
290 fputs(p, fp);
291 col += len;
292 need_space = 1;
293 return *this;
294 }
295
put_float(double d)296 ps_output &ps_output::put_float(double d)
297 {
298 char buf[128];
299 sprintf(buf, "%.4f", d);
300 int len = strlen(buf);
301 if (col > 0 && col + len + need_space > max_line_length) {
302 putc('\n', fp);
303 col = 0;
304 need_space = 0;
305 }
306 if (need_space) {
307 putc(' ', fp);
308 col++;
309 }
310 fputs(buf, fp);
311 col += len;
312 need_space = 1;
313 return *this;
314 }
315
put_symbol(const char * s)316 ps_output &ps_output::put_symbol(const char *s)
317 {
318 int len = strlen(s);
319 if (col > 0 && col + len + need_space > max_line_length) {
320 putc('\n', fp);
321 col = 0;
322 need_space = 0;
323 }
324 if (need_space) {
325 putc(' ', fp);
326 col++;
327 }
328 fputs(s, fp);
329 col += len;
330 need_space = 1;
331 return *this;
332 }
333
put_literal_symbol(const char * s)334 ps_output &ps_output::put_literal_symbol(const char *s)
335 {
336 int len = strlen(s);
337 if (col > 0 && col + len + 1 > max_line_length) {
338 putc('\n', fp);
339 col = 0;
340 }
341 putc('/', fp);
342 fputs(s, fp);
343 col += len + 1;
344 need_space = 1;
345 return *this;
346 }
347
348 class ps_font : public font {
349 ps_font(const char *);
350 public:
351 int encoding_index;
352 char *encoding;
353 char *reencoded_name;
354 ~ps_font();
355 void handle_unknown_font_command(const char *command, const char *arg,
356 const char *filename, int lineno);
357 static ps_font *load_ps_font(const char *);
358 };
359
load_ps_font(const char * s)360 ps_font *ps_font::load_ps_font(const char *s)
361 {
362 ps_font *f = new ps_font(s);
363 if (!f->load()) {
364 delete f;
365 return 0;
366 }
367 return f;
368 }
369
ps_font(const char * nm)370 ps_font::ps_font(const char *nm)
371 : font(nm), encoding(0), reencoded_name(0), encoding_index(-1)
372 {
373 }
374
~ps_font()375 ps_font::~ps_font()
376 {
377 a_delete encoding;
378 a_delete reencoded_name;
379 }
380
handle_unknown_font_command(const char * command,const char * arg,const char * filename,int lineno)381 void ps_font::handle_unknown_font_command(const char *command, const char *arg,
382 const char *filename, int lineno)
383 {
384 if (strcmp(command, "encoding") == 0) {
385 if (arg == 0)
386 error_with_file_and_line(filename, lineno,
387 "`encoding' command requires an argument");
388 else
389 encoding = strsave(arg);
390 }
391 }
392
handle_unknown_desc_command(const char * command,const char * arg,const char * filename,int lineno)393 static void handle_unknown_desc_command(const char *command, const char *arg,
394 const char *filename, int lineno)
395 {
396 if (strcmp(command, "broken") == 0) {
397 if (arg == 0)
398 error_with_file_and_line(filename, lineno,
399 "`broken' command requires an argument");
400 else if (!bflag)
401 broken_flags = atoi(arg);
402 }
403 }
404
405 struct style {
406 font *f;
407 int point_size;
408 int height;
409 int slant;
410 style();
411 style(font *, int, int, int);
412 int operator==(const style &) const;
413 int operator!=(const style &) const;
414 };
415
style()416 style::style() : f(0)
417 {
418 }
419
style(font * p,int sz,int h,int sl)420 style::style(font *p, int sz, int h, int sl)
421 : f(p), point_size(sz), height(h), slant(sl)
422 {
423 }
424
operator ==(const style & s) const425 int style::operator==(const style &s) const
426 {
427 return (f == s.f && point_size == s.point_size
428 && height == s.height && slant == s.slant);
429 }
430
operator !=(const style & s) const431 int style::operator!=(const style &s) const
432 {
433 return !(*this == s);
434 }
435
436 class ps_printer : public printer {
437 FILE *tempfp;
438 ps_output out;
439 int res;
440 int space_char_index;
441 int pages_output;
442 int paper_length;
443 int equalise_spaces;
444 enum { SBUF_SIZE = 256 };
445 char sbuf[SBUF_SIZE];
446 int sbuf_len;
447 int sbuf_start_hpos;
448 int sbuf_vpos;
449 int sbuf_end_hpos;
450 int sbuf_space_width;
451 int sbuf_space_count;
452 int sbuf_space_diff_count;
453 int sbuf_space_code;
454 int sbuf_kern;
455 style sbuf_style;
456 style output_style;
457 int output_hpos;
458 int output_vpos;
459 int output_draw_point_size;
460 int line_thickness;
461 int output_line_thickness;
462 int fill;
463 unsigned char output_space_code;
464 enum { MAX_DEFINED_STYLES = 50 };
465 style defined_styles[MAX_DEFINED_STYLES];
466 int ndefined_styles;
467 int next_encoding_index;
468 string defs;
469 int ndefs;
470 resource_manager rm;
471 int invis_count;
472
473 void flush_sbuf();
474 void set_style(const style &);
475 void set_space_code(unsigned char c);
476 int set_encoding_index(ps_font *);
477 void do_exec(char *, const environment *);
478 void do_import(char *, const environment *);
479 void do_def(char *, const environment *);
480 void do_mdef(char *, const environment *);
481 void do_file(char *, const environment *);
482 void do_invis(char *, const environment *);
483 void do_endinvis(char *, const environment *);
484 void set_line_thickness(const environment *);
485 void fill_path();
486 void encode_fonts();
487 void define_encoding(const char *, int);
488 void reencode_font(ps_font *);
489 public:
490 ps_printer();
491 ~ps_printer();
492 void set_char(int i, font *f, const environment *env, int w);
493 void draw(int code, int *p, int np, const environment *env);
494 void begin_page(int);
495 void end_page(int);
496 void special(char *arg, const environment *env);
497 font *make_font(const char *);
498 void end_of_line();
499 };
500
ps_printer()501 ps_printer::ps_printer()
502 : pages_output(0),
503 sbuf_len(0),
504 output_hpos(-1),
505 output_vpos(-1),
506 out(0, 79),
507 ndefined_styles(0),
508 next_encoding_index(0),
509 line_thickness(-1),
510 fill(FILL_MAX + 1),
511 ndefs(0),
512 invis_count(0)
513 {
514 tempfp = xtmpfile();
515 out.set_file(tempfp);
516 if (linewidth < 0)
517 linewidth = DEFAULT_LINEWIDTH;
518 if (font::hor != 1)
519 fatal("horizontal resolution must be 1");
520 if (font::vert != 1)
521 fatal("vertical resolution must be 1");
522 if (font::res % (font::sizescale*72) != 0)
523 fatal("res must be a multiple of 72*sizescale");
524 int r = font::res;
525 int point = 0;
526 while (r % 10 == 0) {
527 r /= 10;
528 point++;
529 }
530 res = r;
531 out.set_fixed_point(point);
532 space_char_index = font::name_to_index("space");
533 paper_length = font::paperlength;
534 if (paper_length == 0)
535 paper_length = 11*font::res;
536 equalise_spaces = font::res >= 72000;
537 }
538
set_encoding_index(ps_font * f)539 int ps_printer::set_encoding_index(ps_font *f)
540 {
541 if (f->encoding_index >= 0)
542 return f->encoding_index;
543 for (font_pointer_list *p = font_list; p; p = p->next)
544 if (p->p != f) {
545 char *encoding = ((ps_font *)p->p)->encoding;
546 int encoding_index = ((ps_font *)p->p)->encoding_index;
547 if (encoding != 0 && encoding_index >= 0
548 && strcmp(f->encoding, encoding) == 0) {
549 return f->encoding_index = encoding_index;
550 }
551 }
552 return f->encoding_index = next_encoding_index++;
553 }
554
set_char(int i,font * f,const environment * env,int w)555 void ps_printer::set_char(int i, font *f, const environment *env, int w)
556 {
557 if (i == space_char_index || invis_count > 0)
558 return;
559 unsigned char code = f->get_code(i);
560 style sty(f, env->size, env->height, env->slant);
561 if (sty.slant != 0) {
562 if (sty.slant > 80 || sty.slant < -80) {
563 error("silly slant `%1' degrees", sty.slant);
564 sty.slant = 0;
565 }
566 }
567 if (sbuf_len > 0) {
568 if (sbuf_len < SBUF_SIZE
569 && sty == sbuf_style
570 && sbuf_vpos == env->vpos) {
571 if (sbuf_end_hpos == env->hpos) {
572 sbuf[sbuf_len++] = code;
573 sbuf_end_hpos += w + sbuf_kern;
574 return;
575 }
576 if (sbuf_len == 1 && sbuf_kern == 0) {
577 sbuf_kern = env->hpos - sbuf_end_hpos;
578 sbuf_end_hpos = env->hpos + sbuf_kern + w;
579 sbuf[sbuf_len++] = code;
580 return;
581 }
582 /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
583 starting a new string. */
584 if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
585 && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
586 if (sbuf_space_code < 0) {
587 if (f->contains(space_char_index)) {
588 sbuf_space_code = f->get_code(space_char_index);
589 sbuf_space_width = env->hpos - sbuf_end_hpos;
590 sbuf_end_hpos = env->hpos + w + sbuf_kern;
591 sbuf[sbuf_len++] = sbuf_space_code;
592 sbuf[sbuf_len++] = code;
593 sbuf_space_count++;
594 return;
595 }
596 }
597 else {
598 int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
599 if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) {
600 sbuf_end_hpos = env->hpos + w + sbuf_kern;
601 sbuf[sbuf_len++] = sbuf_space_code;
602 sbuf[sbuf_len++] = code;
603 sbuf_space_count++;
604 if (diff == 1)
605 sbuf_space_diff_count++;
606 else if (diff == -1)
607 sbuf_space_diff_count--;
608 return;
609 }
610 }
611 }
612 }
613 flush_sbuf();
614 }
615 sbuf_len = 1;
616 sbuf[0] = code;
617 sbuf_end_hpos = env->hpos + w;
618 sbuf_start_hpos = env->hpos;
619 sbuf_vpos = env->vpos;
620 sbuf_style = sty;
621 sbuf_space_code = -1;
622 sbuf_space_width = 0;
623 sbuf_space_count = sbuf_space_diff_count = 0;
624 sbuf_kern = 0;
625 }
626
is_small_h(int n)627 int is_small_h(int n)
628 {
629 return n < (font::res*2)/72 && n > -(font::res*10)/72;
630 }
631
is_small_v(int n)632 int is_small_v(int n)
633 {
634 return n < (font::res*4)/72 && n > -(font::res*4)/72;
635 }
636
make_encoding_name(int encoding_index)637 static char *make_encoding_name(int encoding_index)
638 {
639 static char buf[3 + INT_DIGITS + 1];
640 sprintf(buf, "ENC%d", encoding_index);
641 return buf;
642 }
643
644 const char *const WS = " \t\n\r";
645
define_encoding(const char * encoding,int encoding_index)646 void ps_printer::define_encoding(const char *encoding, int encoding_index)
647 {
648 char *vec[256];
649 for (int i = 0; i < 256; i++)
650 vec[i] = 0;
651 char *path;
652 FILE *fp = font::open_file(encoding, &path);
653 if (fp == 0)
654 fatal("can't open encoding file `%1'", encoding);
655 int lineno = 1;
656 char buf[256];
657 while (fgets(buf, 512, fp) != 0) {
658 char *p = buf;
659 while (isascii(*p) && isspace(*p))
660 p++;
661 if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) {
662 char *q = strtok(0, WS);
663 int n;
664 if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256)
665 fatal_with_file_and_line(path, lineno, "bad second field");
666 vec[n] = new char[strlen(p) + 1];
667 strcpy(vec[n], p);
668 }
669 lineno++;
670 }
671 a_delete path;
672 out.put_literal_symbol(make_encoding_name(encoding_index));
673 out.put_delimiter('[');
674 for (i = 0; i < 256; i++) {
675 if (vec[i] == 0)
676 out.put_literal_symbol(".notdef");
677 else {
678 out.put_literal_symbol(vec[i]);
679 a_delete vec[i];
680 }
681 }
682 out.put_delimiter(']').put_symbol("def");
683 }
684
reencode_font(ps_font * f)685 void ps_printer::reencode_font(ps_font *f)
686 {
687 out.put_literal_symbol(f->reencoded_name)
688 .put_symbol(make_encoding_name(f->encoding_index))
689 .put_literal_symbol(f->get_internal_name())
690 .put_symbol("RE");
691 }
692
encode_fonts()693 void ps_printer::encode_fonts()
694 {
695 if (next_encoding_index == 0)
696 return;
697 char *done_encoding = new char[next_encoding_index];
698 for (int i = 0; i < next_encoding_index; i++)
699 done_encoding[i] = 0;
700 for (font_pointer_list *f = font_list; f; f = f->next) {
701 int encoding_index = ((ps_font *)f->p)->encoding_index;
702 if (encoding_index >= 0) {
703 assert(encoding_index < next_encoding_index);
704 if (!done_encoding[encoding_index]) {
705 done_encoding[encoding_index] = 1;
706 define_encoding(((ps_font *)f->p)->encoding, encoding_index);
707 }
708 reencode_font((ps_font *)f->p);
709 }
710 }
711 a_delete done_encoding;
712 }
713
set_style(const style & sty)714 void ps_printer::set_style(const style &sty)
715 {
716 char buf[1 + INT_DIGITS + 1];
717 for (int i = 0; i < ndefined_styles; i++)
718 if (sty == defined_styles[i]) {
719 sprintf(buf, "F%d", i);
720 out.put_symbol(buf);
721 return;
722 }
723 if (ndefined_styles >= MAX_DEFINED_STYLES)
724 ndefined_styles = 0;
725 sprintf(buf, "F%d", ndefined_styles);
726 out.put_literal_symbol(buf);
727 const char *psname = sty.f->get_internal_name();
728 if (psname == 0)
729 fatal("no internalname specified for font `%1'", sty.f->get_name());
730 char *encoding = ((ps_font *)sty.f)->encoding;
731 if (encoding != 0) {
732 char *s = ((ps_font *)sty.f)->reencoded_name;
733 if (s == 0) {
734 int ei = set_encoding_index((ps_font *)sty.f);
735 char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1];
736 sprintf(tem, "%s@%d", psname, ei);
737 psname = tem;
738 ((ps_font *)sty.f)->reencoded_name = tem;
739 }
740 else
741 psname = s;
742 }
743 out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size);
744 if (sty.height != 0 || sty.slant != 0) {
745 int h = sty.height == 0 ? sty.point_size : sty.height;
746 h *= font::res/(72*font::sizescale);
747 int c = int(h*tan(radians(sty.slant)) + .5);
748 out.put_fix_number(c).put_fix_number(h).put_literal_symbol(psname)
749 .put_symbol("MF");
750 }
751 else {
752 out.put_literal_symbol(psname).put_symbol("SF");
753 }
754 defined_styles[ndefined_styles++] = sty;
755 }
756
set_space_code(unsigned char c)757 void ps_printer::set_space_code(unsigned char c)
758 {
759 out.put_literal_symbol("SC").put_number(c).put_symbol("def");
760 }
761
end_of_line()762 void ps_printer::end_of_line()
763 {
764 flush_sbuf();
765 // this ensures that we do an absolute motion to the beginning of a line
766 output_vpos = output_hpos = -1;
767 }
768
flush_sbuf()769 void ps_printer::flush_sbuf()
770 {
771 enum {
772 NONE,
773 RELATIVE_H,
774 RELATIVE_V,
775 RELATIVE_HV,
776 ABSOLUTE
777 } motion = NONE;
778 int space_flag = 0;
779 if (sbuf_len == 0)
780 return;
781 if (output_style != sbuf_style) {
782 set_style(sbuf_style);
783 output_style = sbuf_style;
784 }
785 int extra_space = 0;
786 if (output_hpos < 0 || output_vpos < 0
787 || !is_small_h(output_hpos - sbuf_start_hpos)
788 || !is_small_v(output_vpos - sbuf_vpos))
789 motion = ABSOLUTE;
790 else {
791 if (output_hpos != sbuf_start_hpos)
792 motion = RELATIVE_H;
793 if (output_vpos != sbuf_vpos) {
794 if (motion != NONE)
795 motion = RELATIVE_HV;
796 else
797 motion = RELATIVE_V;
798 }
799 }
800 if (sbuf_space_code >= 0) {
801 int w = sbuf_style.f->get_width(space_char_index, sbuf_style.point_size);
802 if (w + sbuf_kern != sbuf_space_width) {
803 if (sbuf_space_code != output_space_code) {
804 set_space_code(sbuf_space_code);
805 output_space_code = sbuf_space_code;
806 }
807 space_flag = 1;
808 extra_space = sbuf_space_width - w - sbuf_kern;
809 if (sbuf_space_diff_count > sbuf_space_count/2)
810 extra_space++;
811 else if (sbuf_space_diff_count < -(sbuf_space_count/2))
812 extra_space--;
813 }
814 }
815 if (space_flag)
816 out.put_fix_number(extra_space);
817 if (sbuf_kern != 0)
818 out.put_fix_number(sbuf_kern);
819 out.put_string(sbuf, sbuf_len);
820 char sym[2];
821 sym[0] = 'A' + motion*4 + space_flag + 2*(sbuf_kern != 0);
822 sym[1] = '\0';
823 switch (motion) {
824 case NONE:
825 break;
826 case ABSOLUTE:
827 out.put_fix_number(sbuf_start_hpos)
828 .put_fix_number(sbuf_vpos);
829 break;
830 case RELATIVE_H:
831 out.put_fix_number(sbuf_start_hpos - output_hpos);
832 break;
833 case RELATIVE_V:
834 out.put_fix_number(sbuf_vpos - output_vpos);
835 break;
836 case RELATIVE_HV:
837 out.put_fix_number(sbuf_start_hpos - output_hpos)
838 .put_fix_number(sbuf_vpos - output_vpos);
839 break;
840 default:
841 assert(0);
842 }
843 out.put_symbol(sym);
844 output_hpos = sbuf_end_hpos;
845 output_vpos = sbuf_vpos;
846 sbuf_len = 0;
847 }
848
849
set_line_thickness(const environment * env)850 void ps_printer::set_line_thickness(const environment *env)
851 {
852 if (line_thickness < 0) {
853 if (output_draw_point_size != env->size) {
854 // we ought to check for overflow here
855 int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
856 out.put_fix_number(lw).put_symbol("LW");
857 output_draw_point_size = env->size;
858 output_line_thickness = -1;
859 }
860 }
861 else {
862 if (output_line_thickness != line_thickness) {
863 out.put_fix_number(line_thickness).put_symbol("LW");
864 output_line_thickness = line_thickness;
865 output_draw_point_size = -1;
866 }
867 }
868 }
869
fill_path()870 void ps_printer::fill_path()
871 {
872 if (fill > FILL_MAX)
873 out.put_symbol("BL");
874 else
875 out.put_float(transform_fill(fill)).put_symbol("FL");
876 }
877
draw(int code,int * p,int np,const environment * env)878 void ps_printer::draw(int code, int *p, int np, const environment *env)
879 {
880 if (invis_count > 0)
881 return;
882 int fill_flag = 0;
883 switch (code) {
884 case 'C':
885 fill_flag = 1;
886 // fall through
887 case 'c':
888 // troff adds an extra argument to C
889 if (np != 1 && !(code == 'C' && np == 2)) {
890 error("1 argument required for circle");
891 break;
892 }
893 out.put_fix_number(env->hpos + p[0]/2)
894 .put_fix_number(env->vpos)
895 .put_fix_number(p[0]/2)
896 .put_symbol("DC");
897 if (fill_flag) {
898 fill_path();
899 }
900 else {
901 set_line_thickness(env);
902 out.put_symbol("ST");
903 }
904 break;
905 case 'l':
906 if (np != 2) {
907 error("2 arguments required for line");
908 break;
909 }
910 set_line_thickness(env);
911 out.put_fix_number(p[0] + env->hpos)
912 .put_fix_number(p[1] + env->vpos)
913 .put_fix_number(env->hpos)
914 .put_fix_number(env->vpos)
915 .put_symbol("DL");
916 break;
917 case 'E':
918 fill_flag = 1;
919 // fall through
920 case 'e':
921 if (np != 2) {
922 error("2 arguments required for ellipse");
923 break;
924 }
925 out.put_fix_number(p[0])
926 .put_fix_number(p[1])
927 .put_fix_number(env->hpos + p[0]/2)
928 .put_fix_number(env->vpos)
929 .put_symbol("DE");
930 if (fill_flag) {
931 fill_path();
932 }
933 else {
934 set_line_thickness(env);
935 out.put_symbol("ST");
936 }
937 break;
938 case 'P':
939 fill_flag = 1;
940 // fall through
941 case 'p':
942 {
943 if (np & 1) {
944 error("even number of arguments required for polygon");
945 break;
946 }
947 if (np == 0) {
948 error("no arguments for polygon");
949 break;
950 }
951 out.put_fix_number(env->hpos)
952 .put_fix_number(env->vpos)
953 .put_symbol("MT");
954 for (int i = 0; i < np; i += 2)
955 out.put_fix_number(p[i])
956 .put_fix_number(p[i+1])
957 .put_symbol("RL");
958 out.put_symbol("CL");
959 if (fill_flag) {
960 fill_path();
961 }
962 else {
963 set_line_thickness(env);
964 out.put_symbol("ST");
965 }
966 break;
967 }
968 case '~':
969 {
970 if (np & 1) {
971 error("even number of arguments required for spline");
972 break;
973 }
974 if (np == 0) {
975 error("no arguments for spline");
976 break;
977 }
978 out.put_fix_number(env->hpos)
979 .put_fix_number(env->vpos)
980 .put_symbol("MT");
981 out.put_fix_number(p[0]/2)
982 .put_fix_number(p[1]/2)
983 .put_symbol("RL");
984 /* tnum/tden should be between 0 and 1; the closer it is to 1
985 the tighter the curve will be to the guiding lines; 2/3
986 is the standard value */
987 const int tnum = 2;
988 const int tden = 3;
989 for (int i = 0; i < np - 2; i += 2) {
990 out.put_fix_number((p[i]*tnum)/(2*tden))
991 .put_fix_number((p[i + 1]*tnum)/(2*tden))
992 .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden))
993 .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden))
994 .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2)
995 .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2)
996 .put_symbol("RC");
997 }
998 out.put_fix_number(p[np - 2] - p[np - 2]/2)
999 .put_fix_number(p[np - 1] - p[np - 1]/2)
1000 .put_symbol("RL");
1001 set_line_thickness(env);
1002 out.put_symbol("ST");
1003 }
1004 break;
1005 case 'a':
1006 {
1007 if (np != 4) {
1008 error("4 arguments required for arc");
1009 break;
1010 }
1011 set_line_thickness(env);
1012 double c[2];
1013 if (adjust_arc_center(p, c))
1014 out.put_fix_number(env->hpos + int(c[0]))
1015 .put_fix_number(env->vpos + int(c[1]))
1016 .put_fix_number(int(sqrt(c[0]*c[0] + c[1]*c[1])))
1017 .put_float(degrees(atan2(-c[1], -c[0])))
1018 .put_float(degrees(atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])))
1019 .put_symbol("DA");
1020 else
1021 out.put_fix_number(p[0] + p[2] + env->hpos)
1022 .put_fix_number(p[1] + p[3] + env->vpos)
1023 .put_fix_number(env->hpos)
1024 .put_fix_number(env->vpos)
1025 .put_symbol("DL");
1026 }
1027 break;
1028 case 't':
1029 {
1030 if (np == 0) {
1031 line_thickness = -1;
1032 }
1033 else {
1034 // troff gratuitously adds an extra 0
1035 if (np != 1 && np != 2) {
1036 error("0 or 1 argument required for thickness");
1037 break;
1038 }
1039 line_thickness = p[0];
1040 }
1041 break;
1042 }
1043 case 'f':
1044 {
1045 if (np != 1 && np != 2) {
1046 error("1 argument required for fill");
1047 break;
1048 }
1049 fill = p[0];
1050 if (fill < 0 || fill > FILL_MAX) {
1051 // This means fill with the current color.
1052 fill = FILL_MAX + 1;
1053 }
1054 break;
1055 }
1056 default:
1057 error("unrecognised drawing command `%1'", char(code));
1058 break;
1059 }
1060
1061 output_hpos = output_vpos = -1;
1062 }
1063
1064
begin_page(int n)1065 void ps_printer::begin_page(int n)
1066 {
1067 out.begin_comment("Page:").comment_arg(itoa(n));
1068 out.comment_arg(itoa(++pages_output)).end_comment();
1069 output_style.f = 0;
1070 output_space_code = 32;
1071 output_draw_point_size = -1;
1072 output_line_thickness = -1;
1073 output_hpos = output_vpos = -1;
1074 ndefined_styles = 0;
1075 out.simple_comment("BeginPageSetup");
1076 out.put_symbol("BP");
1077 out.simple_comment("EndPageSetup");
1078 }
1079
end_page(int)1080 void ps_printer::end_page(int)
1081 {
1082 flush_sbuf();
1083 out.put_symbol("EP");
1084 if (invis_count != 0) {
1085 error("missing `endinvis' command");
1086 invis_count = 0;
1087 }
1088 }
1089
make_font(const char * nm)1090 font *ps_printer::make_font(const char *nm)
1091 {
1092 return ps_font::load_ps_font(nm);
1093 }
1094
~ps_printer()1095 ps_printer::~ps_printer()
1096 {
1097 out.simple_comment("Trailer");
1098 out.put_symbol("end");
1099 out.simple_comment("EOF");
1100 if (fseek(tempfp, 0L, 0) < 0)
1101 fatal("fseek on temporary file failed");
1102 fputs("%!PS-Adobe-", stdout);
1103 fputs((broken_flags & USE_PS_ADOBE_2_0) ? "2.0" : "3.0", stdout);
1104 putchar('\n');
1105 out.set_file(stdout);
1106 {
1107 extern const char *version_string;
1108 out.begin_comment("Creator:")
1109 .comment_arg("groff")
1110 .comment_arg("version")
1111 .comment_arg(version_string)
1112 .end_comment();
1113 }
1114 for (font_pointer_list *f = font_list; f; f = f->next) {
1115 ps_font *psf = (ps_font *)(f->p);
1116 rm.need_font(psf->get_internal_name());
1117 }
1118 rm.print_header_comments(out);
1119 out.begin_comment("Pages:").comment_arg(itoa(pages_output)).end_comment();
1120 out.begin_comment("PageOrder:").comment_arg("Ascend").end_comment();
1121 #if 0
1122 fprintf(out.get_file(), "%%%%DocumentMedia: () %g %g 0 () ()\n",
1123 font::paperwidth*72.0/font::res,
1124 paper_length*72.0/font::res);
1125 #endif
1126 out.begin_comment("Orientation:")
1127 .comment_arg(landscape_flag ? "Landscape" : "Portrait")
1128 .end_comment();
1129 if (ncopies != 1) {
1130 out.end_line();
1131 fprintf(out.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies);
1132 }
1133 out.simple_comment("EndComments");
1134 out.simple_comment("BeginProlog");
1135 rm.output_prolog(out);
1136 if (!(broken_flags & NO_SETUP_SECTION)) {
1137 out.simple_comment("EndProlog");
1138 out.simple_comment("BeginSetup");
1139 }
1140 rm.document_setup(out);
1141 out.put_symbol(dict_name).put_symbol("begin");
1142 if (ndefs > 0)
1143 ndefs += DEFS_DICT_SPARE;
1144 out.put_literal_symbol(defs_dict_name)
1145 .put_number(ndefs + 1)
1146 .put_symbol("dict")
1147 .put_symbol("def");
1148 out.put_symbol(defs_dict_name)
1149 .put_symbol("begin");
1150 out.put_literal_symbol("u")
1151 .put_delimiter('{')
1152 .put_fix_number(1)
1153 .put_symbol("mul")
1154 .put_delimiter('}')
1155 .put_symbol("bind")
1156 .put_symbol("def");
1157 defs += '\0';
1158 out.special(defs.contents());
1159 out.put_symbol("end");
1160 if (ncopies != 1)
1161 out.put_literal_symbol("#copies").put_number(ncopies).put_symbol("def");
1162 out.put_literal_symbol("RES").put_number(res).put_symbol("def");
1163 out.put_literal_symbol("PL");
1164 if (guess_flag)
1165 out.put_symbol("PLG");
1166 else
1167 out.put_fix_number(paper_length);
1168 out.put_symbol("def");
1169 out.put_literal_symbol("LS")
1170 .put_symbol(landscape_flag ? "true" : "false")
1171 .put_symbol("def");
1172 encode_fonts();
1173 out.simple_comment((broken_flags & NO_SETUP_SECTION)
1174 ? "EndProlog"
1175 : "EndSetup");
1176 out.end_line();
1177 out.copy_file(tempfp);
1178 fclose(tempfp);
1179 }
1180
special(char * arg,const environment * env)1181 void ps_printer::special(char *arg, const environment *env)
1182 {
1183 typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *);
1184 static struct {
1185 const char *name;
1186 SPECIAL_PROCP proc;
1187 } proc_table[] = {
1188 "exec", &ps_printer::do_exec,
1189 "def", &ps_printer::do_def,
1190 "mdef", &ps_printer::do_mdef,
1191 "import", &ps_printer::do_import,
1192 "file", &ps_printer::do_file,
1193 "invis", &ps_printer::do_invis,
1194 "endinvis", &ps_printer::do_endinvis,
1195 };
1196 for (char *p = arg; *p == ' ' || *p == '\n'; p++)
1197 ;
1198 char *tag = p;
1199 for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
1200 ;
1201 if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) {
1202 error("X command without `ps:' tag ignored");
1203 return;
1204 }
1205 p++;
1206 for (; *p == ' ' || *p == '\n'; p++)
1207 ;
1208 char *command = p;
1209 for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1210 ;
1211 if (*command == '\0') {
1212 error("X command without `ps:' tag ignored");
1213 return;
1214 }
1215 for (int i = 0; i < sizeof(proc_table)/sizeof(proc_table[0]); i++)
1216 if (strncmp(command, proc_table[i].name, p - command) == 0) {
1217 (this->*(proc_table[i].proc))(p, env);
1218 return;
1219 }
1220 error("X command `%1' not recognised", command);
1221 }
1222
1223 // A conforming PostScript document must not have lines longer
1224 // than 255 characters (excluding line termination characters).
1225
check_line_lengths(const char * p)1226 static int check_line_lengths(const char *p)
1227 {
1228 for (;;) {
1229 const char *end = strchr(p, '\n');
1230 if (end == 0)
1231 end = strchr(p, '\0');
1232 if (end - p > 255)
1233 return 0;
1234 if (*end == '\0')
1235 break;
1236 p = end + 1;
1237 }
1238 return 1;
1239 }
1240
do_exec(char * arg,const environment * env)1241 void ps_printer::do_exec(char *arg, const environment *env)
1242 {
1243 flush_sbuf();
1244 while (csspace(*arg))
1245 arg++;
1246 if (*arg == '\0') {
1247 error("missing argument to X exec command");
1248 return;
1249 }
1250 if (!check_line_lengths(arg)) {
1251 error("lines in X exec command must not be more than 255 characters long");
1252 return;
1253 }
1254 out.put_fix_number(env->hpos)
1255 .put_fix_number(env->vpos)
1256 .put_symbol("EBEGIN")
1257 .special(arg)
1258 .put_symbol("EEND");
1259 output_hpos = output_vpos = -1;
1260 output_style.f = 0;
1261 output_draw_point_size = -1;
1262 output_line_thickness = -1;
1263 ndefined_styles = 0;
1264 if (!ndefs)
1265 ndefs = 1;
1266 }
1267
do_file(char * arg,const environment * env)1268 void ps_printer::do_file(char *arg, const environment *env)
1269 {
1270 flush_sbuf();
1271 while (csspace(*arg))
1272 arg++;
1273 if (*arg == '\0') {
1274 error("missing argument to X file command");
1275 return;
1276 }
1277 const char *filename = arg;
1278 do {
1279 ++arg;
1280 } while (*arg != '\0' && *arg != ' ' && *arg != '\n');
1281 out.put_fix_number(env->hpos)
1282 .put_fix_number(env->vpos)
1283 .put_symbol("EBEGIN");
1284 rm.import_file(filename, out);
1285 out.put_symbol("EEND");
1286 output_hpos = output_vpos = -1;
1287 output_style.f = 0;
1288 output_draw_point_size = -1;
1289 output_line_thickness = -1;
1290 ndefined_styles = 0;
1291 if (!ndefs)
1292 ndefs = 1;
1293 }
1294
do_def(char * arg,const environment *)1295 void ps_printer::do_def(char *arg, const environment *)
1296 {
1297 flush_sbuf();
1298 while (csspace(*arg))
1299 arg++;
1300 if (!check_line_lengths(arg)) {
1301 error("lines in X def command must not be more than 255 characters long");
1302 return;
1303 }
1304 defs += arg;
1305 if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1306 defs += '\n';
1307 ndefs++;
1308 }
1309
1310 // Like def, but the first argument says how many definitions it contains.
1311
do_mdef(char * arg,const environment *)1312 void ps_printer::do_mdef(char *arg, const environment *)
1313 {
1314 flush_sbuf();
1315 char *p;
1316 int n = (int)strtol(arg, &p, 10);
1317 if (n == 0 && p == arg) {
1318 error("first argument to X mdef must be an integer");
1319 return;
1320 }
1321 if (n < 0) {
1322 error("out of range argument `%1' to X mdef command", int(n));
1323 return;
1324 }
1325 arg = p;
1326 while (csspace(*arg))
1327 arg++;
1328 if (!check_line_lengths(arg)) {
1329 error("lines in X mdef command must not be more than 255 characters long");
1330 return;
1331 }
1332 defs += arg;
1333 if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1334 defs += '\n';
1335 ndefs += n;
1336 }
1337
do_import(char * arg,const environment * env)1338 void ps_printer::do_import(char *arg, const environment *env)
1339 {
1340 flush_sbuf();
1341 while (*arg == ' ' || *arg == '\n')
1342 arg++;
1343 for (char *p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1344 ;
1345 if (*p != '\0')
1346 *p++ = '\0';
1347 int parms[6];
1348 int nparms = 0;
1349 while (nparms < 6) {
1350 char *end;
1351 long n = strtol(p, &end, 10);
1352 if (n == 0 && end == p)
1353 break;
1354 parms[nparms++] = int(n);
1355 p = end;
1356 }
1357 if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
1358 error("scaling indicators not allowed in arguments for X import command");
1359 return;
1360 }
1361 while (*p == ' ' || *p == '\n')
1362 p++;
1363 if (nparms < 5) {
1364 if (*p == '\0')
1365 error("too few arguments for X import command");
1366 else
1367 error("invalid argument `%1' for X import command", p);
1368 return;
1369 }
1370 if (*p != '\0') {
1371 error("superflous argument `%1' for X import command", p);
1372 return;
1373 }
1374 int llx = parms[0];
1375 int lly = parms[1];
1376 int urx = parms[2];
1377 int ury = parms[3];
1378 int desired_width = parms[4];
1379 int desired_height = parms[5];
1380 if (desired_width <= 0) {
1381 error("bad width argument `%1' for X import command: must be > 0",
1382 desired_width);
1383 return;
1384 }
1385 if (nparms == 6 && desired_height <= 0) {
1386 error("bad height argument `%1' for X import command: must be > 0",
1387 desired_height);
1388 return;
1389 }
1390 if (llx == urx) {
1391 error("llx and urx arguments for X import command must not be equal");
1392 return;
1393 }
1394 if (lly == ury) {
1395 error("lly and ury arguments for X import command must not be equal");
1396 return;
1397 }
1398 if (nparms == 5) {
1399 int old_wid = urx - llx;
1400 int old_ht = ury - lly;
1401 if (old_wid < 0)
1402 old_wid = -old_wid;
1403 if (old_ht < 0)
1404 old_ht = -old_ht;
1405 desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5);
1406 }
1407 if (env->vpos - desired_height < 0)
1408 warning("top of imported graphic is above the top of the page");
1409 out.put_number(llx)
1410 .put_number(lly)
1411 .put_fix_number(desired_width)
1412 .put_number(urx - llx)
1413 .put_fix_number(-desired_height)
1414 .put_number(ury - lly)
1415 .put_fix_number(env->hpos)
1416 .put_fix_number(env->vpos)
1417 .put_symbol("PBEGIN");
1418 rm.import_file(arg, out);
1419 // do this here just in case application defines PEND
1420 out.put_symbol("end");
1421 out.put_symbol("PEND");
1422 }
1423
do_invis(char *,const environment *)1424 void ps_printer::do_invis(char *, const environment *)
1425 {
1426 invis_count++;
1427 }
1428
do_endinvis(char *,const environment *)1429 void ps_printer::do_endinvis(char *, const environment *)
1430 {
1431 if (invis_count == 0)
1432 error("unbalanced `endinvis' command");
1433 else
1434 --invis_count;
1435 }
1436
make_printer()1437 printer *make_printer()
1438 {
1439 return new ps_printer;
1440 }
1441
1442 static void usage();
1443
main(int argc,char ** argv)1444 int main(int argc, char **argv)
1445 {
1446 program_name = argv[0];
1447 static char stderr_buf[BUFSIZ];
1448 setbuf(stderr, stderr_buf);
1449 int c;
1450 while ((c = getopt(argc, argv, "F:glc:w:vb:")) != EOF)
1451 switch(c) {
1452 case 'v':
1453 {
1454 extern const char *version_string;
1455 fprintf(stderr, "grops version %s\n", version_string);
1456 fflush(stderr);
1457 break;
1458 }
1459 case 'c':
1460 if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
1461 error("bad number of copies `%s'", optarg);
1462 ncopies = 1;
1463 }
1464 break;
1465 case 'g':
1466 guess_flag = 1;
1467 break;
1468 case 'l':
1469 landscape_flag = 1;
1470 break;
1471 case 'F':
1472 font::command_line_font_dir(optarg);
1473 break;
1474 case 'w':
1475 if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
1476 error("bad linewidth `%s'", optarg);
1477 linewidth = -1;
1478 }
1479 break;
1480 case 'b':
1481 // XXX check this
1482 broken_flags = atoi(optarg);
1483 bflag = 1;
1484 break;
1485 case '?':
1486 usage();
1487 break;
1488 default:
1489 assert(0);
1490 }
1491 font::set_unknown_desc_command_handler(handle_unknown_desc_command);
1492 if (optind >= argc)
1493 do_file("-");
1494 else {
1495 for (int i = optind; i < argc; i++)
1496 do_file(argv[i]);
1497 }
1498 delete pr;
1499 exit(0);
1500 }
1501
usage()1502 static void usage()
1503 {
1504 fprintf(stderr, "usage: %s [-glv] [-b n] [-c n] [-w n] [-F dir] [files ...]\n",
1505 program_name);
1506 exit(1);
1507 }
1508