groff before CVS: release 1.07
[s-roff.git] / grops / ps.cc
blob3601d971a67a4fe3969664a53b139e68b7f5de2d
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
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.
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.
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. */
21 #include "driver.h"
22 #include "stringclass.h"
23 #include "cset.h"
25 #include "ps.h"
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;
34 // Non-zero if -b was specified on the command line.
35 static int bflag = 0;
36 unsigned broken_flags = 0;
38 #define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
39 #define FILL_MAX 1000
41 const char *const dict_name = "grops";
42 const char *const defs_dict_name = "DEFS";
43 const int DEFS_DICT_SPARE = 50;
45 double degrees(double r)
47 return r*180.0/M_PI;
50 double radians(double d)
52 return d*M_PI/180.0;
55 inline double transform_fill(int fill)
57 return 1 - fill/double(FILL_MAX);
60 ps_output::ps_output(FILE *f, int n)
61 : fp(f), max_line_length(n), col(0), need_space(0), fixed_point(0)
65 ps_output &ps_output::set_file(FILE *f)
67 fp = f;
68 col = 0;
69 return *this;
72 ps_output &ps_output::copy_file(FILE *infp)
74 int c;
75 while ((c = getc(infp)) != EOF)
76 putc(c, fp);
77 return *this;
80 ps_output &ps_output::end_line()
82 if (col != 0) {
83 putc('\n', fp);
84 col = 0;
85 need_space = 0;
87 return *this;
90 ps_output &ps_output::special(const char *s)
92 if (s == 0 || *s == '\0')
93 return *this;
94 if (col != 0) {
95 putc('\n', fp);
96 col = 0;
98 fputs(s, fp);
99 if (strchr(s, '\0')[-1] != '\n')
100 putc('\n', fp);
101 need_space = 0;
102 return *this;
105 ps_output &ps_output::simple_comment(const char *s)
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;
118 ps_output &ps_output::begin_comment(const char *s)
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;
129 ps_output &ps_output::end_comment()
131 if (col != 0) {
132 putc('\n', fp);
133 col = 0;
135 need_space = 0;
136 return *this;
139 ps_output &ps_output::comment_arg(const char *s)
141 int len = strlen(s);
142 if (col + len + 1 > max_line_length) {
143 putc('\n', fp);
144 fputs("%%+", fp);
145 col = 3;
147 putc(' ', fp);
148 fputs(s, fp);
149 col += len + 1;
150 return *this;
153 ps_output &ps_output::set_fixed_point(int n)
155 assert(n >= 0 && n <= 10);
156 fixed_point = n;
157 return *this;
160 ps_output &ps_output::put_delimiter(char c)
162 if (col + 1 > max_line_length) {
163 putc('\n', fp);
164 col = 0;
166 putc(c, fp);
167 col++;
168 need_space = 0;
169 return *this;
172 ps_output &ps_output::put_string(const char *s, int n)
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;
183 else
184 len += 4;
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;
191 if (col + 1 > max_line_length) {
192 putc('\n', fp);
193 col = 0;
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;
202 fprintf(fp, "%02x", s[i] & 0377);
203 col += 2;
205 putc('>', fp);
206 col++;
208 else {
209 if (col + len + 2 > max_line_length && len + 2 <= max_line_length) {
210 putc('\n', fp);
211 col = 0;
213 if (col + 2 > max_line_length) {
214 putc('\n', fp);
215 col = 0;
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;
227 else
228 len = 4;
229 if (col + len + 1 > max_line_length) {
230 putc('\\', fp);
231 putc('\n', fp);
232 col = 0;
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);
248 col += len;
250 putc(')', fp);
251 col++;
253 need_space = 0;
254 return *this;
257 ps_output &ps_output::put_number(int n)
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;
267 if (need_space) {
268 putc(' ', fp);
269 col++;
271 fputs(buf, fp);
272 col += len;
273 need_space = 1;
274 return *this;
277 ps_output &ps_output::put_fix_number(int i)
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;
286 if (need_space) {
287 putc(' ', fp);
288 col++;
290 fputs(p, fp);
291 col += len;
292 need_space = 1;
293 return *this;
296 ps_output &ps_output::put_float(double d)
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;
306 if (need_space) {
307 putc(' ', fp);
308 col++;
310 fputs(buf, fp);
311 col += len;
312 need_space = 1;
313 return *this;
316 ps_output &ps_output::put_symbol(const char *s)
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;
324 if (need_space) {
325 putc(' ', fp);
326 col++;
328 fputs(s, fp);
329 col += len;
330 need_space = 1;
331 return *this;
334 ps_output &ps_output::put_literal_symbol(const char *s)
336 int len = strlen(s);
337 if (col > 0 && col + len + 1 > max_line_length) {
338 putc('\n', fp);
339 col = 0;
341 putc('/', fp);
342 fputs(s, fp);
343 col += len + 1;
344 need_space = 1;
345 return *this;
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 *);
360 ps_font *ps_font::load_ps_font(const char *s)
362 ps_font *f = new ps_font(s);
363 if (!f->load()) {
364 delete f;
365 return 0;
367 return f;
370 ps_font::ps_font(const char *nm)
371 : font(nm), encoding(0), reencoded_name(0), encoding_index(-1)
375 ps_font::~ps_font()
377 a_delete encoding;
378 a_delete reencoded_name;
381 void ps_font::handle_unknown_font_command(const char *command, const char *arg,
382 const char *filename, int lineno)
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);
393 static void handle_unknown_desc_command(const char *command, const char *arg,
394 const char *filename, int lineno)
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);
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;
416 style::style() : f(0)
420 style::style(font *p, int sz, int h, int sl)
421 : f(p), point_size(sz), height(h), slant(sl)
425 int style::operator==(const style &s) const
427 return (f == s.f && point_size == s.point_size
428 && height == s.height && slant == s.slant);
431 int style::operator!=(const style &s) const
433 return !(*this == s);
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;
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();
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)
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++;
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;
539 int ps_printer::set_encoding_index(ps_font *f)
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;
552 return f->encoding_index = next_encoding_index++;
555 void ps_printer::set_char(int i, font *f, const environment *env, int w)
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;
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;
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;
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;
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;
613 flush_sbuf();
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;
627 int is_small_h(int n)
629 return n < (font::res*2)/72 && n > -(font::res*10)/72;
632 int is_small_v(int n)
634 return n < (font::res*4)/72 && n > -(font::res*4)/72;
637 static char *make_encoding_name(int encoding_index)
639 static char buf[3 + INT_DIGITS + 1];
640 sprintf(buf, "ENC%d", encoding_index);
641 return buf;
644 const char *const WS = " \t\n\r";
646 void ps_printer::define_encoding(const char *encoding, int encoding_index)
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);
669 lineno++;
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];
682 out.put_delimiter(']').put_symbol("def");
685 void ps_printer::reencode_font(ps_font *f)
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");
693 void ps_printer::encode_fonts()
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);
708 reencode_font((ps_font *)f->p);
711 a_delete done_encoding;
714 void ps_printer::set_style(const style &sty)
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;
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;
740 else
741 psname = s;
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");
751 else {
752 out.put_literal_symbol(psname).put_symbol("SF");
754 defined_styles[ndefined_styles++] = sty;
757 void ps_printer::set_space_code(unsigned char c)
759 out.put_literal_symbol("SC").put_number(c).put_symbol("def");
762 void ps_printer::end_of_line()
764 flush_sbuf();
765 // this ensures that we do an absolute motion to the beginning of a line
766 output_vpos = output_hpos = -1;
769 void ps_printer::flush_sbuf()
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;
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;
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;
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--;
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);
843 out.put_symbol(sym);
844 output_hpos = sbuf_end_hpos;
845 output_vpos = sbuf_vpos;
846 sbuf_len = 0;
850 void ps_printer::set_line_thickness(const environment *env)
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;
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;
870 void ps_printer::fill_path()
872 if (fill > FILL_MAX)
873 out.put_symbol("BL");
874 else
875 out.put_float(transform_fill(fill)).put_symbol("FL");
878 void ps_printer::draw(int code, int *p, int np, const environment *env)
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;
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();
900 else {
901 set_line_thickness(env);
902 out.put_symbol("ST");
904 break;
905 case 'l':
906 if (np != 2) {
907 error("2 arguments required for line");
908 break;
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;
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();
933 else {
934 set_line_thickness(env);
935 out.put_symbol("ST");
937 break;
938 case 'P':
939 fill_flag = 1;
940 // fall through
941 case 'p':
943 if (np & 1) {
944 error("even number of arguments required for polygon");
945 break;
947 if (np == 0) {
948 error("no arguments for polygon");
949 break;
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();
962 else {
963 set_line_thickness(env);
964 out.put_symbol("ST");
966 break;
968 case '~':
970 if (np & 1) {
971 error("even number of arguments required for spline");
972 break;
974 if (np == 0) {
975 error("no arguments for spline");
976 break;
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");
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");
1004 break;
1005 case 'a':
1007 if (np != 4) {
1008 error("4 arguments required for arc");
1009 break;
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");
1027 break;
1028 case 't':
1030 if (np == 0) {
1031 line_thickness = -1;
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;
1039 line_thickness = p[0];
1041 break;
1043 case 'f':
1045 if (np != 1 && np != 2) {
1046 error("1 argument required for fill");
1047 break;
1049 fill = p[0];
1050 if (fill < 0 || fill > FILL_MAX) {
1051 // This means fill with the current color.
1052 fill = FILL_MAX + 1;
1054 break;
1056 default:
1057 error("unrecognised drawing command `%1'", char(code));
1058 break;
1061 output_hpos = output_vpos = -1;
1065 void ps_printer::begin_page(int n)
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");
1080 void ps_printer::end_page(int)
1082 flush_sbuf();
1083 out.put_symbol("EP");
1084 if (invis_count != 0) {
1085 error("missing `endinvis' command");
1086 invis_count = 0;
1090 font *ps_printer::make_font(const char *nm)
1092 return ps_font::load_ps_font(nm);
1095 ps_printer::~ps_printer()
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-3.0\n", stdout);
1103 out.set_file(stdout);
1105 extern const char *version_string;
1106 out.begin_comment("Creator:")
1107 .comment_arg("groff")
1108 .comment_arg("version")
1109 .comment_arg(version_string)
1110 .end_comment();
1112 for (font_pointer_list *f = font_list; f; f = f->next) {
1113 ps_font *psf = (ps_font *)(f->p);
1114 rm.need_font(psf->get_internal_name());
1116 rm.print_header_comments(out);
1117 out.begin_comment("Pages:").comment_arg(itoa(pages_output)).end_comment();
1118 out.begin_comment("PageOrder:").comment_arg("Ascend").end_comment();
1119 #if 0
1120 fprintf(out.get_file(), "%%%%DocumentMedia: () %g %g 0 () ()\n",
1121 font::paperwidth*72.0/font::res,
1122 paper_length*72.0/font::res);
1123 #endif
1124 out.begin_comment("Orientation:")
1125 .comment_arg(landscape_flag ? "Landscape" : "Portrait")
1126 .end_comment();
1127 if (ncopies != 1) {
1128 out.end_line();
1129 fprintf(out.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies);
1131 out.simple_comment("EndComments");
1132 out.simple_comment("BeginProlog");
1133 rm.output_prolog(out);
1134 if (!(broken_flags & NO_SETUP_SECTION)) {
1135 out.simple_comment("EndProlog");
1136 out.simple_comment("BeginSetup");
1138 rm.document_setup(out);
1139 out.put_symbol(dict_name).put_symbol("begin");
1140 if (ndefs > 0)
1141 ndefs += DEFS_DICT_SPARE;
1142 out.put_literal_symbol(defs_dict_name)
1143 .put_number(ndefs + 1)
1144 .put_symbol("dict")
1145 .put_symbol("def");
1146 out.put_symbol(defs_dict_name)
1147 .put_symbol("begin");
1148 out.put_literal_symbol("u")
1149 .put_delimiter('{')
1150 .put_fix_number(1)
1151 .put_symbol("mul")
1152 .put_delimiter('}')
1153 .put_symbol("bind")
1154 .put_symbol("def");
1155 defs += '\0';
1156 out.special(defs.contents());
1157 out.put_symbol("end");
1158 if (ncopies != 1)
1159 out.put_literal_symbol("#copies").put_number(ncopies).put_symbol("def");
1160 out.put_literal_symbol("RES").put_number(res).put_symbol("def");
1161 out.put_literal_symbol("PL");
1162 if (guess_flag)
1163 out.put_symbol("PLG");
1164 else
1165 out.put_fix_number(paper_length);
1166 out.put_symbol("def");
1167 out.put_literal_symbol("LS")
1168 .put_symbol(landscape_flag ? "true" : "false")
1169 .put_symbol("def");
1170 encode_fonts();
1171 out.simple_comment((broken_flags & NO_SETUP_SECTION)
1172 ? "EndProlog"
1173 : "EndSetup");
1174 out.end_line();
1175 out.copy_file(tempfp);
1176 fclose(tempfp);
1179 void ps_printer::special(char *arg, const environment *env)
1181 typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *);
1182 static struct {
1183 const char *name;
1184 SPECIAL_PROCP proc;
1185 } proc_table[] = {
1186 "exec", &ps_printer::do_exec,
1187 "def", &ps_printer::do_def,
1188 "mdef", &ps_printer::do_mdef,
1189 "import", &ps_printer::do_import,
1190 "file", &ps_printer::do_file,
1191 "invis", &ps_printer::do_invis,
1192 "endinvis", &ps_printer::do_endinvis,
1194 for (char *p = arg; *p == ' ' || *p == '\n'; p++)
1196 char *tag = p;
1197 for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
1199 if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) {
1200 error("X command without `ps:' tag ignored");
1201 return;
1203 p++;
1204 for (; *p == ' ' || *p == '\n'; p++)
1206 char *command = p;
1207 for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1209 if (*command == '\0') {
1210 error("X command without `ps:' tag ignored");
1211 return;
1213 for (int i = 0; i < sizeof(proc_table)/sizeof(proc_table[0]); i++)
1214 if (strncmp(command, proc_table[i].name, p - command) == 0) {
1215 (this->*(proc_table[i].proc))(p, env);
1216 return;
1218 error("X command `%1' not recognised", command);
1221 // A conforming PostScript document must not have lines longer
1222 // than 255 characters (excluding line termination characters).
1224 static int check_line_lengths(const char *p)
1226 for (;;) {
1227 const char *end = strchr(p, '\n');
1228 if (end == 0)
1229 end = strchr(p, '\0');
1230 if (end - p > 255)
1231 return 0;
1232 if (*end == '\0')
1233 break;
1234 p = end + 1;
1236 return 1;
1239 void ps_printer::do_exec(char *arg, const environment *env)
1241 flush_sbuf();
1242 while (csspace(*arg))
1243 arg++;
1244 if (*arg == '\0') {
1245 error("missing argument to X exec command");
1246 return;
1248 if (!check_line_lengths(arg)) {
1249 error("lines in X exec command must not be more than 255 characters long");
1250 return;
1252 out.put_fix_number(env->hpos)
1253 .put_fix_number(env->vpos)
1254 .put_symbol("EBEGIN")
1255 .special(arg)
1256 .put_symbol("EEND");
1257 output_hpos = output_vpos = -1;
1258 output_style.f = 0;
1259 output_draw_point_size = -1;
1260 output_line_thickness = -1;
1261 ndefined_styles = 0;
1262 if (!ndefs)
1263 ndefs = 1;
1266 void ps_printer::do_file(char *arg, const environment *env)
1268 flush_sbuf();
1269 while (csspace(*arg))
1270 arg++;
1271 if (*arg == '\0') {
1272 error("missing argument to X file command");
1273 return;
1275 const char *filename = arg;
1276 do {
1277 ++arg;
1278 } while (*arg != '\0' && *arg != ' ' && *arg != '\n');
1279 out.put_fix_number(env->hpos)
1280 .put_fix_number(env->vpos)
1281 .put_symbol("EBEGIN");
1282 rm.import_file(filename, out);
1283 out.put_symbol("EEND");
1284 output_hpos = output_vpos = -1;
1285 output_style.f = 0;
1286 output_draw_point_size = -1;
1287 output_line_thickness = -1;
1288 ndefined_styles = 0;
1289 if (!ndefs)
1290 ndefs = 1;
1293 void ps_printer::do_def(char *arg, const environment *)
1295 flush_sbuf();
1296 while (csspace(*arg))
1297 arg++;
1298 if (!check_line_lengths(arg)) {
1299 error("lines in X def command must not be more than 255 characters long");
1300 return;
1302 defs += arg;
1303 if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1304 defs += '\n';
1305 ndefs++;
1308 // Like def, but the first argument says how many definitions it contains.
1310 void ps_printer::do_mdef(char *arg, const environment *)
1312 flush_sbuf();
1313 char *p;
1314 int n = (int)strtol(arg, &p, 10);
1315 if (n == 0 && p == arg) {
1316 error("first argument to X mdef must be an integer");
1317 return;
1319 if (n < 0) {
1320 error("out of range argument `%1' to X mdef command", int(n));
1321 return;
1323 arg = p;
1324 while (csspace(*arg))
1325 arg++;
1326 if (!check_line_lengths(arg)) {
1327 error("lines in X mdef command must not be more than 255 characters long");
1328 return;
1330 defs += arg;
1331 if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1332 defs += '\n';
1333 ndefs += n;
1336 void ps_printer::do_import(char *arg, const environment *env)
1338 flush_sbuf();
1339 while (*arg == ' ' || *arg == '\n')
1340 arg++;
1341 for (char *p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1343 if (*p != '\0')
1344 *p++ = '\0';
1345 int parms[6];
1346 int nparms = 0;
1347 while (nparms < 6) {
1348 char *end;
1349 long n = strtol(p, &end, 10);
1350 if (n == 0 && end == p)
1351 break;
1352 parms[nparms++] = int(n);
1353 p = end;
1355 if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
1356 error("scaling indicators not allowed in arguments for X import command");
1357 return;
1359 while (*p == ' ' || *p == '\n')
1360 p++;
1361 if (nparms < 5) {
1362 if (*p == '\0')
1363 error("too few arguments for X import command");
1364 else
1365 error("invalid argument `%1' for X import command", p);
1366 return;
1368 if (*p != '\0') {
1369 error("superflous argument `%1' for X import command", p);
1370 return;
1372 int llx = parms[0];
1373 int lly = parms[1];
1374 int urx = parms[2];
1375 int ury = parms[3];
1376 int desired_width = parms[4];
1377 int desired_height = parms[5];
1378 if (desired_width <= 0) {
1379 error("bad width argument `%1' for X import command: must be > 0",
1380 desired_width);
1381 return;
1383 if (nparms == 6 && desired_height <= 0) {
1384 error("bad height argument `%1' for X import command: must be > 0",
1385 desired_height);
1386 return;
1388 if (llx == urx) {
1389 error("llx and urx arguments for X import command must not be equal");
1390 return;
1392 if (lly == ury) {
1393 error("lly and ury arguments for X import command must not be equal");
1394 return;
1396 if (nparms == 5) {
1397 int old_wid = urx - llx;
1398 int old_ht = ury - lly;
1399 if (old_wid < 0)
1400 old_wid = -old_wid;
1401 if (old_ht < 0)
1402 old_ht = -old_ht;
1403 desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5);
1405 if (env->vpos - desired_height < 0)
1406 warning("top of imported graphic is above the top of the page");
1407 out.put_number(llx)
1408 .put_number(lly)
1409 .put_fix_number(desired_width)
1410 .put_number(urx - llx)
1411 .put_fix_number(-desired_height)
1412 .put_number(ury - lly)
1413 .put_fix_number(env->hpos)
1414 .put_fix_number(env->vpos)
1415 .put_symbol("PBEGIN");
1416 rm.import_file(arg, out);
1417 // do this here just in case application defines PEND
1418 out.put_symbol("end");
1419 out.put_symbol("PEND");
1422 void ps_printer::do_invis(char *, const environment *)
1424 invis_count++;
1427 void ps_printer::do_endinvis(char *, const environment *)
1429 if (invis_count == 0)
1430 error("unbalanced `endinvis' command");
1431 else
1432 --invis_count;
1435 printer *make_printer()
1437 return new ps_printer;
1440 static void usage();
1442 int main(int argc, char **argv)
1444 program_name = argv[0];
1445 static char stderr_buf[BUFSIZ];
1446 setbuf(stderr, stderr_buf);
1447 int c;
1448 while ((c = getopt(argc, argv, "F:glc:w:vb:")) != EOF)
1449 switch(c) {
1450 case 'v':
1452 extern const char *version_string;
1453 fprintf(stderr, "grops version %s\n", version_string);
1454 fflush(stderr);
1455 break;
1457 case 'c':
1458 if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
1459 error("bad number of copies `%s'", optarg);
1460 ncopies = 1;
1462 break;
1463 case 'g':
1464 guess_flag = 1;
1465 break;
1466 case 'l':
1467 landscape_flag = 1;
1468 break;
1469 case 'F':
1470 font::command_line_font_dir(optarg);
1471 break;
1472 case 'w':
1473 if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
1474 error("bad linewidth `%s'", optarg);
1475 linewidth = -1;
1477 break;
1478 case 'b':
1479 // XXX check this
1480 broken_flags = atoi(optarg);
1481 bflag = 1;
1482 break;
1483 case '?':
1484 usage();
1485 break;
1486 default:
1487 assert(0);
1489 font::set_unknown_desc_command_handler(handle_unknown_desc_command);
1490 if (optind >= argc)
1491 do_file("-");
1492 else {
1493 for (int i = optind; i < argc; i++)
1494 do_file(argv[i]);
1496 delete pr;
1497 exit(0);
1500 static void usage()
1502 fprintf(stderr, "usage: %s [-glv] [-b n] [-c n] [-w n] [-F dir] [files ...]\n",
1503 program_name);
1504 exit(1);