1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2004
3114402Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru/*
23114402Sru * PostScript documentation:
24114402Sru *   http://www.adobe.com/products/postscript/pdfs/PLRM.pdf
25114402Sru *   http://partners.adobe.com/asn/developer/pdfs/tn/5001.DSC_Spec.pdf
26114402Sru */
27114402Sru
28114402Sru#include "driver.h"
29114402Sru#include "stringclass.h"
30114402Sru#include "cset.h"
31114402Sru#include "nonposix.h"
32114402Sru#include "paper.h"
33114402Sru
34114402Sru#include "ps.h"
35114402Sru#include <time.h>
36114402Sru
37114402Sru#ifdef NEED_DECLARATION_PUTENV
38114402Sruextern "C" {
39114402Sru  int putenv(const char *);
40114402Sru}
41114402Sru#endif /* NEED_DECLARATION_PUTENV */
42114402Sru
43114402Sruextern "C" const char *Version_string;
44114402Sru
45151497Sru// search path defaults to the current directory
46151497Srusearch_path include_search_path(0, 0, 0, 1);
47151497Sru
48114402Srustatic int landscape_flag = 0;
49114402Srustatic int manual_feed_flag = 0;
50114402Srustatic int ncopies = 1;
51114402Srustatic int linewidth = -1;
52114402Sru// Non-zero means generate PostScript code that guesses the paper
53114402Sru// length using the imageable area.
54114402Srustatic int guess_flag = 0;
55114402Srustatic double user_paper_length = 0;
56114402Srustatic double user_paper_width = 0;
57114402Sru
58114402Sru// Non-zero if -b was specified on the command line.
59114402Srustatic int bflag = 0;
60114402Sruunsigned broken_flags = 0;
61114402Sru
62114402Sru// Non-zero means we need the CMYK extension for PostScript Level 1
63114402Srustatic int cmyk_flag = 0;
64114402Sru
65114402Sru#define DEFAULT_LINEWIDTH 40	/* in ems/1000 */
66114402Sru#define MAX_LINE_LENGTH 72
67114402Sru#define FILL_MAX 1000
68114402Sru
69114402Sruconst char *const dict_name = "grops";
70114402Sruconst char *const defs_dict_name = "DEFS";
71114402Sruconst int DEFS_DICT_SPARE = 50;
72114402Sru
73114402Srudouble degrees(double r)
74114402Sru{
75114402Sru  return r*180.0/PI;
76114402Sru}
77114402Sru
78114402Srudouble radians(double d)
79114402Sru{
80114402Sru  return d*PI/180.0;
81114402Sru}
82114402Sru
83114402Sru// This is used for testing whether a character should be output in the
84114402Sru// PostScript file using \nnn, so we really want the character to be
85114402Sru// less than 0200.
86114402Sru
87114402Sruinline int is_ascii(char c)
88114402Sru{
89114402Sru  return (unsigned char)c < 0200;
90114402Sru}
91114402Sru
92114402Srups_output::ps_output(FILE *f, int n)
93114402Sru: fp(f), col(0), max_line_length(n), need_space(0), fixed_point(0)
94114402Sru{
95114402Sru}
96114402Sru
97114402Srups_output &ps_output::set_file(FILE *f)
98114402Sru{
99114402Sru  fp = f;
100114402Sru  col = 0;
101114402Sru  return *this;
102114402Sru}
103114402Sru
104114402Srups_output &ps_output::copy_file(FILE *infp)
105114402Sru{
106114402Sru  int c;
107114402Sru  while ((c = getc(infp)) != EOF)
108114402Sru    putc(c, fp);
109114402Sru  return *this;
110114402Sru}
111114402Sru
112114402Srups_output &ps_output::end_line()
113114402Sru{
114114402Sru  if (col != 0) {
115114402Sru    putc('\n', fp);
116114402Sru    col = 0;
117114402Sru    need_space = 0;
118114402Sru  }
119114402Sru  return *this;
120114402Sru}
121114402Sru
122114402Srups_output &ps_output::special(const char *s)
123114402Sru{
124114402Sru  if (s == 0 || *s == '\0')
125114402Sru    return *this;
126114402Sru  if (col != 0) {
127114402Sru    putc('\n', fp);
128114402Sru    col = 0;
129114402Sru  }
130114402Sru  fputs(s, fp);
131114402Sru  if (strchr(s, '\0')[-1] != '\n')
132114402Sru    putc('\n', fp);
133114402Sru  need_space = 0;
134114402Sru  return *this;
135114402Sru}
136114402Sru
137114402Srups_output &ps_output::simple_comment(const char *s)
138114402Sru{
139114402Sru  if (col != 0)
140114402Sru    putc('\n', fp);
141114402Sru  putc('%', fp);
142114402Sru  putc('%', fp);
143114402Sru  fputs(s, fp);
144114402Sru  putc('\n', fp);
145114402Sru  col = 0;
146114402Sru  need_space = 0;
147114402Sru  return *this;
148114402Sru}
149114402Sru
150114402Srups_output &ps_output::begin_comment(const char *s)
151114402Sru{
152114402Sru  if (col != 0)
153114402Sru    putc('\n', fp);
154114402Sru  putc('%', fp);
155114402Sru  putc('%', fp);
156114402Sru  fputs(s, fp);
157114402Sru  col = 2 + strlen(s);
158114402Sru  return *this;
159114402Sru}
160114402Sru
161114402Srups_output &ps_output::end_comment()
162114402Sru{
163114402Sru  if (col != 0) {
164114402Sru    putc('\n', fp);
165114402Sru    col = 0;
166114402Sru  }
167114402Sru  need_space = 0;
168114402Sru  return *this;
169114402Sru}
170114402Sru
171114402Srups_output &ps_output::comment_arg(const char *s)
172114402Sru{
173114402Sru  int len = strlen(s);
174114402Sru  if (col + len + 1 > max_line_length) {
175114402Sru    putc('\n', fp);
176114402Sru    fputs("%%+", fp);
177114402Sru    col = 3;
178114402Sru  }
179114402Sru  putc(' ',  fp);
180114402Sru  fputs(s, fp);
181114402Sru  col += len + 1;
182114402Sru  return *this;
183114402Sru}
184114402Sru
185114402Srups_output &ps_output::set_fixed_point(int n)
186114402Sru{
187114402Sru  assert(n >= 0 && n <= 10);
188114402Sru  fixed_point = n;
189114402Sru  return *this;
190114402Sru}
191114402Sru
192114402Srups_output &ps_output::put_delimiter(char c)
193114402Sru{
194114402Sru  if (col + 1 > max_line_length) {
195114402Sru    putc('\n', fp);
196114402Sru    col = 0;
197114402Sru  }
198114402Sru  putc(c, fp);
199114402Sru  col++;
200114402Sru  need_space = 0;
201114402Sru  return *this;
202114402Sru}
203114402Sru
204114402Srups_output &ps_output::put_string(const char *s, int n)
205114402Sru{
206114402Sru  int len = 0;
207114402Sru  int i;
208114402Sru  for (i = 0; i < n; i++) {
209114402Sru    char c = s[i];
210114402Sru    if (is_ascii(c) && csprint(c)) {
211114402Sru      if (c == '(' || c == ')' || c == '\\')
212114402Sru	len += 2;
213114402Sru      else
214114402Sru	len += 1;
215114402Sru    }
216114402Sru    else
217114402Sru      len += 4;
218114402Sru  }
219114402Sru  if (len > n*2) {
220114402Sru    if (col + n*2 + 2 > max_line_length && n*2 + 2 <= max_line_length) {
221114402Sru      putc('\n', fp);
222114402Sru      col = 0;
223114402Sru    }
224114402Sru    if (col + 1 > max_line_length) {
225114402Sru      putc('\n', fp);
226114402Sru      col = 0;
227114402Sru    }
228114402Sru    putc('<', fp);
229114402Sru    col++;
230114402Sru    for (i = 0; i < n; i++) {
231114402Sru      if (col + 2 > max_line_length) {
232114402Sru	putc('\n', fp);
233114402Sru	col = 0;
234114402Sru      }
235114402Sru      fprintf(fp, "%02x", s[i] & 0377);
236114402Sru      col += 2;
237114402Sru    }
238114402Sru    putc('>', fp);
239114402Sru    col++;
240114402Sru  }
241114402Sru  else {
242114402Sru    if (col + len + 2 > max_line_length && len + 2 <= max_line_length) {
243114402Sru      putc('\n', fp);
244114402Sru      col = 0;
245114402Sru    }
246114402Sru    if (col + 2 > max_line_length) {
247114402Sru      putc('\n', fp);
248114402Sru      col = 0;
249114402Sru    }
250114402Sru    putc('(', fp);
251114402Sru    col++;
252114402Sru    for (i = 0; i < n; i++) {
253114402Sru      char c = s[i];
254114402Sru      if (is_ascii(c) && csprint(c)) {
255114402Sru	if (c == '(' || c == ')' || c == '\\')
256114402Sru	  len = 2;
257114402Sru	else
258114402Sru	  len = 1;
259114402Sru      }
260114402Sru      else
261114402Sru	len = 4;
262114402Sru      if (col + len + 1 > max_line_length) {
263114402Sru	putc('\\', fp);
264114402Sru	putc('\n', fp);
265114402Sru	col = 0;
266114402Sru      }
267114402Sru      switch (len) {
268114402Sru      case 1:
269114402Sru	putc(c, fp);
270114402Sru	break;
271114402Sru      case 2:
272114402Sru	putc('\\', fp);
273114402Sru	putc(c, fp);
274114402Sru	break;
275114402Sru      case 4:
276114402Sru	fprintf(fp, "\\%03o", c & 0377);
277114402Sru	break;
278114402Sru      default:
279114402Sru	assert(0);
280114402Sru      }
281114402Sru      col += len;
282114402Sru    }
283114402Sru    putc(')', fp);
284114402Sru    col++;
285114402Sru  }
286114402Sru  need_space = 0;
287114402Sru  return *this;
288114402Sru}
289114402Sru
290114402Srups_output &ps_output::put_number(int n)
291114402Sru{
292114402Sru  char buf[1 + INT_DIGITS + 1];
293114402Sru  sprintf(buf, "%d", n);
294114402Sru  int len = strlen(buf);
295114402Sru  if (col > 0 && col + len + need_space > max_line_length) {
296114402Sru    putc('\n', fp);
297114402Sru    col = 0;
298114402Sru    need_space = 0;
299114402Sru  }
300114402Sru  if (need_space) {
301114402Sru    putc(' ', fp);
302114402Sru    col++;
303114402Sru  }
304114402Sru  fputs(buf, fp);
305114402Sru  col += len;
306114402Sru  need_space = 1;
307114402Sru  return *this;
308114402Sru}
309114402Sru
310114402Srups_output &ps_output::put_fix_number(int i)
311114402Sru{
312114402Sru  const char *p = if_to_a(i, fixed_point);
313114402Sru  int len = strlen(p);
314114402Sru  if (col > 0 && col + len + need_space > max_line_length) {
315114402Sru    putc('\n', fp);
316114402Sru    col = 0;
317114402Sru    need_space = 0;
318114402Sru  }
319114402Sru  if (need_space) {
320114402Sru    putc(' ', fp);
321114402Sru    col++;
322114402Sru  }
323114402Sru  fputs(p, fp);
324114402Sru  col += len;
325114402Sru  need_space = 1;
326114402Sru  return *this;
327114402Sru}
328114402Sru
329114402Srups_output &ps_output::put_float(double d)
330114402Sru{
331114402Sru  char buf[128];
332151497Sru  sprintf(buf, "%.4f", d);
333151497Sru  int last = strlen(buf) - 1;
334151497Sru  while (buf[last] == '0')
335151497Sru    last--;
336151497Sru  if (buf[last] == '.')
337151497Sru    last--;
338151497Sru  buf[++last] = '\0';
339151497Sru  if (col > 0 && col + last + need_space > max_line_length) {
340114402Sru    putc('\n', fp);
341114402Sru    col = 0;
342114402Sru    need_space = 0;
343114402Sru  }
344114402Sru  if (need_space) {
345114402Sru    putc(' ', fp);
346114402Sru    col++;
347114402Sru  }
348114402Sru  fputs(buf, fp);
349151497Sru  col += last;
350114402Sru  need_space = 1;
351114402Sru  return *this;
352114402Sru}
353114402Sru
354114402Srups_output &ps_output::put_symbol(const char *s)
355114402Sru{
356114402Sru  int len = strlen(s);
357114402Sru  if (col > 0 && col + len + need_space > max_line_length) {
358114402Sru    putc('\n', fp);
359114402Sru    col = 0;
360114402Sru    need_space = 0;
361114402Sru  }
362114402Sru  if (need_space) {
363114402Sru    putc(' ', fp);
364114402Sru    col++;
365114402Sru  }
366114402Sru  fputs(s, fp);
367114402Sru  col += len;
368114402Sru  need_space = 1;
369114402Sru  return *this;
370114402Sru}
371114402Sru
372114402Srups_output &ps_output::put_color(unsigned int c)
373114402Sru{
374114402Sru  char buf[128];
375114402Sru  sprintf(buf, "%.3g", double(c) / color::MAX_COLOR_VAL);
376114402Sru  int len = strlen(buf);
377114402Sru  if (col > 0 && col + len + need_space > max_line_length) {
378114402Sru    putc('\n', fp);
379114402Sru    col = 0;
380114402Sru    need_space = 0;
381114402Sru  }
382114402Sru  if (need_space) {
383114402Sru    putc(' ', fp);
384114402Sru    col++;
385114402Sru  }
386114402Sru  fputs(buf, fp);
387114402Sru  col += len;
388114402Sru  need_space = 1;
389114402Sru  return *this;
390114402Sru}
391114402Sru
392114402Srups_output &ps_output::put_literal_symbol(const char *s)
393114402Sru{
394114402Sru  int len = strlen(s);
395114402Sru  if (col > 0 && col + len + 1 > max_line_length) {
396114402Sru    putc('\n', fp);
397114402Sru    col = 0;
398114402Sru  }
399114402Sru  putc('/', fp);
400114402Sru  fputs(s, fp);
401114402Sru  col += len + 1;
402114402Sru  need_space = 1;
403114402Sru  return *this;
404114402Sru}
405114402Sru
406114402Sruclass ps_font : public font {
407114402Sru  ps_font(const char *);
408114402Srupublic:
409114402Sru  int encoding_index;
410114402Sru  char *encoding;
411114402Sru  char *reencoded_name;
412114402Sru  ~ps_font();
413114402Sru  void handle_unknown_font_command(const char *command, const char *arg,
414114402Sru				   const char *filename, int lineno);
415114402Sru  static ps_font *load_ps_font(const char *);
416114402Sru};
417114402Sru
418114402Srups_font *ps_font::load_ps_font(const char *s)
419114402Sru{
420114402Sru  ps_font *f = new ps_font(s);
421114402Sru  if (!f->load()) {
422114402Sru    delete f;
423114402Sru    return 0;
424114402Sru  }
425114402Sru  return f;
426114402Sru}
427114402Sru
428114402Srups_font::ps_font(const char *nm)
429114402Sru: font(nm), encoding_index(-1), encoding(0), reencoded_name(0)
430114402Sru{
431114402Sru}
432114402Sru
433114402Srups_font::~ps_font()
434114402Sru{
435114402Sru  a_delete encoding;
436114402Sru  a_delete reencoded_name;
437114402Sru}
438114402Sru
439114402Sruvoid ps_font::handle_unknown_font_command(const char *command, const char *arg,
440114402Sru					  const char *filename, int lineno)
441114402Sru{
442114402Sru  if (strcmp(command, "encoding") == 0) {
443114402Sru    if (arg == 0)
444114402Sru      error_with_file_and_line(filename, lineno,
445114402Sru			       "`encoding' command requires an argument");
446114402Sru    else
447114402Sru      encoding = strsave(arg);
448114402Sru  }
449114402Sru}
450114402Sru
451114402Srustatic void handle_unknown_desc_command(const char *command, const char *arg,
452114402Sru					const char *filename, int lineno)
453114402Sru{
454114402Sru  if (strcmp(command, "broken") == 0) {
455114402Sru    if (arg == 0)
456114402Sru      error_with_file_and_line(filename, lineno,
457114402Sru			       "`broken' command requires an argument");
458114402Sru    else if (!bflag)
459114402Sru      broken_flags = atoi(arg);
460114402Sru  }
461114402Sru}
462114402Sru
463114402Srustruct subencoding {
464114402Sru  font *p;
465114402Sru  unsigned int num;
466114402Sru  int idx;
467114402Sru  char *subfont;
468114402Sru  const char *glyphs[256];
469114402Sru  subencoding *next;
470114402Sru
471114402Sru  subencoding(font *, unsigned int, int, subencoding *);
472114402Sru  ~subencoding();
473114402Sru};
474114402Sru
475114402Srusubencoding::subencoding(font *f, unsigned int n, int ix, subencoding *s)
476114402Sru: p(f), num(n), idx(ix), subfont(0), next(s)
477114402Sru{
478114402Sru  for (int i = 0; i < 256; i++)
479114402Sru    glyphs[i] = 0;
480114402Sru}
481114402Sru
482114402Srusubencoding::~subencoding()
483114402Sru{
484114402Sru  a_delete subfont;
485114402Sru}
486114402Sru
487114402Srustruct style {
488114402Sru  font *f;
489114402Sru  subencoding *sub;
490114402Sru  int point_size;
491114402Sru  int height;
492114402Sru  int slant;
493114402Sru  style();
494114402Sru  style(font *, subencoding *, int, int, int);
495114402Sru  int operator==(const style &) const;
496114402Sru  int operator!=(const style &) const;
497114402Sru};
498114402Sru
499114402Srustyle::style() : f(0)
500114402Sru{
501114402Sru}
502114402Sru
503114402Srustyle::style(font *p, subencoding *s, int sz, int h, int sl)
504114402Sru: f(p), sub(s), point_size(sz), height(h), slant(sl)
505114402Sru{
506114402Sru}
507114402Sru
508114402Sruint style::operator==(const style &s) const
509114402Sru{
510114402Sru  return (f == s.f
511114402Sru	  && sub == s.sub
512114402Sru	  && point_size == s.point_size
513114402Sru	  && height == s.height
514114402Sru	  && slant == s.slant);
515114402Sru}
516114402Sru
517114402Sruint style::operator!=(const style &s) const
518114402Sru{
519114402Sru  return !(*this == s);
520114402Sru}
521114402Sru
522114402Sruclass ps_printer : public printer {
523114402Sru  FILE *tempfp;
524114402Sru  ps_output out;
525114402Sru  int res;
526114402Sru  int space_char_index;
527114402Sru  int pages_output;
528114402Sru  int paper_length;
529114402Sru  int equalise_spaces;
530114402Sru  enum { SBUF_SIZE = 256 };
531114402Sru  char sbuf[SBUF_SIZE];
532114402Sru  int sbuf_len;
533114402Sru  int sbuf_start_hpos;
534114402Sru  int sbuf_vpos;
535114402Sru  int sbuf_end_hpos;
536114402Sru  int sbuf_space_width;
537114402Sru  int sbuf_space_count;
538114402Sru  int sbuf_space_diff_count;
539114402Sru  int sbuf_space_code;
540114402Sru  int sbuf_kern;
541114402Sru  style sbuf_style;
542114402Sru  color sbuf_color;		// the current PS color
543114402Sru  style output_style;
544114402Sru  subencoding *subencodings;
545114402Sru  int output_hpos;
546114402Sru  int output_vpos;
547114402Sru  int output_draw_point_size;
548114402Sru  int line_thickness;
549114402Sru  int output_line_thickness;
550114402Sru  unsigned char output_space_code;
551114402Sru  enum { MAX_DEFINED_STYLES = 50 };
552114402Sru  style defined_styles[MAX_DEFINED_STYLES];
553114402Sru  int ndefined_styles;
554114402Sru  int next_encoding_index;
555114402Sru  int next_subencoding_index;
556114402Sru  string defs;
557114402Sru  int ndefs;
558114402Sru  resource_manager rm;
559114402Sru  int invis_count;
560114402Sru
561114402Sru  void flush_sbuf();
562114402Sru  void set_style(const style &);
563114402Sru  void set_space_code(unsigned char c);
564114402Sru  int set_encoding_index(ps_font *);
565114402Sru  subencoding *set_subencoding(font *, int, unsigned char *);
566114402Sru  char *get_subfont(subencoding *, const char *);
567114402Sru  void do_exec(char *, const environment *);
568114402Sru  void do_import(char *, const environment *);
569114402Sru  void do_def(char *, const environment *);
570114402Sru  void do_mdef(char *, const environment *);
571114402Sru  void do_file(char *, const environment *);
572114402Sru  void do_invis(char *, const environment *);
573114402Sru  void do_endinvis(char *, const environment *);
574114402Sru  void set_line_thickness_and_color(const environment *);
575114402Sru  void fill_path(const environment *);
576114402Sru  void encode_fonts();
577114402Sru  void encode_subfont(subencoding *);
578114402Sru  void define_encoding(const char *, int);
579114402Sru  void reencode_font(ps_font *);
580114402Sru  void set_color(color *c, int fill = 0);
581114402Sru
582114402Sru  const char *media_name();
583114402Sru  int media_width();
584114402Sru  int media_height();
585114402Sru  void media_set();
586114402Sru
587114402Srupublic:
588114402Sru  ps_printer(double);
589114402Sru  ~ps_printer();
590114402Sru  void set_char(int i, font *f, const environment *env, int w,
591114402Sru		const char *name);
592114402Sru  void draw(int code, int *p, int np, const environment *env);
593114402Sru  void begin_page(int);
594114402Sru  void end_page(int);
595114402Sru  void special(char *arg, const environment *env, char type);
596114402Sru  font *make_font(const char *);
597114402Sru  void end_of_line();
598114402Sru};
599114402Sru
600114402Sru// `pl' is in inches
601114402Srups_printer::ps_printer(double pl)
602114402Sru: out(0, MAX_LINE_LENGTH),
603114402Sru  pages_output(0),
604114402Sru  sbuf_len(0),
605114402Sru  subencodings(0),
606114402Sru  output_hpos(-1),
607114402Sru  output_vpos(-1),
608114402Sru  line_thickness(-1),
609114402Sru  ndefined_styles(0),
610114402Sru  next_encoding_index(0),
611114402Sru  next_subencoding_index(0),
612114402Sru  ndefs(0),
613114402Sru  invis_count(0)
614114402Sru{
615114402Sru  tempfp = xtmpfile();
616114402Sru  out.set_file(tempfp);
617114402Sru  if (linewidth < 0)
618114402Sru    linewidth = DEFAULT_LINEWIDTH;
619114402Sru  if (font::hor != 1)
620114402Sru    fatal("horizontal resolution must be 1");
621114402Sru  if (font::vert != 1)
622114402Sru    fatal("vertical resolution must be 1");
623114402Sru  if (font::res % (font::sizescale*72) != 0)
624114402Sru    fatal("res must be a multiple of 72*sizescale");
625114402Sru  int r = font::res;
626114402Sru  int point = 0;
627114402Sru  while (r % 10 == 0) {
628114402Sru    r /= 10;
629114402Sru    point++;
630114402Sru  }
631114402Sru  res = r;
632114402Sru  out.set_fixed_point(point);
633114402Sru  space_char_index = font::name_to_index("space");
634114402Sru  if (pl == 0)
635114402Sru    paper_length = font::paperlength;
636114402Sru  else
637114402Sru    paper_length = int(pl * font::res + 0.5);
638114402Sru  if (paper_length == 0)
639114402Sru    paper_length = 11 * font::res;
640114402Sru  equalise_spaces = font::res >= 72000;
641114402Sru}
642114402Sru
643114402Sruint ps_printer::set_encoding_index(ps_font *f)
644114402Sru{
645114402Sru  if (f->encoding_index >= 0)
646114402Sru    return f->encoding_index;
647114402Sru  for (font_pointer_list *p = font_list; p; p = p->next)
648114402Sru    if (p->p != f) {
649114402Sru      char *encoding = ((ps_font *)p->p)->encoding;
650114402Sru      int encoding_index = ((ps_font *)p->p)->encoding_index;
651114402Sru      if (encoding != 0 && encoding_index >= 0
652114402Sru	  && strcmp(f->encoding, encoding) == 0) {
653114402Sru	return f->encoding_index = encoding_index;
654114402Sru      }
655114402Sru    }
656114402Sru  return f->encoding_index = next_encoding_index++;
657114402Sru}
658114402Sru
659114402Srusubencoding *ps_printer::set_subencoding(font *f, int i, unsigned char *codep)
660114402Sru{
661114402Sru  unsigned int idx = f->get_code(i);
662114402Sru  *codep = idx % 256;
663114402Sru  unsigned int num = idx >> 8;
664114402Sru  if (num == 0)
665114402Sru    return 0;
666114402Sru  subencoding *p = 0;
667114402Sru  for (p = subencodings; p; p = p->next)
668114402Sru    if (p->p == f && p->num == num)
669114402Sru      break;
670114402Sru  if (p == 0)
671114402Sru    p = subencodings = new subencoding(f, num, next_subencoding_index++,
672114402Sru				       subencodings);
673114402Sru  p->glyphs[*codep] = f->get_special_device_encoding(i);
674114402Sru  return p;
675114402Sru}
676114402Sru
677114402Sruchar *ps_printer::get_subfont(subencoding *sub, const char *stem)
678114402Sru{
679114402Sru  assert(sub != 0);
680114402Sru  if (!sub->subfont) {
681114402Sru    char *tem = new char[strlen(stem) + 2 + INT_DIGITS + 1];
682114402Sru    sprintf(tem, "%s@@%d", stem, next_subencoding_index);
683114402Sru    sub->subfont = tem;
684114402Sru  }
685114402Sru  return sub->subfont;
686114402Sru}
687114402Sru
688114402Sruvoid ps_printer::set_char(int i, font *f, const environment *env, int w,
689114402Sru			  const char *)
690114402Sru{
691114402Sru  if (i == space_char_index || invis_count > 0)
692114402Sru    return;
693114402Sru  unsigned char code;
694114402Sru  subencoding *sub = set_subencoding(f, i, &code);
695114402Sru  style sty(f, sub, env->size, env->height, env->slant);
696114402Sru  if (sty.slant != 0) {
697114402Sru    if (sty.slant > 80 || sty.slant < -80) {
698114402Sru      error("silly slant `%1' degrees", sty.slant);
699114402Sru      sty.slant = 0;
700114402Sru    }
701114402Sru  }
702114402Sru  if (sbuf_len > 0) {
703114402Sru    if (sbuf_len < SBUF_SIZE
704114402Sru	&& sty == sbuf_style
705114402Sru	&& sbuf_vpos == env->vpos
706114402Sru	&& sbuf_color == *env->col) {
707114402Sru      if (sbuf_end_hpos == env->hpos) {
708114402Sru	sbuf[sbuf_len++] = code;
709114402Sru	sbuf_end_hpos += w + sbuf_kern;
710114402Sru	return;
711114402Sru      }
712114402Sru      if (sbuf_len == 1 && sbuf_kern == 0) {
713114402Sru	sbuf_kern = env->hpos - sbuf_end_hpos;
714114402Sru	sbuf_end_hpos = env->hpos + sbuf_kern + w;
715114402Sru	sbuf[sbuf_len++] = code;
716114402Sru	return;
717114402Sru      }
718114402Sru      /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
719114402Sru	 starting a new string. */
720114402Sru      if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
721114402Sru	  && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
722114402Sru	if (sbuf_space_code < 0) {
723114402Sru	  if (f->contains(space_char_index)) {
724114402Sru	    sbuf_space_code = f->get_code(space_char_index);
725114402Sru	    sbuf_space_width = env->hpos - sbuf_end_hpos;
726114402Sru	    sbuf_end_hpos = env->hpos + w + sbuf_kern;
727114402Sru	    sbuf[sbuf_len++] = sbuf_space_code;
728114402Sru	    sbuf[sbuf_len++] = code;
729114402Sru	    sbuf_space_count++;
730114402Sru	    return;
731114402Sru	  }
732114402Sru	}
733114402Sru	else {
734114402Sru	  int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
735114402Sru	  if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) {
736114402Sru	    sbuf_end_hpos = env->hpos + w + sbuf_kern;
737114402Sru	    sbuf[sbuf_len++] = sbuf_space_code;
738114402Sru	    sbuf[sbuf_len++] = code;
739114402Sru	    sbuf_space_count++;
740114402Sru	    if (diff == 1)
741114402Sru	      sbuf_space_diff_count++;
742114402Sru	    else if (diff == -1)
743114402Sru	      sbuf_space_diff_count--;
744114402Sru	    return;
745114402Sru	  }
746114402Sru	}
747114402Sru      }
748114402Sru    }
749114402Sru    flush_sbuf();
750114402Sru  }
751114402Sru  sbuf_len = 1;
752114402Sru  sbuf[0] = code;
753114402Sru  sbuf_end_hpos = env->hpos + w;
754114402Sru  sbuf_start_hpos = env->hpos;
755114402Sru  sbuf_vpos = env->vpos;
756114402Sru  sbuf_style = sty;
757114402Sru  sbuf_space_code = -1;
758114402Sru  sbuf_space_width = 0;
759114402Sru  sbuf_space_count = sbuf_space_diff_count = 0;
760114402Sru  sbuf_kern = 0;
761114402Sru  if (sbuf_color != *env->col)
762114402Sru    set_color(env->col);
763114402Sru}
764114402Sru
765114402Srustatic char *make_encoding_name(int encoding_index)
766114402Sru{
767114402Sru  static char buf[3 + INT_DIGITS + 1];
768114402Sru  sprintf(buf, "ENC%d", encoding_index);
769114402Sru  return buf;
770114402Sru}
771114402Sru
772114402Srustatic char *make_subencoding_name(int subencoding_index)
773114402Sru{
774114402Sru  static char buf[6 + INT_DIGITS + 1];
775114402Sru  sprintf(buf, "SUBENC%d", subencoding_index);
776114402Sru  return buf;
777114402Sru}
778114402Sru
779114402Sruconst char *const WS = " \t\n\r";
780114402Sru
781114402Sruvoid ps_printer::define_encoding(const char *encoding, int encoding_index)
782114402Sru{
783114402Sru  char *vec[256];
784114402Sru  int i;
785114402Sru  for (i = 0; i < 256; i++)
786114402Sru    vec[i] = 0;
787114402Sru  char *path;
788114402Sru  FILE *fp = font::open_file(encoding, &path);
789114402Sru  if (fp == 0)
790114402Sru    fatal("can't open encoding file `%1'", encoding);
791114402Sru  int lineno = 1;
792114402Sru  const int BUFFER_SIZE = 512;
793114402Sru  char buf[BUFFER_SIZE];
794114402Sru  while (fgets(buf, BUFFER_SIZE, fp) != 0) {
795114402Sru    char *p = buf;
796114402Sru    while (csspace(*p))
797114402Sru      p++;
798114402Sru    if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) {
799114402Sru      char *q = strtok(0, WS);
800151497Sru      int n = 0;		// pacify compiler
801114402Sru      if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256)
802114402Sru	fatal_with_file_and_line(path, lineno, "bad second field");
803114402Sru      vec[n] = new char[strlen(p) + 1];
804114402Sru      strcpy(vec[n], p);
805114402Sru    }
806114402Sru    lineno++;
807114402Sru  }
808114402Sru  a_delete path;
809114402Sru  out.put_literal_symbol(make_encoding_name(encoding_index))
810114402Sru     .put_delimiter('[');
811114402Sru  for (i = 0; i < 256; i++) {
812114402Sru    if (vec[i] == 0)
813114402Sru      out.put_literal_symbol(".notdef");
814114402Sru    else {
815114402Sru      out.put_literal_symbol(vec[i]);
816114402Sru      a_delete vec[i];
817114402Sru    }
818114402Sru  }
819114402Sru  out.put_delimiter(']')
820114402Sru     .put_symbol("def");
821114402Sru  fclose(fp);
822114402Sru}
823114402Sru
824114402Sruvoid ps_printer::reencode_font(ps_font *f)
825114402Sru{
826114402Sru  out.put_literal_symbol(f->reencoded_name)
827114402Sru     .put_symbol(make_encoding_name(f->encoding_index))
828114402Sru     .put_literal_symbol(f->get_internal_name())
829114402Sru     .put_symbol("RE");
830114402Sru}
831114402Sru
832114402Sruvoid ps_printer::encode_fonts()
833114402Sru{
834114402Sru  if (next_encoding_index == 0)
835114402Sru    return;
836114402Sru  char *done_encoding = new char[next_encoding_index];
837114402Sru  for (int i = 0; i < next_encoding_index; i++)
838114402Sru    done_encoding[i] = 0;
839114402Sru  for (font_pointer_list *f = font_list; f; f = f->next) {
840114402Sru    int encoding_index = ((ps_font *)f->p)->encoding_index;
841114402Sru    if (encoding_index >= 0) {
842114402Sru      assert(encoding_index < next_encoding_index);
843114402Sru      if (!done_encoding[encoding_index]) {
844114402Sru	done_encoding[encoding_index] = 1;
845114402Sru	define_encoding(((ps_font *)f->p)->encoding, encoding_index);
846114402Sru      }
847114402Sru      reencode_font((ps_font *)f->p);
848114402Sru    }
849114402Sru  }
850114402Sru  a_delete done_encoding;
851114402Sru}
852114402Sru
853114402Sruvoid ps_printer::encode_subfont(subencoding *sub)
854114402Sru{
855114402Sru  assert(sub->glyphs != 0);
856114402Sru  out.put_literal_symbol(make_subencoding_name(sub->idx))
857114402Sru     .put_delimiter('[');
858114402Sru  for (int i = 0; i < 256; i++)
859114402Sru  {
860114402Sru    if (sub->glyphs[i])
861114402Sru      out.put_literal_symbol(sub->glyphs[i]);
862114402Sru    else
863114402Sru      out.put_literal_symbol(".notdef");
864114402Sru  }
865114402Sru  out.put_delimiter(']')
866114402Sru     .put_symbol("def");
867114402Sru}
868114402Sru
869114402Sruvoid ps_printer::set_style(const style &sty)
870114402Sru{
871114402Sru  char buf[1 + INT_DIGITS + 1];
872114402Sru  for (int i = 0; i < ndefined_styles; i++)
873114402Sru    if (sty == defined_styles[i]) {
874114402Sru      sprintf(buf, "F%d", i);
875114402Sru      out.put_symbol(buf);
876114402Sru      return;
877114402Sru    }
878114402Sru  if (ndefined_styles >= MAX_DEFINED_STYLES)
879114402Sru    ndefined_styles = 0;
880114402Sru  sprintf(buf, "F%d", ndefined_styles);
881114402Sru  out.put_literal_symbol(buf);
882114402Sru  const char *psname = sty.f->get_internal_name();
883114402Sru  if (psname == 0)
884114402Sru    fatal("no internalname specified for font `%1'", sty.f->get_name());
885114402Sru  char *encoding = ((ps_font *)sty.f)->encoding;
886114402Sru  if (sty.sub == 0) {
887114402Sru    if (encoding != 0) {
888114402Sru      char *s = ((ps_font *)sty.f)->reencoded_name;
889114402Sru      if (s == 0) {
890114402Sru	int ei = set_encoding_index((ps_font *)sty.f);
891114402Sru	char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1];
892114402Sru	sprintf(tem, "%s@%d", psname, ei);
893114402Sru	psname = tem;
894114402Sru	((ps_font *)sty.f)->reencoded_name = tem;
895114402Sru      }
896114402Sru      else
897114402Sru        psname = s;
898114402Sru    }
899114402Sru  }
900114402Sru  else
901114402Sru    psname = get_subfont(sty.sub, psname);
902114402Sru  out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size);
903114402Sru  if (sty.height != 0 || sty.slant != 0) {
904114402Sru    int h = sty.height == 0 ? sty.point_size : sty.height;
905114402Sru    h *= font::res/(72*font::sizescale);
906114402Sru    int c = int(h*tan(radians(sty.slant)) + .5);
907114402Sru    out.put_fix_number(c)
908114402Sru       .put_fix_number(h)
909114402Sru       .put_literal_symbol(psname)
910114402Sru       .put_symbol("MF");
911114402Sru  }
912114402Sru  else {
913114402Sru    out.put_literal_symbol(psname)
914114402Sru       .put_symbol("SF");
915114402Sru  }
916114402Sru  defined_styles[ndefined_styles++] = sty;
917114402Sru}
918114402Sru
919114402Sruvoid ps_printer::set_color(color *col, int fill)
920114402Sru{
921114402Sru  sbuf_color = *col;
922114402Sru  unsigned int components[4];
923114402Sru  char s[3];
924114402Sru  color_scheme cs = col->get_components(components);
925114402Sru  s[0] = fill ? 'F' : 'C';
926114402Sru  s[2] = 0;
927114402Sru  switch (cs) {
928114402Sru  case DEFAULT:			// black
929114402Sru    out.put_symbol("0");
930114402Sru    s[1] = 'g';
931114402Sru    break;
932114402Sru  case RGB:
933114402Sru    out.put_color(Red)
934114402Sru       .put_color(Green)
935114402Sru       .put_color(Blue);
936114402Sru    s[1] = 'r';
937114402Sru    break;
938114402Sru  case CMY:
939114402Sru    col->get_cmyk(&Cyan, &Magenta, &Yellow, &Black);
940114402Sru    // fall through
941114402Sru  case CMYK:
942114402Sru    out.put_color(Cyan)
943114402Sru       .put_color(Magenta)
944114402Sru       .put_color(Yellow)
945114402Sru       .put_color(Black);
946114402Sru    s[1] = 'k';
947114402Sru    cmyk_flag = 1;
948114402Sru    break;
949114402Sru  case GRAY:
950114402Sru    out.put_color(Gray);
951114402Sru    s[1] = 'g';
952114402Sru    break;
953114402Sru  }
954114402Sru  out.put_symbol(s);
955114402Sru}
956114402Sru
957114402Sruvoid ps_printer::set_space_code(unsigned char c)
958114402Sru{
959114402Sru  out.put_literal_symbol("SC")
960114402Sru     .put_number(c)
961114402Sru     .put_symbol("def");
962114402Sru}
963114402Sru
964114402Sruvoid ps_printer::end_of_line()
965114402Sru{
966114402Sru  flush_sbuf();
967114402Sru  // this ensures that we do an absolute motion to the beginning of a line
968114402Sru  output_vpos = output_hpos = -1;
969114402Sru}
970114402Sru
971114402Sruvoid ps_printer::flush_sbuf()
972114402Sru{
973114402Sru  enum {
974114402Sru    NONE,
975114402Sru    RELATIVE_H,
976114402Sru    RELATIVE_V,
977114402Sru    RELATIVE_HV,
978114402Sru    ABSOLUTE
979114402Sru    } motion = NONE;
980114402Sru  int space_flag = 0;
981114402Sru  if (sbuf_len == 0)
982114402Sru    return;
983114402Sru  if (output_style != sbuf_style) {
984114402Sru    set_style(sbuf_style);
985114402Sru    output_style = sbuf_style;
986114402Sru  }
987114402Sru  int extra_space = 0;
988114402Sru  if (output_hpos < 0 || output_vpos < 0)
989114402Sru    motion = ABSOLUTE;
990114402Sru  else {
991114402Sru    if (output_hpos != sbuf_start_hpos)
992114402Sru      motion = RELATIVE_H;
993114402Sru    if (output_vpos != sbuf_vpos) {
994114402Sru      if  (motion != NONE)
995114402Sru	motion = RELATIVE_HV;
996114402Sru      else
997114402Sru	motion = RELATIVE_V;
998114402Sru    }
999114402Sru  }
1000114402Sru  if (sbuf_space_code >= 0) {
1001114402Sru    int w = sbuf_style.f->get_width(space_char_index, sbuf_style.point_size);
1002114402Sru    if (w + sbuf_kern != sbuf_space_width) {
1003114402Sru      if (sbuf_space_code != output_space_code) {
1004114402Sru	set_space_code(sbuf_space_code);
1005114402Sru	output_space_code = sbuf_space_code;
1006114402Sru      }
1007114402Sru      space_flag = 1;
1008114402Sru      extra_space = sbuf_space_width - w - sbuf_kern;
1009114402Sru      if (sbuf_space_diff_count > sbuf_space_count/2)
1010114402Sru	extra_space++;
1011114402Sru      else if (sbuf_space_diff_count < -(sbuf_space_count/2))
1012114402Sru	extra_space--;
1013114402Sru    }
1014114402Sru  }
1015114402Sru  if (space_flag)
1016114402Sru    out.put_fix_number(extra_space);
1017114402Sru  if (sbuf_kern != 0)
1018114402Sru    out.put_fix_number(sbuf_kern);
1019114402Sru  out.put_string(sbuf, sbuf_len);
1020114402Sru  char command_array[] = {'A', 'B', 'C', 'D',
1021114402Sru			  'E', 'F', 'G', 'H',
1022114402Sru			  'I', 'J', 'K', 'L',
1023114402Sru			  'M', 'N', 'O', 'P',
1024114402Sru			  'Q', 'R', 'S', 'T'};
1025114402Sru  char sym[2];
1026114402Sru  sym[0] = command_array[motion*4 + space_flag + 2*(sbuf_kern != 0)];
1027114402Sru  sym[1] = '\0';
1028114402Sru  switch (motion) {
1029114402Sru  case NONE:
1030114402Sru    break;
1031114402Sru  case ABSOLUTE:
1032114402Sru    out.put_fix_number(sbuf_start_hpos)
1033114402Sru       .put_fix_number(sbuf_vpos);
1034114402Sru    break;
1035114402Sru  case RELATIVE_H:
1036114402Sru    out.put_fix_number(sbuf_start_hpos - output_hpos);
1037114402Sru    break;
1038114402Sru  case RELATIVE_V:
1039114402Sru    out.put_fix_number(sbuf_vpos - output_vpos);
1040114402Sru    break;
1041114402Sru  case RELATIVE_HV:
1042114402Sru    out.put_fix_number(sbuf_start_hpos - output_hpos)
1043114402Sru       .put_fix_number(sbuf_vpos - output_vpos);
1044114402Sru    break;
1045114402Sru  default:
1046114402Sru    assert(0);
1047114402Sru  }
1048114402Sru  out.put_symbol(sym);
1049114402Sru  output_hpos = sbuf_end_hpos;
1050114402Sru  output_vpos = sbuf_vpos;
1051114402Sru  sbuf_len = 0;
1052114402Sru}
1053114402Sru
1054114402Sruvoid ps_printer::set_line_thickness_and_color(const environment *env)
1055114402Sru{
1056114402Sru  if (line_thickness < 0) {
1057114402Sru    if (output_draw_point_size != env->size) {
1058114402Sru      // we ought to check for overflow here
1059114402Sru      int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
1060114402Sru      out.put_fix_number(lw)
1061114402Sru	 .put_symbol("LW");
1062114402Sru      output_draw_point_size = env->size;
1063114402Sru      output_line_thickness = -1;
1064114402Sru    }
1065114402Sru  }
1066114402Sru  else {
1067114402Sru    if (output_line_thickness != line_thickness) {
1068114402Sru      out.put_fix_number(line_thickness)
1069114402Sru	 .put_symbol("LW");
1070114402Sru      output_line_thickness = line_thickness;
1071114402Sru      output_draw_point_size = -1;
1072114402Sru    }
1073114402Sru  }
1074114402Sru  if (sbuf_color != *env->col)
1075114402Sru    set_color(env->col);
1076114402Sru}
1077114402Sru
1078114402Sruvoid ps_printer::fill_path(const environment *env)
1079114402Sru{
1080114402Sru  if (sbuf_color == *env->fill)
1081114402Sru    out.put_symbol("FL");
1082114402Sru  else
1083114402Sru    set_color(env->fill, 1);
1084114402Sru}
1085114402Sru
1086114402Sruvoid ps_printer::draw(int code, int *p, int np, const environment *env)
1087114402Sru{
1088114402Sru  if (invis_count > 0)
1089114402Sru    return;
1090114402Sru  flush_sbuf();
1091114402Sru  int fill_flag = 0;
1092114402Sru  switch (code) {
1093114402Sru  case 'C':
1094114402Sru    fill_flag = 1;
1095114402Sru    // fall through
1096114402Sru  case 'c':
1097114402Sru    // troff adds an extra argument to C
1098114402Sru    if (np != 1 && !(code == 'C' && np == 2)) {
1099114402Sru      error("1 argument required for circle");
1100114402Sru      break;
1101114402Sru    }
1102114402Sru    out.put_fix_number(env->hpos + p[0]/2)
1103114402Sru       .put_fix_number(env->vpos)
1104114402Sru       .put_fix_number(p[0]/2)
1105114402Sru       .put_symbol("DC");
1106114402Sru    if (fill_flag)
1107114402Sru      fill_path(env);
1108114402Sru    else {
1109114402Sru      set_line_thickness_and_color(env);
1110114402Sru      out.put_symbol("ST");
1111114402Sru    }
1112114402Sru    break;
1113114402Sru  case 'l':
1114114402Sru    if (np != 2) {
1115114402Sru      error("2 arguments required for line");
1116114402Sru      break;
1117114402Sru    }
1118114402Sru    set_line_thickness_and_color(env);
1119114402Sru    out.put_fix_number(p[0] + env->hpos)
1120114402Sru       .put_fix_number(p[1] + env->vpos)
1121114402Sru       .put_fix_number(env->hpos)
1122114402Sru       .put_fix_number(env->vpos)
1123114402Sru       .put_symbol("DL");
1124114402Sru    break;
1125114402Sru  case 'E':
1126114402Sru    fill_flag = 1;
1127114402Sru    // fall through
1128114402Sru  case 'e':
1129114402Sru    if (np != 2) {
1130114402Sru      error("2 arguments required for ellipse");
1131114402Sru      break;
1132114402Sru    }
1133114402Sru    out.put_fix_number(p[0])
1134114402Sru       .put_fix_number(p[1])
1135114402Sru       .put_fix_number(env->hpos + p[0]/2)
1136114402Sru       .put_fix_number(env->vpos)
1137114402Sru       .put_symbol("DE");
1138114402Sru    if (fill_flag)
1139114402Sru      fill_path(env);
1140114402Sru    else {
1141114402Sru      set_line_thickness_and_color(env);
1142114402Sru      out.put_symbol("ST");
1143114402Sru    }
1144114402Sru    break;
1145114402Sru  case 'P':
1146114402Sru    fill_flag = 1;
1147114402Sru    // fall through
1148114402Sru  case 'p':
1149114402Sru    {
1150114402Sru      if (np & 1) {
1151114402Sru	error("even number of arguments required for polygon");
1152114402Sru	break;
1153114402Sru      }
1154114402Sru      if (np == 0) {
1155114402Sru	error("no arguments for polygon");
1156114402Sru	break;
1157114402Sru      }
1158114402Sru      out.put_fix_number(env->hpos)
1159114402Sru	 .put_fix_number(env->vpos)
1160114402Sru	 .put_symbol("MT");
1161114402Sru      for (int i = 0; i < np; i += 2)
1162114402Sru	out.put_fix_number(p[i])
1163114402Sru	   .put_fix_number(p[i+1])
1164114402Sru	   .put_symbol("RL");
1165114402Sru      out.put_symbol("CL");
1166114402Sru      if (fill_flag)
1167114402Sru	fill_path(env);
1168114402Sru      else {
1169114402Sru	set_line_thickness_and_color(env);
1170114402Sru	out.put_symbol("ST");
1171114402Sru      }
1172114402Sru      break;
1173114402Sru    }
1174114402Sru  case '~':
1175114402Sru    {
1176114402Sru      if (np & 1) {
1177114402Sru	error("even number of arguments required for spline");
1178114402Sru	break;
1179114402Sru      }
1180114402Sru      if (np == 0) {
1181114402Sru	error("no arguments for spline");
1182114402Sru	break;
1183114402Sru      }
1184114402Sru      out.put_fix_number(env->hpos)
1185114402Sru	 .put_fix_number(env->vpos)
1186114402Sru	 .put_symbol("MT");
1187114402Sru      out.put_fix_number(p[0]/2)
1188114402Sru	 .put_fix_number(p[1]/2)
1189114402Sru	 .put_symbol("RL");
1190114402Sru      /* tnum/tden should be between 0 and 1; the closer it is to 1
1191114402Sru	 the tighter the curve will be to the guiding lines; 2/3
1192114402Sru	 is the standard value */
1193114402Sru      const int tnum = 2;
1194114402Sru      const int tden = 3;
1195114402Sru      for (int i = 0; i < np - 2; i += 2) {
1196114402Sru	out.put_fix_number((p[i]*tnum)/(2*tden))
1197114402Sru	   .put_fix_number((p[i + 1]*tnum)/(2*tden))
1198114402Sru	   .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden))
1199114402Sru	   .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden))
1200114402Sru	   .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2)
1201114402Sru	   .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2)
1202114402Sru	   .put_symbol("RC");
1203114402Sru      }
1204114402Sru      out.put_fix_number(p[np - 2] - p[np - 2]/2)
1205114402Sru	 .put_fix_number(p[np - 1] - p[np - 1]/2)
1206114402Sru	 .put_symbol("RL");
1207114402Sru      set_line_thickness_and_color(env);
1208114402Sru      out.put_symbol("ST");
1209114402Sru    }
1210114402Sru    break;
1211114402Sru  case 'a':
1212114402Sru    {
1213114402Sru      if (np != 4) {
1214114402Sru	error("4 arguments required for arc");
1215114402Sru	break;
1216114402Sru      }
1217114402Sru      set_line_thickness_and_color(env);
1218114402Sru      double c[2];
1219114402Sru      if (adjust_arc_center(p, c))
1220114402Sru	out.put_fix_number(env->hpos + int(c[0]))
1221114402Sru	   .put_fix_number(env->vpos + int(c[1]))
1222114402Sru	   .put_fix_number(int(sqrt(c[0]*c[0] + c[1]*c[1])))
1223114402Sru	   .put_float(degrees(atan2(-c[1], -c[0])))
1224114402Sru	   .put_float(degrees(atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])))
1225114402Sru	   .put_symbol("DA");
1226114402Sru      else
1227114402Sru	out.put_fix_number(p[0] + p[2] + env->hpos)
1228114402Sru	   .put_fix_number(p[1] + p[3] + env->vpos)
1229114402Sru	   .put_fix_number(env->hpos)
1230114402Sru	   .put_fix_number(env->vpos)
1231114402Sru	   .put_symbol("DL");
1232114402Sru    }
1233114402Sru    break;
1234114402Sru  case 't':
1235114402Sru    if (np == 0)
1236114402Sru      line_thickness = -1;
1237114402Sru    else {
1238114402Sru      // troff gratuitously adds an extra 0
1239114402Sru      if (np != 1 && np != 2) {
1240114402Sru	error("0 or 1 argument required for thickness");
1241114402Sru	break;
1242114402Sru      }
1243114402Sru      line_thickness = p[0];
1244114402Sru    }
1245114402Sru    break;
1246114402Sru  default:
1247114402Sru    error("unrecognised drawing command `%1'", char(code));
1248114402Sru    break;
1249114402Sru  }
1250114402Sru  output_hpos = output_vpos = -1;
1251114402Sru}
1252114402Sru
1253114402Sruconst char *ps_printer::media_name()
1254114402Sru{
1255114402Sru  return "Default";
1256114402Sru}
1257114402Sru
1258114402Sruint ps_printer::media_width()
1259114402Sru{
1260114402Sru  /*
1261114402Sru   *  NOTE:
1262114402Sru   *  Although paper size is defined as real numbers, it seems to be
1263114402Sru   *  a common convention to round to the nearest postscript unit.
1264114402Sru   *  For example, a4 is really 595.276 by 841.89 but we use 595 by 842.
1265114402Sru   *
1266114402Sru   *  This is probably a good compromise, especially since the
1267114402Sru   *  Postscript definition specifies that media
1268114402Sru   *  matching should be done within a tolerance of 5 units.
1269114402Sru   */
1270114402Sru  return int(user_paper_width ? user_paper_width*72.0 + 0.5
1271114402Sru			      : font::paperwidth*72.0/font::res + 0.5);
1272114402Sru}
1273114402Sru
1274114402Sruint ps_printer::media_height()
1275114402Sru{
1276114402Sru  return int(user_paper_length ? user_paper_length*72.0 + 0.5
1277114402Sru			       : paper_length*72.0/font::res + 0.5);
1278114402Sru}
1279114402Sru
1280114402Sruvoid ps_printer::media_set()
1281114402Sru{
1282114402Sru  /*
1283114402Sru   *  The setpagedevice implies an erasepage and initgraphics, and
1284114402Sru   *  must thus precede any descriptions for a particular page.
1285114402Sru   *
1286114402Sru   *  NOTE:
1287114402Sru   *  This does not work with ps2pdf when there are included eps
1288114402Sru   *  segments that contain PageSize/setpagedevice.
1289114402Sru   *  This might be a bug in ghostscript -- must be investigated.
1290114402Sru   *  Using setpagedevice in an .eps is really the wrong concept, anyway.
1291114402Sru   *
1292114402Sru   *  NOTE:
1293114402Sru   *  For the future, this is really the place to insert other
1294114402Sru   *  media selection features, like:
1295114402Sru   *    MediaColor
1296114402Sru   *    MediaPosition
1297114402Sru   *    MediaType
1298114402Sru   *    MediaWeight
1299114402Sru   *    MediaClass
1300114402Sru   *    TraySwitch
1301114402Sru   *    ManualFeed
1302114402Sru   *    InsertSheet
1303114402Sru   *    Duplex
1304114402Sru   *    Collate
1305114402Sru   *    ProcessColorModel
1306114402Sru   *  etc.
1307114402Sru   */
1308114402Sru  if (!(broken_flags & (USE_PS_ADOBE_2_0|NO_PAPERSIZE))) {
1309114402Sru    out.begin_comment("BeginFeature:")
1310114402Sru       .comment_arg("*PageSize")
1311114402Sru       .comment_arg(media_name())
1312114402Sru       .end_comment();
1313114402Sru    int w = media_width();
1314114402Sru    int h = media_height();
1315114402Sru    if (w > 0 && h > 0)
1316114402Sru      // warning to user is done elsewhere
1317114402Sru      fprintf(out.get_file(),
1318114402Sru	      "<< /PageSize [ %d %d ] /ImagingBBox null >> setpagedevice\n",
1319114402Sru	      w, h);
1320114402Sru    out.simple_comment("EndFeature");
1321114402Sru  }
1322114402Sru}
1323114402Sru
1324114402Sruvoid ps_printer::begin_page(int n)
1325114402Sru{
1326114402Sru  out.begin_comment("Page:")
1327114402Sru     .comment_arg(i_to_a(n));
1328114402Sru  out.comment_arg(i_to_a(++pages_output))
1329114402Sru     .end_comment();
1330114402Sru  output_style.f = 0;
1331114402Sru  output_space_code = 32;
1332114402Sru  output_draw_point_size = -1;
1333114402Sru  output_line_thickness = -1;
1334114402Sru  output_hpos = output_vpos = -1;
1335114402Sru  ndefined_styles = 0;
1336114402Sru  out.simple_comment("BeginPageSetup");
1337114402Sru
1338114402Sru#if 0
1339114402Sru  /*
1340114402Sru   *  NOTE:
1341114402Sru   *  may decide to do this once per page
1342114402Sru   */
1343114402Sru  media_set();
1344114402Sru#endif
1345114402Sru
1346114402Sru  out.put_symbol("BP")
1347114402Sru     .simple_comment("EndPageSetup");
1348114402Sru  if (sbuf_color != default_color)
1349114402Sru    set_color(&sbuf_color);
1350114402Sru}
1351114402Sru
1352114402Sruvoid ps_printer::end_page(int)
1353114402Sru{
1354114402Sru  flush_sbuf();
1355114402Sru  set_color(&default_color);
1356114402Sru  out.put_symbol("EP");
1357114402Sru  if (invis_count != 0) {
1358114402Sru    error("missing `endinvis' command");
1359114402Sru    invis_count = 0;
1360114402Sru  }
1361114402Sru}
1362114402Sru
1363114402Srufont *ps_printer::make_font(const char *nm)
1364114402Sru{
1365114402Sru  return ps_font::load_ps_font(nm);
1366114402Sru}
1367114402Sru
1368114402Srups_printer::~ps_printer()
1369114402Sru{
1370114402Sru  out.simple_comment("Trailer")
1371114402Sru     .put_symbol("end")
1372114402Sru     .simple_comment("EOF");
1373114402Sru  if (fseek(tempfp, 0L, 0) < 0)
1374114402Sru    fatal("fseek on temporary file failed");
1375114402Sru  fputs("%!PS-Adobe-", stdout);
1376114402Sru  fputs((broken_flags & USE_PS_ADOBE_2_0) ? "2.0" : "3.0", stdout);
1377114402Sru  putchar('\n');
1378114402Sru  out.set_file(stdout);
1379114402Sru  if (cmyk_flag)
1380114402Sru    out.begin_comment("Extensions:")
1381114402Sru       .comment_arg("CMYK")
1382114402Sru       .end_comment();
1383114402Sru  out.begin_comment("Creator:")
1384114402Sru     .comment_arg("groff")
1385114402Sru     .comment_arg("version")
1386114402Sru     .comment_arg(Version_string)
1387114402Sru     .end_comment();
1388114402Sru  {
1389114402Sru    fputs("%%CreationDate: ", out.get_file());
1390114402Sru#ifdef LONG_FOR_TIME_T
1391114402Sru    long
1392114402Sru#else
1393114402Sru    time_t
1394114402Sru#endif
1395114402Sru    t = time(0);
1396114402Sru    fputs(ctime(&t), out.get_file());
1397114402Sru  }
1398114402Sru  for (font_pointer_list *f = font_list; f; f = f->next) {
1399114402Sru    ps_font *psf = (ps_font *)(f->p);
1400114402Sru    rm.need_font(psf->get_internal_name());
1401114402Sru  }
1402114402Sru  rm.print_header_comments(out);
1403114402Sru  out.begin_comment("Pages:")
1404114402Sru     .comment_arg(i_to_a(pages_output))
1405114402Sru     .end_comment();
1406114402Sru  out.begin_comment("PageOrder:")
1407114402Sru     .comment_arg("Ascend")
1408114402Sru     .end_comment();
1409114402Sru  if (!(broken_flags & NO_PAPERSIZE)) {
1410114402Sru    int w = media_width();
1411114402Sru    int h = media_height();
1412114402Sru    if (w > 0 && h > 0)
1413114402Sru      fprintf(out.get_file(),
1414114402Sru	      "%%%%DocumentMedia: %s %d %d %d %s %s\n",
1415114402Sru	      media_name(),			// tag name of media
1416114402Sru	      w,				// media width
1417114402Sru	      h,				// media height
1418114402Sru	      0,				// weight in g/m2
1419114402Sru	      "()",				// paper color, e.g. white
1420114402Sru	      "()"				// preprinted form type
1421114402Sru	     );
1422114402Sru    else {
1423114402Sru      if (h <= 0)
1424114402Sru	// see ps_printer::ps_printer
1425114402Sru	warning("bad paper height, defaulting to 11i");
1426114402Sru      if (w <= 0)
1427114402Sru	warning("bad paper width");
1428114402Sru    }
1429114402Sru  }
1430114402Sru  out.begin_comment("Orientation:")
1431114402Sru     .comment_arg(landscape_flag ? "Landscape" : "Portrait")
1432114402Sru     .end_comment();
1433114402Sru  if (ncopies != 1) {
1434114402Sru    out.end_line();
1435114402Sru    fprintf(out.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies);
1436114402Sru  }
1437114402Sru  out.simple_comment("EndComments");
1438114402Sru  if (!(broken_flags & NO_PAPERSIZE)) {
1439114402Sru    /* gv works fine without this one, but it really should be there. */
1440114402Sru    out.simple_comment("BeginDefaults");
1441114402Sru    fprintf(out.get_file(), "%%%%PageMedia: %s\n", media_name());
1442114402Sru    out.simple_comment("EndDefaults");
1443114402Sru  }
1444114402Sru  out.simple_comment("BeginProlog");
1445114402Sru  rm.output_prolog(out);
1446114402Sru  if (!(broken_flags & NO_SETUP_SECTION)) {
1447114402Sru    out.simple_comment("EndProlog");
1448114402Sru    out.simple_comment("BeginSetup");
1449114402Sru  }
1450114402Sru#if 1
1451114402Sru  /*
1452114402Sru   * Define paper (i.e., media) size for entire document here.
1453114402Sru   * This allows ps2pdf to correctly determine page size, for instance.
1454114402Sru   */
1455114402Sru  media_set();
1456114402Sru#endif
1457114402Sru  rm.document_setup(out);
1458114402Sru  out.put_symbol(dict_name)
1459114402Sru     .put_symbol("begin");
1460114402Sru  if (ndefs > 0)
1461114402Sru    ndefs += DEFS_DICT_SPARE;
1462114402Sru  out.put_literal_symbol(defs_dict_name)
1463114402Sru     .put_number(ndefs + 1)
1464114402Sru     .put_symbol("dict")
1465114402Sru     .put_symbol("def");
1466114402Sru  out.put_symbol(defs_dict_name)
1467114402Sru     .put_symbol("begin");
1468114402Sru  out.put_literal_symbol("u")
1469114402Sru     .put_delimiter('{')
1470114402Sru     .put_fix_number(1)
1471114402Sru     .put_symbol("mul")
1472114402Sru     .put_delimiter('}')
1473114402Sru     .put_symbol("bind")
1474114402Sru     .put_symbol("def");
1475114402Sru  defs += '\0';
1476114402Sru  out.special(defs.contents());
1477114402Sru  out.put_symbol("end");
1478114402Sru  if (ncopies != 1)
1479114402Sru    out.put_literal_symbol("#copies")
1480114402Sru       .put_number(ncopies)
1481114402Sru       .put_symbol("def");
1482114402Sru  out.put_literal_symbol("RES")
1483114402Sru     .put_number(res)
1484114402Sru     .put_symbol("def");
1485114402Sru  out.put_literal_symbol("PL");
1486114402Sru  if (guess_flag)
1487114402Sru    out.put_symbol("PLG");
1488114402Sru  else
1489114402Sru    out.put_fix_number(paper_length);
1490114402Sru  out.put_symbol("def");
1491114402Sru  out.put_literal_symbol("LS")
1492114402Sru     .put_symbol(landscape_flag ? "true" : "false")
1493114402Sru     .put_symbol("def");
1494114402Sru  if (manual_feed_flag) {
1495114402Sru    out.begin_comment("BeginFeature:")
1496114402Sru       .comment_arg("*ManualFeed")
1497114402Sru       .comment_arg("True")
1498114402Sru       .end_comment()
1499114402Sru       .put_symbol("MANUAL")
1500114402Sru       .simple_comment("EndFeature");
1501114402Sru  }
1502114402Sru  encode_fonts();
1503114402Sru  while (subencodings) {
1504114402Sru    subencoding *tem = subencodings;
1505114402Sru    subencodings = subencodings->next;
1506114402Sru    encode_subfont(tem);
1507114402Sru    out.put_literal_symbol(tem->subfont)
1508114402Sru       .put_symbol(make_subencoding_name(tem->idx))
1509114402Sru       .put_literal_symbol(tem->p->get_internal_name())
1510114402Sru       .put_symbol("RE");
1511114402Sru    delete tem;
1512114402Sru  }
1513114402Sru  out.simple_comment((broken_flags & NO_SETUP_SECTION)
1514114402Sru		     ? "EndProlog"
1515114402Sru		     : "EndSetup");
1516114402Sru  out.end_line();
1517114402Sru  out.copy_file(tempfp);
1518114402Sru  fclose(tempfp);
1519114402Sru}
1520114402Sru
1521114402Sruvoid ps_printer::special(char *arg, const environment *env, char type)
1522114402Sru{
1523114402Sru  if (type != 'p')
1524114402Sru    return;
1525114402Sru  typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *);
1526114402Sru  static struct {
1527114402Sru    const char *name;
1528114402Sru    SPECIAL_PROCP proc;
1529114402Sru  } proc_table[] = {
1530114402Sru    { "exec", &ps_printer::do_exec },
1531114402Sru    { "def", &ps_printer::do_def },
1532114402Sru    { "mdef", &ps_printer::do_mdef },
1533114402Sru    { "import", &ps_printer::do_import },
1534114402Sru    { "file", &ps_printer::do_file },
1535114402Sru    { "invis", &ps_printer::do_invis },
1536114402Sru    { "endinvis", &ps_printer::do_endinvis },
1537114402Sru  };
1538114402Sru  char *p;
1539114402Sru  for (p = arg; *p == ' ' || *p == '\n'; p++)
1540114402Sru    ;
1541114402Sru  char *tag = p;
1542114402Sru  for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
1543114402Sru    ;
1544114402Sru  if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) {
1545114402Sru    error("X command without `ps:' tag ignored");
1546114402Sru    return;
1547114402Sru  }
1548114402Sru  p++;
1549114402Sru  for (; *p == ' ' || *p == '\n'; p++)
1550114402Sru    ;
1551114402Sru  char *command = p;
1552114402Sru  for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1553114402Sru    ;
1554114402Sru  if (*command == '\0') {
1555114402Sru    error("empty X command ignored");
1556114402Sru    return;
1557114402Sru  }
1558114402Sru  for (unsigned int i = 0; i < sizeof(proc_table)/sizeof(proc_table[0]); i++)
1559114402Sru    if (strncmp(command, proc_table[i].name, p - command) == 0) {
1560114402Sru      (this->*(proc_table[i].proc))(p, env);
1561114402Sru      return;
1562114402Sru    }
1563114402Sru  error("X command `%1' not recognised", command);
1564114402Sru}
1565114402Sru
1566114402Sru// A conforming PostScript document must not have lines longer
1567114402Sru// than 255 characters (excluding line termination characters).
1568114402Sru
1569114402Srustatic int check_line_lengths(const char *p)
1570114402Sru{
1571114402Sru  for (;;) {
1572114402Sru    const char *end = strchr(p, '\n');
1573114402Sru    if (end == 0)
1574114402Sru      end = strchr(p, '\0');
1575114402Sru    if (end - p > 255)
1576114402Sru      return 0;
1577114402Sru    if (*end == '\0')
1578114402Sru      break;
1579114402Sru    p = end + 1;
1580114402Sru  }
1581114402Sru  return 1;
1582114402Sru}
1583114402Sru
1584114402Sruvoid ps_printer::do_exec(char *arg, const environment *env)
1585114402Sru{
1586114402Sru  flush_sbuf();
1587114402Sru  while (csspace(*arg))
1588114402Sru    arg++;
1589114402Sru  if (*arg == '\0') {
1590114402Sru    error("missing argument to X exec command");
1591114402Sru    return;
1592114402Sru  }
1593114402Sru  if (!check_line_lengths(arg)) {
1594114402Sru    error("lines in X exec command must not be more than 255 characters long");
1595114402Sru    return;
1596114402Sru  }
1597114402Sru  out.put_fix_number(env->hpos)
1598114402Sru     .put_fix_number(env->vpos)
1599114402Sru     .put_symbol("EBEGIN")
1600114402Sru     .special(arg)
1601114402Sru     .put_symbol("EEND");
1602114402Sru  output_hpos = output_vpos = -1;
1603114402Sru  output_style.f = 0;
1604114402Sru  output_draw_point_size = -1;
1605114402Sru  output_line_thickness = -1;
1606114402Sru  ndefined_styles = 0;
1607114402Sru  if (!ndefs)
1608114402Sru    ndefs = 1;
1609114402Sru}
1610114402Sru
1611114402Sruvoid ps_printer::do_file(char *arg, const environment *env)
1612114402Sru{
1613114402Sru  flush_sbuf();
1614114402Sru  while (csspace(*arg))
1615114402Sru    arg++;
1616114402Sru  if (*arg == '\0') {
1617114402Sru    error("missing argument to X file command");
1618114402Sru    return;
1619114402Sru  }
1620114402Sru  const char *filename = arg;
1621114402Sru  do {
1622114402Sru    ++arg;
1623114402Sru  } while (*arg != '\0' && *arg != ' ' && *arg != '\n');
1624114402Sru  out.put_fix_number(env->hpos)
1625114402Sru     .put_fix_number(env->vpos)
1626114402Sru     .put_symbol("EBEGIN");
1627114402Sru  rm.import_file(filename, out);
1628114402Sru  out.put_symbol("EEND");
1629114402Sru  output_hpos = output_vpos = -1;
1630114402Sru  output_style.f = 0;
1631114402Sru  output_draw_point_size = -1;
1632114402Sru  output_line_thickness = -1;
1633114402Sru  ndefined_styles = 0;
1634114402Sru  if (!ndefs)
1635114402Sru    ndefs = 1;
1636114402Sru}
1637114402Sru
1638114402Sruvoid ps_printer::do_def(char *arg, const environment *)
1639114402Sru{
1640114402Sru  flush_sbuf();
1641114402Sru  while (csspace(*arg))
1642114402Sru    arg++;
1643114402Sru  if (!check_line_lengths(arg)) {
1644114402Sru    error("lines in X def command must not be more than 255 characters long");
1645114402Sru    return;
1646114402Sru  }
1647114402Sru  defs += arg;
1648114402Sru  if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1649114402Sru    defs += '\n';
1650114402Sru  ndefs++;
1651114402Sru}
1652114402Sru
1653114402Sru// Like def, but the first argument says how many definitions it contains.
1654114402Sru
1655114402Sruvoid ps_printer::do_mdef(char *arg, const environment *)
1656114402Sru{
1657114402Sru  flush_sbuf();
1658114402Sru  char *p;
1659114402Sru  int n = (int)strtol(arg, &p, 10);
1660114402Sru  if (n == 0 && p == arg) {
1661114402Sru    error("first argument to X mdef must be an integer");
1662114402Sru    return;
1663114402Sru  }
1664114402Sru  if (n < 0) {
1665114402Sru    error("out of range argument `%1' to X mdef command", int(n));
1666114402Sru    return;
1667114402Sru  }
1668114402Sru  arg = p;
1669114402Sru  while (csspace(*arg))
1670114402Sru    arg++;
1671114402Sru  if (!check_line_lengths(arg)) {
1672114402Sru    error("lines in X mdef command must not be more than 255 characters long");
1673114402Sru    return;
1674114402Sru  }
1675114402Sru  defs += arg;
1676114402Sru  if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
1677114402Sru    defs += '\n';
1678114402Sru  ndefs += n;
1679114402Sru}
1680114402Sru
1681114402Sruvoid ps_printer::do_import(char *arg, const environment *env)
1682114402Sru{
1683114402Sru  flush_sbuf();
1684114402Sru  while (*arg == ' ' || *arg == '\n')
1685114402Sru    arg++;
1686114402Sru  char *p;
1687114402Sru  for (p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++)
1688114402Sru    ;
1689114402Sru  if (*p != '\0')
1690114402Sru    *p++ = '\0';
1691114402Sru  int parms[6];
1692114402Sru  int nparms = 0;
1693114402Sru  while (nparms < 6) {
1694114402Sru    char *end;
1695114402Sru    long n = strtol(p, &end, 10);
1696114402Sru    if (n == 0 && end == p)
1697114402Sru      break;
1698114402Sru    parms[nparms++] = int(n);
1699114402Sru    p = end;
1700114402Sru  }
1701114402Sru  if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
1702114402Sru    error("scaling indicators not allowed in arguments for X import command");
1703114402Sru    return;
1704114402Sru  }
1705114402Sru  while (*p == ' ' || *p == '\n')
1706114402Sru    p++;
1707114402Sru  if (nparms < 5) {
1708114402Sru    if (*p == '\0')
1709114402Sru      error("too few arguments for X import command");
1710114402Sru    else
1711114402Sru      error("invalid argument `%1' for X import command", p);
1712114402Sru    return;
1713114402Sru  }
1714114402Sru  if (*p != '\0') {
1715114402Sru    error("superfluous argument `%1' for X import command", p);
1716114402Sru    return;
1717114402Sru  }
1718114402Sru  int llx = parms[0];
1719114402Sru  int lly = parms[1];
1720114402Sru  int urx = parms[2];
1721114402Sru  int ury = parms[3];
1722114402Sru  int desired_width = parms[4];
1723114402Sru  int desired_height = parms[5];
1724114402Sru  if (desired_width <= 0) {
1725114402Sru    error("bad width argument `%1' for X import command: must be > 0",
1726114402Sru	  desired_width);
1727114402Sru    return;
1728114402Sru  }
1729114402Sru  if (nparms == 6 && desired_height <= 0) {
1730114402Sru    error("bad height argument `%1' for X import command: must be > 0",
1731114402Sru	  desired_height);
1732114402Sru    return;
1733114402Sru  }
1734114402Sru  if (llx == urx) {
1735114402Sru    error("llx and urx arguments for X import command must not be equal");
1736114402Sru    return;
1737114402Sru  }
1738114402Sru  if (lly == ury) {
1739114402Sru    error("lly and ury arguments for X import command must not be equal");
1740114402Sru    return;
1741114402Sru  }
1742114402Sru  if (nparms == 5) {
1743114402Sru    int old_wid = urx - llx;
1744114402Sru    int old_ht = ury - lly;
1745114402Sru    if (old_wid < 0)
1746114402Sru      old_wid = -old_wid;
1747114402Sru    if (old_ht < 0)
1748114402Sru      old_ht = -old_ht;
1749114402Sru    desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5);
1750114402Sru  }
1751114402Sru  if (env->vpos - desired_height < 0)
1752114402Sru    warning("top of imported graphic is above the top of the page");
1753114402Sru  out.put_number(llx)
1754114402Sru     .put_number(lly)
1755114402Sru     .put_fix_number(desired_width)
1756114402Sru     .put_number(urx - llx)
1757114402Sru     .put_fix_number(-desired_height)
1758114402Sru     .put_number(ury - lly)
1759114402Sru     .put_fix_number(env->hpos)
1760114402Sru     .put_fix_number(env->vpos)
1761114402Sru     .put_symbol("PBEGIN");
1762114402Sru  rm.import_file(arg, out);
1763114402Sru  // do this here just in case application defines PEND
1764114402Sru  out.put_symbol("end")
1765114402Sru     .put_symbol("PEND");
1766114402Sru}
1767114402Sru
1768114402Sruvoid ps_printer::do_invis(char *, const environment *)
1769114402Sru{
1770114402Sru  invis_count++;
1771114402Sru}
1772114402Sru
1773114402Sruvoid ps_printer::do_endinvis(char *, const environment *)
1774114402Sru{
1775114402Sru  if (invis_count == 0)
1776114402Sru    error("unbalanced `endinvis' command");
1777114402Sru  else
1778114402Sru    --invis_count;
1779114402Sru}
1780114402Sru
1781114402Sruprinter *make_printer()
1782114402Sru{
1783114402Sru  return new ps_printer(user_paper_length);
1784114402Sru}
1785114402Sru
1786114402Srustatic void usage(FILE *stream);
1787114402Sru
1788114402Sruint main(int argc, char **argv)
1789114402Sru{
1790114402Sru  setlocale(LC_NUMERIC, "C");
1791114402Sru  program_name = argv[0];
1792114402Sru  string env;
1793114402Sru  static char stderr_buf[BUFSIZ];
1794114402Sru  setbuf(stderr, stderr_buf);
1795114402Sru  int c;
1796114402Sru  static const struct option long_options[] = {
1797114402Sru    { "help", no_argument, 0, CHAR_MAX + 1 },
1798114402Sru    { "version", no_argument, 0, 'v' },
1799114402Sru    { NULL, 0, 0, 0 }
1800114402Sru  };
1801151497Sru  while ((c = getopt_long(argc, argv, "b:c:F:gI:lmp:P:vw:", long_options, NULL))
1802114402Sru	 != EOF)
1803114402Sru    switch(c) {
1804114402Sru    case 'b':
1805114402Sru      // XXX check this
1806114402Sru      broken_flags = atoi(optarg);
1807114402Sru      bflag = 1;
1808114402Sru      break;
1809114402Sru    case 'c':
1810114402Sru      if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
1811114402Sru	error("bad number of copies `%s'", optarg);
1812114402Sru	ncopies = 1;
1813114402Sru      }
1814114402Sru      break;
1815114402Sru    case 'F':
1816114402Sru      font::command_line_font_dir(optarg);
1817114402Sru      break;
1818114402Sru    case 'g':
1819114402Sru      guess_flag = 1;
1820114402Sru      break;
1821151497Sru    case 'I':
1822151497Sru      include_search_path.command_line_dir(optarg);
1823151497Sru      break;
1824114402Sru    case 'l':
1825114402Sru      landscape_flag = 1;
1826114402Sru      break;
1827114402Sru    case 'm':
1828114402Sru      manual_feed_flag = 1;
1829114402Sru      break;
1830114402Sru    case 'p':
1831114402Sru      if (!font::scan_papersize(optarg, 0,
1832114402Sru				&user_paper_length, &user_paper_width))
1833114402Sru	error("invalid custom paper size `%1' ignored", optarg);
1834114402Sru      break;
1835114402Sru    case 'P':
1836114402Sru      env = "GROPS_PROLOGUE";
1837114402Sru      env += '=';
1838114402Sru      env += optarg;
1839114402Sru      env += '\0';
1840114402Sru      if (putenv(strsave(env.contents())))
1841114402Sru	fatal("putenv failed");
1842114402Sru      break;
1843114402Sru    case 'v':
1844114402Sru      printf("GNU grops (groff) version %s\n", Version_string);
1845114402Sru      exit(0);
1846114402Sru      break;
1847114402Sru    case 'w':
1848114402Sru      if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
1849114402Sru	error("bad linewidth `%1'", optarg);
1850114402Sru	linewidth = -1;
1851114402Sru      }
1852114402Sru      break;
1853114402Sru    case CHAR_MAX + 1: // --help
1854114402Sru      usage(stdout);
1855114402Sru      exit(0);
1856114402Sru      break;
1857114402Sru    case '?':
1858114402Sru      usage(stderr);
1859114402Sru      exit(1);
1860114402Sru      break;
1861114402Sru    default:
1862114402Sru      assert(0);
1863114402Sru    }
1864114402Sru  font::set_unknown_desc_command_handler(handle_unknown_desc_command);
1865114402Sru  SET_BINARY(fileno(stdout));
1866114402Sru  if (optind >= argc)
1867114402Sru    do_file("-");
1868114402Sru  else {
1869114402Sru    for (int i = optind; i < argc; i++)
1870114402Sru      do_file(argv[i]);
1871114402Sru  }
1872114402Sru  return 0;
1873114402Sru}
1874114402Sru
1875114402Srustatic void usage(FILE *stream)
1876114402Sru{
1877114402Sru  fprintf(stream,
1878151497Sru"usage: %s [-glmv] [-b n] [-c n] [-w n] [-I dir] [-P prologue]\n"
1879151497Sru"       [-F dir] [files ...]\n",
1880114402Sru    program_name);
1881114402Sru}
1882