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