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