2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
23 #include "stringclass.h"
31 #ifdef NEED_DECLARATION_PUTENV
33 int putenv(const char *);
35 #endif /* NEED_DECLARATION_PUTENV */
37 extern "C" const char *Version_string;
39 static int landscape_flag = 0;
40 static int manual_feed_flag = 0;
41 static int ncopies = 1;
42 static int linewidth = -1;
43 // Non-zero means generate PostScript code that guesses the paper
44 // length using the imageable area.
45 static int guess_flag = 0;
46 static double user_paper_length = 0;
48 // Non-zero if -b was specified on the command line.
50 unsigned broken_flags = 0;
52 #define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
53 #define MAX_LINE_LENGTH 72
56 const char *const dict_name = "grops";
57 const char *const defs_dict_name = "DEFS";
58 const int DEFS_DICT_SPARE = 50;
60 double degrees(double r)
65 double radians(double d)
70 inline double transform_fill(int fill)
72 return 1 - fill/double(FILL_MAX);
75 // This is used for testing whether a character should be output in the
76 // PostScript file using \nnn, so we really want the character to be
79 inline int is_ascii(char c)
81 return (unsigned char)c < 0200;
84 ps_output::ps_output(FILE *f, int n)
85 : fp(f), col(0), max_line_length(n), need_space(0), fixed_point(0)
89 ps_output &ps_output::set_file(FILE *f)
96 ps_output &ps_output::copy_file(FILE *infp)
99 while ((c = getc(infp)) != EOF)
104 ps_output &ps_output::end_line()
114 ps_output &ps_output::special(const char *s)
116 if (s == 0 || *s == '\0')
123 if (strchr(s, '\0')[-1] != '\n')
129 ps_output &ps_output::simple_comment(const char *s)
142 ps_output &ps_output::begin_comment(const char *s)
153 ps_output &ps_output::end_comment()
163 ps_output &ps_output::comment_arg(const char *s)
166 if (col + len + 1 > max_line_length) {
177 ps_output &ps_output::set_fixed_point(int n)
179 assert(n >= 0 && n <= 10);
184 ps_output &ps_output::put_delimiter(char c)
186 if (col + 1 > max_line_length) {
196 ps_output &ps_output::put_string(const char *s, int n)
200 for (i = 0; i < n; i++) {
202 if (is_ascii(c) && csprint(c)) {
203 if (c == '(' || c == ')' || c == '\\')
212 if (col + n*2 + 2 > max_line_length && n*2 + 2 <= max_line_length) {
216 if (col + 1 > max_line_length) {
222 for (i = 0; i < n; i++) {
223 if (col + 2 > max_line_length) {
227 fprintf(fp, "%02x", s[i] & 0377);
234 if (col + len + 2 > max_line_length && len + 2 <= max_line_length) {
238 if (col + 2 > max_line_length) {
244 for (i = 0; i < n; i++) {
246 if (is_ascii(c) && csprint(c)) {
247 if (c == '(' || c == ')' || c == '\\')
254 if (col + len + 1 > max_line_length) {
268 fprintf(fp, "\\%03o", c & 0377);
282 ps_output &ps_output::put_number(int n)
284 char buf[1 + INT_DIGITS + 1];
285 sprintf(buf, "%d", n);
286 int len = strlen(buf);
287 if (col > 0 && col + len + need_space > max_line_length) {
302 ps_output &ps_output::put_fix_number(int i)
304 const char *p = if_to_a(i, fixed_point);
306 if (col > 0 && col + len + need_space > max_line_length) {
321 ps_output &ps_output::put_float(double d)
324 sprintf(buf, "%.3g", d);
325 int len = strlen(buf);
326 if (col > 0 && col + len + need_space > max_line_length) {
341 ps_output &ps_output::put_symbol(const char *s)
344 if (col > 0 && col + len + need_space > max_line_length) {
359 ps_output &ps_output::put_color(unsigned int c)
362 sprintf(buf, "%.3g", double(c) / color::MAX_COLOR_VAL);
363 int len = strlen(buf);
364 if (col > 0 && col + len + need_space > max_line_length) {
379 ps_output &ps_output::put_literal_symbol(const char *s)
382 if (col > 0 && col + len + 1 > max_line_length) {
393 class ps_font : public font {
394 ps_font(const char *);
398 char *reencoded_name;
400 void handle_unknown_font_command(const char *command, const char *arg,
401 const char *filename, int lineno);
402 static ps_font *load_ps_font(const char *);
405 ps_font *ps_font::load_ps_font(const char *s)
407 ps_font *f = new ps_font(s);
415 ps_font::ps_font(const char *nm)
416 : font(nm), encoding_index(-1), encoding(0), reencoded_name(0)
423 a_delete reencoded_name;
426 void ps_font::handle_unknown_font_command(const char *command, const char *arg,
427 const char *filename, int lineno)
429 if (strcmp(command, "encoding") == 0) {
431 error_with_file_and_line(filename, lineno,
432 "`encoding' command requires an argument");
434 encoding = strsave(arg);
438 static void handle_unknown_desc_command(const char *command, const char *arg,
439 const char *filename, int lineno)
441 if (strcmp(command, "broken") == 0) {
443 error_with_file_and_line(filename, lineno,
444 "`broken' command requires an argument");
446 broken_flags = atoi(arg);
456 style(font *, int, int, int);
457 int operator==(const style &) const;
458 int operator!=(const style &) const;
461 style::style() : f(0)
465 style::style(font *p, int sz, int h, int sl)
466 : f(p), point_size(sz), height(h), slant(sl)
470 int style::operator==(const style &s) const
472 return (f == s.f && point_size == s.point_size
473 && height == s.height && slant == s.slant);
476 int style::operator!=(const style &s) const
478 return !(*this == s);
481 class ps_printer : public printer {
485 int space_char_index;
489 enum { SBUF_SIZE = 256 };
490 char sbuf[SBUF_SIZE];
495 int sbuf_space_width;
496 int sbuf_space_count;
497 int sbuf_space_diff_count;
501 color sbuf_color; // the current PS color
505 int output_draw_point_size;
507 int output_line_thickness;
508 unsigned char output_space_code;
509 enum { MAX_DEFINED_STYLES = 50 };
510 style defined_styles[MAX_DEFINED_STYLES];
512 int next_encoding_index;
519 void set_style(const style &);
520 void set_space_code(unsigned char c);
521 int set_encoding_index(ps_font *);
522 void do_exec(char *, const environment *);
523 void do_import(char *, const environment *);
524 void do_def(char *, const environment *);
525 void do_mdef(char *, const environment *);
526 void do_file(char *, const environment *);
527 void do_invis(char *, const environment *);
528 void do_endinvis(char *, const environment *);
529 void set_line_thickness_and_color(const environment *);
530 void fill_path(const environment *);
532 void define_encoding(const char *, int);
533 void reencode_font(ps_font *);
534 void set_color(color *c, int fill = 0);
539 void set_char(int i, font *f, const environment *env, int w, const char *name);
540 void draw(int code, int *p, int np, const environment *env);
541 void begin_page(int);
543 void special(char *arg, const environment *env, char type);
544 font *make_font(const char *);
549 ps_printer::ps_printer(double pl)
550 : out(0, MAX_LINE_LENGTH),
557 next_encoding_index(0),
562 out.set_file(tempfp);
564 linewidth = DEFAULT_LINEWIDTH;
566 fatal("horizontal resolution must be 1");
568 fatal("vertical resolution must be 1");
569 if (font::res % (font::sizescale*72) != 0)
570 fatal("res must be a multiple of 72*sizescale");
573 while (r % 10 == 0) {
578 out.set_fixed_point(point);
579 space_char_index = font::name_to_index("space");
581 paper_length = font::paperlength;
583 paper_length = int(pl * font::res + 0.5);
584 if (paper_length == 0)
585 paper_length = 11 * font::res;
586 equalise_spaces = font::res >= 72000;
589 int ps_printer::set_encoding_index(ps_font *f)
591 if (f->encoding_index >= 0)
592 return f->encoding_index;
593 for (font_pointer_list *p = font_list; p; p = p->next)
595 char *encoding = ((ps_font *)p->p)->encoding;
596 int encoding_index = ((ps_font *)p->p)->encoding_index;
597 if (encoding != 0 && encoding_index >= 0
598 && strcmp(f->encoding, encoding) == 0) {
599 return f->encoding_index = encoding_index;
602 return f->encoding_index = next_encoding_index++;
605 void ps_printer::set_char(int i, font *f, const environment *env, int w, const char *name)
607 if (i == space_char_index || invis_count > 0)
609 unsigned char code = f->get_code(i);
610 style sty(f, env->size, env->height, env->slant);
611 if (sty.slant != 0) {
612 if (sty.slant > 80 || sty.slant < -80) {
613 error("silly slant `%1' degrees", sty.slant);
618 if (sbuf_len < SBUF_SIZE
620 && sbuf_vpos == env->vpos
621 && sbuf_color == *env->col) {
622 if (sbuf_end_hpos == env->hpos) {
623 sbuf[sbuf_len++] = code;
624 sbuf_end_hpos += w + sbuf_kern;
627 if (sbuf_len == 1 && sbuf_kern == 0) {
628 sbuf_kern = env->hpos - sbuf_end_hpos;
629 sbuf_end_hpos = env->hpos + sbuf_kern + w;
630 sbuf[sbuf_len++] = code;
633 /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
634 starting a new string. */
635 if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
636 && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
637 if (sbuf_space_code < 0) {
638 if (f->contains(space_char_index)) {
639 sbuf_space_code = f->get_code(space_char_index);
640 sbuf_space_width = env->hpos - sbuf_end_hpos;
641 sbuf_end_hpos = env->hpos + w + sbuf_kern;
642 sbuf[sbuf_len++] = sbuf_space_code;
643 sbuf[sbuf_len++] = code;
649 int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
650 if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) {
651 sbuf_end_hpos = env->hpos + w + sbuf_kern;
652 sbuf[sbuf_len++] = sbuf_space_code;
653 sbuf[sbuf_len++] = code;
656 sbuf_space_diff_count++;
658 sbuf_space_diff_count--;
668 sbuf_end_hpos = env->hpos + w;
669 sbuf_start_hpos = env->hpos;
670 sbuf_vpos = env->vpos;
672 sbuf_space_code = -1;
673 sbuf_space_width = 0;
674 sbuf_space_count = sbuf_space_diff_count = 0;
676 if (sbuf_color != *env->col)
680 static char *make_encoding_name(int encoding_index)
682 static char buf[3 + INT_DIGITS + 1];
683 sprintf(buf, "ENC%d", encoding_index);
687 const char *const WS = " \t\n\r";
689 void ps_printer::define_encoding(const char *encoding, int encoding_index)
693 for (i = 0; i < 256; i++)
696 FILE *fp = font::open_file(encoding, &path);
698 fatal("can't open encoding file `%1'", encoding);
701 while (fgets(buf, 512, fp) != 0) {
705 if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) {
706 char *q = strtok(0, WS);
708 if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256)
709 fatal_with_file_and_line(path, lineno, "bad second field");
710 vec[n] = new char[strlen(p) + 1];
716 out.put_literal_symbol(make_encoding_name(encoding_index));
717 out.put_delimiter('[');
718 for (i = 0; i < 256; i++) {
720 out.put_literal_symbol(".notdef");
722 out.put_literal_symbol(vec[i]);
726 out.put_delimiter(']')
730 void ps_printer::reencode_font(ps_font *f)
732 out.put_literal_symbol(f->reencoded_name)
733 .put_symbol(make_encoding_name(f->encoding_index))
734 .put_literal_symbol(f->get_internal_name())
738 void ps_printer::encode_fonts()
740 if (next_encoding_index == 0)
742 char *done_encoding = new char[next_encoding_index];
743 for (int i = 0; i < next_encoding_index; i++)
744 done_encoding[i] = 0;
745 for (font_pointer_list *f = font_list; f; f = f->next) {
746 int encoding_index = ((ps_font *)f->p)->encoding_index;
747 if (encoding_index >= 0) {
748 assert(encoding_index < next_encoding_index);
749 if (!done_encoding[encoding_index]) {
750 done_encoding[encoding_index] = 1;
751 define_encoding(((ps_font *)f->p)->encoding, encoding_index);
753 reencode_font((ps_font *)f->p);
756 a_delete done_encoding;
759 void ps_printer::set_style(const style &sty)
761 char buf[1 + INT_DIGITS + 1];
762 for (int i = 0; i < ndefined_styles; i++)
763 if (sty == defined_styles[i]) {
764 sprintf(buf, "F%d", i);
768 if (ndefined_styles >= MAX_DEFINED_STYLES)
770 sprintf(buf, "F%d", ndefined_styles);
771 out.put_literal_symbol(buf);
772 const char *psname = sty.f->get_internal_name();
774 fatal("no internalname specified for font `%1'", sty.f->get_name());
775 char *encoding = ((ps_font *)sty.f)->encoding;
777 char *s = ((ps_font *)sty.f)->reencoded_name;
779 int ei = set_encoding_index((ps_font *)sty.f);
780 char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1];
781 sprintf(tem, "%s@%d", psname, ei);
783 ((ps_font *)sty.f)->reencoded_name = tem;
788 out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size);
789 if (sty.height != 0 || sty.slant != 0) {
790 int h = sty.height == 0 ? sty.point_size : sty.height;
791 h *= font::res/(72*font::sizescale);
792 int c = int(h*tan(radians(sty.slant)) + .5);
793 out.put_fix_number(c)
795 .put_literal_symbol(psname)
799 out.put_literal_symbol(psname)
802 defined_styles[ndefined_styles++] = sty;
805 void ps_printer::set_color(color *col, int fill)
808 unsigned int components[4];
810 color_scheme cs = col->get_components(components);
811 s[0] = fill ? 'F' : 'C';
814 case DEFAULT: // black
825 col->get_cmyk(&Cyan, &Magenta, &Yellow, &Black);
842 void ps_printer::set_space_code(unsigned char c)
844 out.put_literal_symbol("SC")
849 void ps_printer::end_of_line()
852 // this ensures that we do an absolute motion to the beginning of a line
853 output_vpos = output_hpos = -1;
856 void ps_printer::flush_sbuf()
868 if (output_style != sbuf_style) {
869 set_style(sbuf_style);
870 output_style = sbuf_style;
873 if (output_hpos < 0 || output_vpos < 0)
876 if (output_hpos != sbuf_start_hpos)
878 if (output_vpos != sbuf_vpos) {
880 motion = RELATIVE_HV;
885 if (sbuf_space_code >= 0) {
886 int w = sbuf_style.f->get_width(space_char_index, sbuf_style.point_size);
887 if (w + sbuf_kern != sbuf_space_width) {
888 if (sbuf_space_code != output_space_code) {
889 set_space_code(sbuf_space_code);
890 output_space_code = sbuf_space_code;
893 extra_space = sbuf_space_width - w - sbuf_kern;
894 if (sbuf_space_diff_count > sbuf_space_count/2)
896 else if (sbuf_space_diff_count < -(sbuf_space_count/2))
901 out.put_fix_number(extra_space);
903 out.put_fix_number(sbuf_kern);
904 out.put_string(sbuf, sbuf_len);
905 char command_array[] = {'A', 'B', 'C', 'D',
911 sym[0] = command_array[motion*4 + space_flag + 2*(sbuf_kern != 0)];
917 out.put_fix_number(sbuf_start_hpos)
918 .put_fix_number(sbuf_vpos);
921 out.put_fix_number(sbuf_start_hpos - output_hpos);
924 out.put_fix_number(sbuf_vpos - output_vpos);
927 out.put_fix_number(sbuf_start_hpos - output_hpos)
928 .put_fix_number(sbuf_vpos - output_vpos);
934 output_hpos = sbuf_end_hpos;
935 output_vpos = sbuf_vpos;
939 void ps_printer::set_line_thickness_and_color(const environment *env)
941 if (line_thickness < 0) {
942 if (output_draw_point_size != env->size) {
943 // we ought to check for overflow here
944 int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
945 out.put_fix_number(lw)
947 output_draw_point_size = env->size;
948 output_line_thickness = -1;
952 if (output_line_thickness != line_thickness) {
953 out.put_fix_number(line_thickness)
955 output_line_thickness = line_thickness;
956 output_draw_point_size = -1;
959 if (sbuf_color != *env->col)
963 void ps_printer::fill_path(const environment *env)
965 if (sbuf_color == *env->fill)
966 out.put_symbol("FL");
968 set_color(env->fill, 1);
971 void ps_printer::draw(int code, int *p, int np, const environment *env)
982 // troff adds an extra argument to C
983 if (np != 1 && !(code == 'C' && np == 2)) {
984 error("1 argument required for circle");
987 out.put_fix_number(env->hpos + p[0]/2)
988 .put_fix_number(env->vpos)
989 .put_fix_number(p[0]/2)
994 set_line_thickness_and_color(env);
995 out.put_symbol("ST");
1000 error("2 arguments required for line");
1003 set_line_thickness_and_color(env);
1004 out.put_fix_number(p[0] + env->hpos)
1005 .put_fix_number(p[1] + env->vpos)
1006 .put_fix_number(env->hpos)
1007 .put_fix_number(env->vpos)
1015 error("2 arguments required for ellipse");
1018 out.put_fix_number(p[0])
1019 .put_fix_number(p[1])
1020 .put_fix_number(env->hpos + p[0]/2)
1021 .put_fix_number(env->vpos)
1026 set_line_thickness_and_color(env);
1027 out.put_symbol("ST");
1036 error("even number of arguments required for polygon");
1040 error("no arguments for polygon");
1043 out.put_fix_number(env->hpos)
1044 .put_fix_number(env->vpos)
1046 for (int i = 0; i < np; i += 2)
1047 out.put_fix_number(p[i])
1048 .put_fix_number(p[i+1])
1050 out.put_symbol("CL");
1054 set_line_thickness_and_color(env);
1055 out.put_symbol("ST");
1062 error("even number of arguments required for spline");
1066 error("no arguments for spline");
1069 out.put_fix_number(env->hpos)
1070 .put_fix_number(env->vpos)
1072 out.put_fix_number(p[0]/2)
1073 .put_fix_number(p[1]/2)
1075 /* tnum/tden should be between 0 and 1; the closer it is to 1
1076 the tighter the curve will be to the guiding lines; 2/3
1077 is the standard value */
1080 for (int i = 0; i < np - 2; i += 2) {
1081 out.put_fix_number((p[i]*tnum)/(2*tden))
1082 .put_fix_number((p[i + 1]*tnum)/(2*tden))
1083 .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden))
1084 .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden))
1085 .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2)
1086 .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2)
1089 out.put_fix_number(p[np - 2] - p[np - 2]/2)
1090 .put_fix_number(p[np - 1] - p[np - 1]/2)
1092 set_line_thickness_and_color(env);
1093 out.put_symbol("ST");
1099 error("4 arguments required for arc");
1102 set_line_thickness_and_color(env);
1104 if (adjust_arc_center(p, c))
1105 out.put_fix_number(env->hpos + int(c[0]))
1106 .put_fix_number(env->vpos + int(c[1]))
1107 .put_fix_number(int(sqrt(c[0]*c[0] + c[1]*c[1])))
1108 .put_float(degrees(atan2(-c[1], -c[0])))
1109 .put_float(degrees(atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])))
1112 out.put_fix_number(p[0] + p[2] + env->hpos)
1113 .put_fix_number(p[1] + p[3] + env->vpos)
1114 .put_fix_number(env->hpos)
1115 .put_fix_number(env->vpos)
1121 line_thickness = -1;
1123 // troff gratuitously adds an extra 0
1124 if (np != 1 && np != 2) {
1125 error("0 or 1 argument required for thickness");
1128 line_thickness = p[0];
1132 error("unrecognised drawing command `%1'", char(code));
1135 output_hpos = output_vpos = -1;
1138 void ps_printer::begin_page(int n)
1140 out.begin_comment("Page:")
1141 .comment_arg(i_to_a(n));
1142 out.comment_arg(i_to_a(++pages_output))
1145 output_space_code = 32;
1146 output_draw_point_size = -1;
1147 output_line_thickness = -1;
1148 output_hpos = output_vpos = -1;
1149 ndefined_styles = 0;
1150 out.simple_comment("BeginPageSetup")
1152 .simple_comment("EndPageSetup");
1153 if (sbuf_color != default_color)
1154 set_color(&sbuf_color);
1157 void ps_printer::end_page(int)
1160 set_color(&default_color);
1161 out.put_symbol("EP");
1162 if (invis_count != 0) {
1163 error("missing `endinvis' command");
1168 font *ps_printer::make_font(const char *nm)
1170 return ps_font::load_ps_font(nm);
1173 ps_printer::~ps_printer()
1175 out.simple_comment("Trailer")
1177 .simple_comment("EOF");
1178 if (fseek(tempfp, 0L, 0) < 0)
1179 fatal("fseek on temporary file failed");
1180 fputs("%!PS-Adobe-", stdout);
1181 fputs((broken_flags & USE_PS_ADOBE_2_0) ? "2.0" : "3.0", stdout);
1183 out.set_file(stdout);
1185 out.begin_comment("Creator:")
1186 .comment_arg("groff")
1187 .comment_arg("version")
1188 .comment_arg(Version_string)
1192 fputs("%%CreationDate: ", out.get_file());
1193 #ifdef LONG_FOR_TIME_T
1199 fputs(ctime(&t), out.get_file());
1201 for (font_pointer_list *f = font_list; f; f = f->next) {
1202 ps_font *psf = (ps_font *)(f->p);
1203 rm.need_font(psf->get_internal_name());
1205 rm.print_header_comments(out);
1206 out.begin_comment("Pages:")
1207 .comment_arg(i_to_a(pages_output))
1209 out.begin_comment("PageOrder:")
1210 .comment_arg("Ascend")
1213 fprintf(out.get_file(), "%%%%DocumentMedia: () %g %g 0 () ()\n",
1214 font::paperwidth*72.0/font::res,
1215 paper_length*72.0/font::res);
1217 out.begin_comment("Orientation:")
1218 .comment_arg(landscape_flag ? "Landscape" : "Portrait")
1222 fprintf(out.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies);
1224 out.simple_comment("EndComments");
1225 out.simple_comment("BeginProlog");
1226 rm.output_prolog(out);
1227 if (!(broken_flags & NO_SETUP_SECTION)) {
1228 out.simple_comment("EndProlog");
1229 out.simple_comment("BeginSetup");
1231 rm.document_setup(out);
1232 out.put_symbol(dict_name)
1233 .put_symbol("begin");
1235 ndefs += DEFS_DICT_SPARE;
1236 out.put_literal_symbol(defs_dict_name)
1237 .put_number(ndefs + 1)
1240 out.put_symbol(defs_dict_name)
1241 .put_symbol("begin");
1242 out.put_literal_symbol("u")
1250 out.special(defs.contents());
1251 out.put_symbol("end");
1253 out.put_literal_symbol("#copies")
1254 .put_number(ncopies)
1256 out.put_literal_symbol("RES")
1259 out.put_literal_symbol("PL");
1261 out.put_symbol("PLG");
1263 out.put_fix_number(paper_length);
1264 out.put_symbol("def");
1265 out.put_literal_symbol("LS")
1266 .put_symbol(landscape_flag ? "true" : "false")
1268 if (manual_feed_flag) {
1269 out.begin_comment("BeginFeature:")
1270 .comment_arg("*ManualFeed")
1271 .comment_arg("True")
1273 .put_symbol("MANUAL")
1274 .simple_comment("EndFeature");
1277 out.simple_comment((broken_flags & NO_SETUP_SECTION)
1281 out.copy_file(tempfp);
1285 void ps_printer::special(char *arg, const environment *env, char type)
1289 typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *);
1294 { "exec", &ps_printer::do_exec },
1295 { "def", &ps_printer::do_def },
1296 { "mdef", &ps_printer::do_mdef },
1297 { "import", &ps_printer::do_import },
1298 { "file", &ps_printer::do_file },
1299 { "invis", &ps_printer::do_invis },
1300 { "endinvis", &ps_printer::do_endinvis },
1303 for (p = arg; *p == ' ' || *p == '\n'; p++)
1306 for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
1308 if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) {
1309 error("X command without `ps:' tag ignored");
1313 for (; *p == ' ' || *p == '\n'; p++)
1316 for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1318 if (*command == '\0') {
1319 error("empty X command ignored");
1322 for (unsigned int i = 0; i < sizeof(proc_table)/sizeof(proc_table[0]); i++)
1323 if (strncmp(command, proc_table[i].name, p - command) == 0) {
1324 (this->*(proc_table[i].proc))(p, env);
1327 error("X command `%1' not recognised", command);
1330 // A conforming PostScript document must not have lines longer
1331 // than 255 characters (excluding line termination characters).
1333 static int check_line_lengths(const char *p)
1336 const char *end = strchr(p, '\n');
1338 end = strchr(p, '\0');
1348 void ps_printer::do_exec(char *arg, const environment *env)
1351 while (csspace(*arg))
1354 error("missing argument to X exec command");
1357 if (!check_line_lengths(arg)) {
1358 error("lines in X exec command must not be more than 255 characters long");
1361 out.put_fix_number(env->hpos)
1362 .put_fix_number(env->vpos)
1363 .put_symbol("EBEGIN")
1365 .put_symbol("EEND");
1366 output_hpos = output_vpos = -1;
1368 output_draw_point_size = -1;
1369 output_line_thickness = -1;
1370 ndefined_styles = 0;
1375 void ps_printer::do_file(char *arg, const environment *env)
1378 while (csspace(*arg))
1381 error("missing argument to X file command");
1384 const char *filename = arg;
1387 } while (*arg != '\0' && *arg != ' ' && *arg != '\n');
1388 out.put_fix_number(env->hpos)
1389 .put_fix_number(env->vpos)
1390 .put_symbol("EBEGIN");
1391 rm.import_file(filename, out);
1392 out.put_symbol("EEND");
1393 output_hpos = output_vpos = -1;
1395 output_draw_point_size = -1;
1396 output_line_thickness = -1;
1397 ndefined_styles = 0;
1402 void ps_printer::do_def(char *arg, const environment *)
1405 while (csspace(*arg))
1407 if (!check_line_lengths(arg)) {
1408 error("lines in X def command must not be more than 255 characters long");
1412 if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1417 // Like def, but the first argument says how many definitions it contains.
1419 void ps_printer::do_mdef(char *arg, const environment *)
1423 int n = (int)strtol(arg, &p, 10);
1424 if (n == 0 && p == arg) {
1425 error("first argument to X mdef must be an integer");
1429 error("out of range argument `%1' to X mdef command", int(n));
1433 while (csspace(*arg))
1435 if (!check_line_lengths(arg)) {
1436 error("lines in X mdef command must not be more than 255 characters long");
1440 if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1445 void ps_printer::do_import(char *arg, const environment *env)
1448 while (*arg == ' ' || *arg == '\n')
1451 for (p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1457 while (nparms < 6) {
1459 long n = strtol(p, &end, 10);
1460 if (n == 0 && end == p)
1462 parms[nparms++] = int(n);
1465 if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
1466 error("scaling indicators not allowed in arguments for X import command");
1469 while (*p == ' ' || *p == '\n')
1473 error("too few arguments for X import command");
1475 error("invalid argument `%1' for X import command", p);
1479 error("superflous argument `%1' for X import command", p);
1486 int desired_width = parms[4];
1487 int desired_height = parms[5];
1488 if (desired_width <= 0) {
1489 error("bad width argument `%1' for X import command: must be > 0",
1493 if (nparms == 6 && desired_height <= 0) {
1494 error("bad height argument `%1' for X import command: must be > 0",
1499 error("llx and urx arguments for X import command must not be equal");
1503 error("lly and ury arguments for X import command must not be equal");
1507 int old_wid = urx - llx;
1508 int old_ht = ury - lly;
1513 desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5);
1515 if (env->vpos - desired_height < 0)
1516 warning("top of imported graphic is above the top of the page");
1519 .put_fix_number(desired_width)
1520 .put_number(urx - llx)
1521 .put_fix_number(-desired_height)
1522 .put_number(ury - lly)
1523 .put_fix_number(env->hpos)
1524 .put_fix_number(env->vpos)
1525 .put_symbol("PBEGIN");
1526 rm.import_file(arg, out);
1527 // do this here just in case application defines PEND
1528 out.put_symbol("end");
1529 out.put_symbol("PEND");
1532 void ps_printer::do_invis(char *, const environment *)
1537 void ps_printer::do_endinvis(char *, const environment *)
1539 if (invis_count == 0)
1540 error("unbalanced `endinvis' command");
1545 printer *make_printer()
1547 return new ps_printer(user_paper_length);
1550 static void usage(FILE *stream);
1552 int main(int argc, char **argv)
1554 program_name = argv[0];
1556 static char stderr_buf[BUFSIZ];
1557 setbuf(stderr, stderr_buf);
1559 static const struct option long_options[] = {
1560 { "help", no_argument, 0, CHAR_MAX + 1 },
1561 { "version", no_argument, 0, 'v' },
1564 while ((c = getopt_long(argc, argv, "b:c:F:glmp:P:vw:", long_options, NULL))
1569 broken_flags = atoi(optarg);
1573 if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
1574 error("bad number of copies `%s'", optarg);
1579 font::command_line_font_dir(optarg);
1588 manual_feed_flag = 1;
1591 if (!font::scan_papersize(optarg, 0, &user_paper_length, 0))
1592 error("invalid custom paper size `%1' ignored", optarg);
1595 env = "GROPS_PROLOGUE";
1599 if (putenv(strsave(env.contents())))
1600 fatal("putenv failed");
1603 printf("GNU grops (groff) version %s\n", Version_string);
1607 if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
1608 error("bad linewidth `%1'", optarg);
1612 case CHAR_MAX + 1: // --help
1623 font::set_unknown_desc_command_handler(handle_unknown_desc_command);
1625 SET_BINARY(fileno(stdout));
1630 for (int i = optind; i < argc; i++)
1637 static void usage(FILE *stream)
1640 "usage: %s [-glmv] [-b n] [-c n] [-w n] [-P prologue] [-F dir] [files ...]\n",