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