home *** CD-ROM | disk | FTP | other *** search
/ Aminet 10 / aminetcdnumber101996.iso / Aminet / util / gnu / groff_src.lha / groff-1.10src / grops / ps.cc < prev    next >
C/C++ Source or Header  |  1995-06-27  |  36KB  |  1,533 lines

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992 Free Software Foundation, Inc.
  3.      Written by James Clark (jjc@jclark.com)
  4.  
  5. This file is part of groff.
  6.  
  7. groff is free software; you can redistribute it and/or modify it under
  8. the terms of the GNU General Public License as published by the Free
  9. Software Foundation; either version 2, or (at your option) any later
  10. version.
  11.  
  12. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  13. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. for more details.
  16.  
  17. You should have received a copy of the GNU General Public License along
  18. with groff; see the file COPYING.  If not, write to the Free Software
  19. Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  20.  
  21. #include "driver.h"
  22. #include "stringclass.h"
  23. #include "cset.h"
  24.  
  25. #include "ps.h"
  26. #include <time.h>
  27.  
  28. static int landscape_flag = 0;
  29. static int manual_feed_flag = 0;
  30. static int ncopies = 1;
  31. static int linewidth = -1;
  32. // Non-zero means generate PostScript code that guesses the paper
  33. // length using the imageable area.
  34. static int guess_flag = 0;
  35.  
  36. // Non-zero if -b was specified on the command line.
  37. static int bflag = 0;
  38. unsigned broken_flags = 0;
  39.  
  40. #define DEFAULT_LINEWIDTH 40    /* in ems/1000 */
  41. #define MAX_LINE_LENGTH 72
  42. #define FILL_MAX 1000
  43.  
  44. const char *const dict_name = "grops";
  45. const char *const defs_dict_name = "DEFS";
  46. const int DEFS_DICT_SPARE = 50;
  47.  
  48. double degrees(double r)
  49. {
  50.   return r*180.0/PI;
  51. }
  52.  
  53. double radians(double d)
  54. {
  55.   return d*PI/180.0;
  56. }
  57.  
  58. inline double transform_fill(int fill)
  59. {
  60.   return 1 - fill/double(FILL_MAX);
  61. }
  62.  
  63. // This is used for testing whether a character should be output in the
  64. // PostScript file using \nnn, so we really want the character to be
  65. // less than 0200.
  66.  
  67. inline int is_ascii(char c)
  68. {
  69.   return (unsigned char)c < 0200;
  70. }
  71.  
  72. ps_output::ps_output(FILE *f, int n)
  73. : fp(f), max_line_length(n), col(0), need_space(0), fixed_point(0)
  74. {
  75. }
  76.  
  77. ps_output &ps_output::set_file(FILE *f)
  78. {
  79.   fp = f;
  80.   col = 0;
  81.   return *this;
  82. }
  83.  
  84. ps_output &ps_output::copy_file(FILE *infp)
  85. {
  86.   int c;
  87.   while ((c = getc(infp)) != EOF)
  88.     putc(c, fp);
  89.   return *this;
  90. }
  91.  
  92. ps_output &ps_output::end_line()
  93. {
  94.   if (col != 0) {
  95.     putc('\n', fp);
  96.     col = 0;
  97.     need_space = 0;
  98.   }
  99.   return *this;
  100. }
  101.  
  102. ps_output &ps_output::special(const char *s)
  103. {
  104.   if (s == 0 || *s == '\0')
  105.     return *this;
  106.   if (col != 0) {
  107.     putc('\n', fp);
  108.     col = 0;
  109.   }
  110.   fputs(s, fp);
  111.   if (strchr(s, '\0')[-1] != '\n')
  112.     putc('\n', fp);
  113.   need_space = 0;
  114.   return *this;
  115. }
  116.  
  117. ps_output &ps_output::simple_comment(const char *s)
  118. {
  119.   if (col != 0)
  120.     putc('\n', fp);
  121.   putc('%', fp);
  122.   putc('%', fp);
  123.   fputs(s, fp);
  124.   putc('\n', fp);
  125.   col = 0;
  126.   need_space = 0;
  127.   return *this;
  128. }
  129.  
  130. ps_output &ps_output::begin_comment(const char *s)
  131. {
  132.   if (col != 0)
  133.     putc('\n', fp);
  134.   putc('%', fp);
  135.   putc('%', fp);
  136.   fputs(s, fp);
  137.   col = 2 + strlen(s);
  138.   return *this;
  139. }
  140.  
  141. ps_output &ps_output::end_comment()
  142. {
  143.   if (col != 0) {
  144.     putc('\n', fp);
  145.     col = 0;
  146.   }
  147.   need_space = 0;
  148.   return *this;
  149. }
  150.  
  151. ps_output &ps_output::comment_arg(const char *s)
  152. {
  153.   int len = strlen(s);
  154.   if (col + len + 1 > max_line_length) {
  155.     putc('\n', fp);
  156.     fputs("%%+", fp);
  157.     col = 3;
  158.   }
  159.   putc(' ',  fp);
  160.   fputs(s, fp);
  161.   col += len + 1;
  162.   return *this;
  163. }
  164.  
  165. ps_output &ps_output::set_fixed_point(int n)
  166. {
  167.   assert(n >= 0 && n <= 10);
  168.   fixed_point = n;
  169.   return *this;
  170. }
  171.  
  172. ps_output &ps_output::put_delimiter(char c)
  173. {
  174.   if (col + 1 > max_line_length) {
  175.     putc('\n', fp);
  176.     col = 0;
  177.   }
  178.   putc(c, fp);
  179.   col++;
  180.   need_space = 0;
  181.   return *this;
  182. }
  183.  
  184. ps_output &ps_output::put_string(const char *s, int n)
  185. {
  186.   int len = 0;
  187.   int i;
  188.   for (i = 0; i < n; i++) {
  189.     char c = s[i];
  190.     if (is_ascii(c) && csprint(c)) {
  191.       if (c == '(' || c == ')' || c == '\\')
  192.     len += 2;
  193.       else
  194.     len += 1;
  195.     }
  196.     else
  197.       len += 4;
  198.   }
  199.   if (len > n*2) {
  200.     if (col + n*2 + 2 > max_line_length && n*2 + 2 <= max_line_length) {
  201.       putc('\n', fp);
  202.       col = 0;
  203.     }
  204.     if (col + 1 > max_line_length) {
  205.       putc('\n', fp);
  206.       col = 0;
  207.     }
  208.     putc('<', fp);
  209.     col++;
  210.     for (i = 0; i < n; i++) {
  211.       if (col + 2 > max_line_length) {
  212.     putc('\n', fp);
  213.     col = 0;
  214.       }
  215.       fprintf(fp, "%02x", s[i] & 0377);
  216.       col += 2;
  217.     }
  218.     putc('>', fp);
  219.     col++;
  220.   }
  221.   else {
  222.     if (col + len + 2 > max_line_length && len + 2 <= max_line_length) {
  223.       putc('\n', fp);
  224.       col = 0;
  225.     }
  226.     if (col + 2 > max_line_length) {
  227.       putc('\n', fp);
  228.       col = 0;
  229.     }
  230.     putc('(', fp);
  231.     col++;
  232.     for (i = 0; i < n; i++) {
  233.       char c = s[i];
  234.       if (is_ascii(c) && csprint(c)) {
  235.     if (c == '(' || c == ')' || c == '\\')
  236.       len = 2;
  237.     else
  238.       len = 1;
  239.       }
  240.       else
  241.     len = 4;
  242.       if (col + len + 1 > max_line_length) {
  243.     putc('\\', fp);
  244.     putc('\n', fp);
  245.     col = 0;
  246.       }
  247.       switch (len) {
  248.       case 1:
  249.     putc(c, fp);
  250.     break;
  251.       case 2:
  252.     putc('\\', fp);
  253.     putc(c, fp);
  254.     break;
  255.       case 4:
  256.     fprintf(fp, "\\%03o", c & 0377);
  257.     break;
  258.       default:
  259.     assert(0);
  260.       }
  261.       col += len;
  262.     }
  263.     putc(')', fp);
  264.     col++;
  265.   }
  266.   need_space = 0;
  267.   return *this;
  268. }
  269.  
  270. ps_output &ps_output::put_number(int n)
  271. {
  272.   char buf[1 + INT_DIGITS + 1];
  273.   sprintf(buf, "%d", n);
  274.   int len = strlen(buf);
  275.   if (col > 0 && col + len + need_space > max_line_length) {
  276.     putc('\n', fp);
  277.     col = 0;
  278.     need_space = 0;
  279.   }
  280.   if (need_space) {
  281.     putc(' ', fp);
  282.     col++;
  283.   }
  284.   fputs(buf, fp);
  285.   col += len;
  286.   need_space = 1;
  287.   return *this;
  288. }
  289.  
  290. ps_output &ps_output::put_fix_number(int i)
  291. {
  292.   const char *p = iftoa(i, fixed_point);
  293.   int len = strlen(p);
  294.   if (col > 0 && col + len + need_space > max_line_length) {
  295.     putc('\n', fp);
  296.     col = 0;
  297.     need_space = 0;
  298.   }
  299.   if (need_space) {
  300.     putc(' ', fp);
  301.     col++;
  302.   }
  303.   fputs(p, fp);
  304.   col += len;
  305.   need_space = 1;
  306.   return *this;
  307. }
  308.  
  309. ps_output &ps_output::put_float(double d)
  310. {
  311.   char buf[128];
  312.   sprintf(buf, "%.4f", d);
  313.   int len = strlen(buf);
  314.   if (col > 0 && col + len + need_space > max_line_length) {
  315.     putc('\n', fp);
  316.     col = 0;
  317.     need_space = 0;
  318.   }
  319.   if (need_space) {
  320.     putc(' ', fp);
  321.     col++;
  322.   }
  323.   fputs(buf, fp);
  324.   col += len;
  325.   need_space = 1;
  326.   return *this;
  327. }
  328.  
  329. ps_output &ps_output::put_symbol(const char *s)
  330. {
  331.   int len = strlen(s);
  332.   if (col > 0 && col + len + need_space > max_line_length) {
  333.     putc('\n', fp);
  334.     col = 0;
  335.     need_space = 0;
  336.   }
  337.   if (need_space) {
  338.     putc(' ', fp);
  339.     col++;
  340.   }
  341.   fputs(s, fp);
  342.   col += len;
  343.   need_space = 1;
  344.   return *this;
  345. }
  346.  
  347. ps_output &ps_output::put_literal_symbol(const char *s)
  348. {
  349.   int len = strlen(s);
  350.   if (col > 0 && col + len + 1 > max_line_length) {
  351.     putc('\n', fp);
  352.     col = 0;
  353.   }
  354.   putc('/', fp);
  355.   fputs(s, fp);
  356.   col += len + 1;
  357.   need_space = 1;
  358.   return *this;
  359. }
  360.  
  361. class ps_font : public font {
  362.   ps_font(const char *);
  363. public:
  364.   int encoding_index;
  365.   char *encoding;
  366.   char *reencoded_name;
  367.   ~ps_font();
  368.   void handle_unknown_font_command(const char *command, const char *arg,
  369.                    const char *filename, int lineno);
  370.   static ps_font *load_ps_font(const char *);
  371. };
  372.  
  373. ps_font *ps_font::load_ps_font(const char *s)
  374. {
  375.   ps_font *f = new ps_font(s);
  376.   if (!f->load()) {
  377.     delete f;
  378.     return 0;
  379.   }
  380.   return f;
  381. }
  382.  
  383. ps_font::ps_font(const char *nm)
  384. : font(nm), encoding(0), reencoded_name(0), encoding_index(-1)
  385. {
  386. }
  387.  
  388. ps_font::~ps_font()
  389. {
  390.   a_delete encoding;
  391.   a_delete reencoded_name;
  392. }
  393.  
  394. void ps_font::handle_unknown_font_command(const char *command, const char *arg,
  395.                       const char *filename, int lineno)
  396. {
  397.   if (strcmp(command, "encoding") == 0) {
  398.     if (arg == 0)
  399.       error_with_file_and_line(filename, lineno,
  400.                    "`encoding' command requires an argument");
  401.     else
  402.       encoding = strsave(arg);
  403.   }
  404. }
  405.  
  406. static void handle_unknown_desc_command(const char *command, const char *arg,
  407.                     const char *filename, int lineno)
  408. {
  409.   if (strcmp(command, "broken") == 0) {
  410.     if (arg == 0)
  411.       error_with_file_and_line(filename, lineno,
  412.                    "`broken' command requires an argument");
  413.     else if (!bflag)
  414.       broken_flags = atoi(arg);
  415.   }
  416. }
  417.  
  418. struct style {
  419.   font *f;
  420.   int point_size;
  421.   int height;
  422.   int slant;
  423.   style();
  424.   style(font *, int, int, int);
  425.   int operator==(const style &) const;
  426.   int operator!=(const style &) const;
  427. };
  428.  
  429. style::style() : f(0)
  430. {
  431. }
  432.  
  433. style::style(font *p, int sz, int h, int sl)
  434. : f(p), point_size(sz), height(h), slant(sl)
  435. {
  436. }
  437.  
  438. int style::operator==(const style &s) const
  439. {
  440.   return (f == s.f && point_size == s.point_size
  441.       && height == s.height && slant == s.slant);
  442. }
  443.  
  444. int style::operator!=(const style &s) const
  445. {
  446.   return !(*this == s);
  447. }
  448.  
  449. class ps_printer : public printer {
  450.   FILE *tempfp;
  451.   ps_output out;
  452.   int res;
  453.   int space_char_index;
  454.   int pages_output;
  455.   int paper_length;
  456.   int equalise_spaces;
  457.   enum { SBUF_SIZE = 256 };
  458.   char sbuf[SBUF_SIZE];
  459.   int sbuf_len;
  460.   int sbuf_start_hpos;
  461.   int sbuf_vpos;
  462.   int sbuf_end_hpos;
  463.   int sbuf_space_width;
  464.   int sbuf_space_count;
  465.   int sbuf_space_diff_count;
  466.   int sbuf_space_code;
  467.   int sbuf_kern;
  468.   style sbuf_style;
  469.   style output_style;
  470.   int output_hpos;
  471.   int output_vpos;
  472.   int output_draw_point_size;
  473.   int line_thickness;
  474.   int output_line_thickness;
  475.   int fill;
  476.   unsigned char output_space_code;
  477.   enum { MAX_DEFINED_STYLES = 50 };
  478.   style defined_styles[MAX_DEFINED_STYLES];
  479.   int ndefined_styles;
  480.   int next_encoding_index;
  481.   string defs;
  482.   int ndefs;
  483.   resource_manager rm;
  484.   int invis_count;
  485.  
  486.   void flush_sbuf();
  487.   void set_style(const style &);
  488.   void set_space_code(unsigned char c);
  489.   int set_encoding_index(ps_font *);
  490.   void do_exec(char *, const environment *);
  491.   void do_import(char *, const environment *);
  492.   void do_def(char *, const environment *);
  493.   void do_mdef(char *, const environment *);
  494.   void do_file(char *, const environment *);
  495.   void do_invis(char *, const environment *);
  496.   void do_endinvis(char *, const environment *);
  497.   void set_line_thickness(const environment *);
  498.   void fill_path();
  499.   void encode_fonts();
  500.   void define_encoding(const char *, int);
  501.   void reencode_font(ps_font *);
  502. public:
  503.   ps_printer();
  504.   ~ps_printer();
  505.   void set_char(int i, font *f, const environment *env, int w);
  506.   void draw(int code, int *p, int np, const environment *env);
  507.   void begin_page(int);
  508.   void end_page(int);
  509.   void special(char *arg, const environment *env);
  510.   font *make_font(const char *);
  511.   void end_of_line();
  512. };
  513.  
  514. ps_printer::ps_printer()
  515. : pages_output(0),
  516.   sbuf_len(0),
  517.   output_hpos(-1),
  518.   output_vpos(-1),
  519.   out(0, MAX_LINE_LENGTH),
  520.   ndefined_styles(0),
  521.   next_encoding_index(0),
  522.   line_thickness(-1),
  523.   fill(FILL_MAX + 1),
  524.   ndefs(0),
  525.   invis_count(0)
  526. {
  527.   tempfp = xtmpfile();
  528.   out.set_file(tempfp);
  529.   if (linewidth < 0)
  530.     linewidth = DEFAULT_LINEWIDTH;
  531.   if (font::hor != 1)
  532.     fatal("horizontal resolution must be 1");
  533.   if (font::vert != 1)
  534.     fatal("vertical resolution must be 1");
  535.   if (font::res % (font::sizescale*72) != 0)
  536.     fatal("res must be a multiple of 72*sizescale");
  537.   int r = font::res;
  538.   int point = 0;
  539.   while (r % 10 == 0) {
  540.     r /= 10;
  541.     point++;
  542.   }
  543.   res = r;
  544.   out.set_fixed_point(point);
  545.   space_char_index = font::name_to_index("space");
  546.   paper_length = font::paperlength;
  547.   if (paper_length == 0)
  548.     paper_length = 11*font::res;
  549.   equalise_spaces = font::res >= 72000;
  550. }
  551.  
  552. int ps_printer::set_encoding_index(ps_font *f)
  553. {
  554.   if (f->encoding_index >= 0)
  555.     return f->encoding_index;
  556.   for (font_pointer_list *p = font_list; p; p = p->next)
  557.     if (p->p != f) {
  558.       char *encoding = ((ps_font *)p->p)->encoding;
  559.       int encoding_index = ((ps_font *)p->p)->encoding_index;
  560.       if (encoding != 0 && encoding_index >= 0 
  561.       && strcmp(f->encoding, encoding) == 0) {
  562.     return f->encoding_index = encoding_index;
  563.       }
  564.     }
  565.   return f->encoding_index = next_encoding_index++;
  566. }
  567.  
  568. void ps_printer::set_char(int i, font *f, const environment *env, int w)
  569. {
  570.   if (i == space_char_index || invis_count > 0)
  571.     return;
  572.   unsigned char code = f->get_code(i);
  573.   style sty(f, env->size, env->height, env->slant);
  574.   if (sty.slant != 0) {
  575.     if (sty.slant > 80 || sty.slant < -80) {
  576.       error("silly slant `%1' degrees", sty.slant);
  577.       sty.slant = 0;
  578.     }
  579.   }
  580.   if (sbuf_len > 0) {
  581.     if (sbuf_len < SBUF_SIZE
  582.     && sty == sbuf_style
  583.     && sbuf_vpos == env->vpos) {
  584.       if (sbuf_end_hpos == env->hpos) {
  585.     sbuf[sbuf_len++] = code;
  586.     sbuf_end_hpos += w + sbuf_kern;
  587.     return;
  588.       }
  589.       if (sbuf_len == 1 && sbuf_kern == 0) {
  590.     sbuf_kern = env->hpos - sbuf_end_hpos;
  591.     sbuf_end_hpos = env->hpos + sbuf_kern + w;
  592.     sbuf[sbuf_len++] = code;
  593.     return;
  594.       }
  595.       /* If sbuf_end_hpos - sbuf_kern == env->hpos, we are better off
  596.      starting a new string. */
  597.       if (sbuf_len < SBUF_SIZE - 1 && env->hpos >= sbuf_end_hpos
  598.       && (sbuf_kern == 0 || sbuf_end_hpos - sbuf_kern != env->hpos)) {
  599.     if (sbuf_space_code < 0) {
  600.       if (f->contains(space_char_index)) {
  601.         sbuf_space_code = f->get_code(space_char_index);
  602.         sbuf_space_width = env->hpos - sbuf_end_hpos;
  603.         sbuf_end_hpos = env->hpos + w + sbuf_kern;
  604.         sbuf[sbuf_len++] = sbuf_space_code;
  605.         sbuf[sbuf_len++] = code;
  606.         sbuf_space_count++;
  607.         return;
  608.       }
  609.     }
  610.     else {
  611.       int diff = env->hpos - sbuf_end_hpos - sbuf_space_width;
  612.       if (diff == 0 || (equalise_spaces && (diff == 1 || diff == -1))) {
  613.         sbuf_end_hpos = env->hpos + w + sbuf_kern;
  614.         sbuf[sbuf_len++] = sbuf_space_code;
  615.         sbuf[sbuf_len++] = code;
  616.         sbuf_space_count++;
  617.         if (diff == 1)
  618.           sbuf_space_diff_count++;
  619.         else if (diff == -1)
  620.           sbuf_space_diff_count--;
  621.         return;
  622.       }
  623.     }
  624.       }
  625.     }
  626.     flush_sbuf();
  627.   }
  628.   sbuf_len = 1;
  629.   sbuf[0] = code;
  630.   sbuf_end_hpos = env->hpos + w;
  631.   sbuf_start_hpos = env->hpos;
  632.   sbuf_vpos = env->vpos;
  633.   sbuf_style = sty;
  634.   sbuf_space_code = -1;
  635.   sbuf_space_width = 0;
  636.   sbuf_space_count = sbuf_space_diff_count = 0;
  637.   sbuf_kern = 0;
  638. }
  639.  
  640. static char *make_encoding_name(int encoding_index)
  641. {
  642.   static char buf[3 + INT_DIGITS + 1];
  643.   sprintf(buf, "ENC%d", encoding_index);
  644.   return buf;
  645. }
  646.  
  647. const char *const WS = " \t\n\r";
  648.  
  649. void ps_printer::define_encoding(const char *encoding, int encoding_index)
  650. {
  651.   char *vec[256];
  652.   int i;
  653.   for (i = 0; i < 256; i++)
  654.     vec[i] = 0;
  655.   char *path;
  656.   FILE *fp = font::open_file(encoding, &path);
  657.   if (fp == 0)
  658.     fatal("can't open encoding file `%1'", encoding);
  659.   int lineno = 1;
  660.   char buf[256];
  661.   while (fgets(buf, 512, fp) != 0) {
  662.     char *p = buf;
  663.     while (csspace(*p))
  664.       p++;
  665.     if (*p != '#' && *p != '\0' && (p = strtok(buf, WS)) != 0) {
  666.       char *q = strtok(0, WS);
  667.       int n;
  668.       if (q == 0 || sscanf(q, "%d", &n) != 1 || n < 0 || n >= 256)
  669.     fatal_with_file_and_line(path, lineno, "bad second field");
  670.       vec[n] = new char[strlen(p) + 1];
  671.       strcpy(vec[n], p);
  672.     }
  673.     lineno++;
  674.   }
  675.   a_delete path;
  676.   out.put_literal_symbol(make_encoding_name(encoding_index));
  677.   out.put_delimiter('[');
  678.   for (i = 0; i < 256; i++) {
  679.     if (vec[i] == 0)
  680.       out.put_literal_symbol(".notdef");
  681.     else {
  682.       out.put_literal_symbol(vec[i]);
  683.       a_delete vec[i];
  684.     }
  685.   }
  686.   out.put_delimiter(']').put_symbol("def");
  687. }
  688.  
  689. void ps_printer::reencode_font(ps_font *f)
  690. {
  691.   out.put_literal_symbol(f->reencoded_name)
  692.      .put_symbol(make_encoding_name(f->encoding_index))
  693.      .put_literal_symbol(f->get_internal_name())
  694.      .put_symbol("RE");
  695. }
  696.  
  697. void ps_printer::encode_fonts()
  698. {
  699.   if (next_encoding_index == 0)
  700.     return;
  701.   char *done_encoding = new char[next_encoding_index];
  702.   for (int i = 0; i < next_encoding_index; i++)
  703.     done_encoding[i] = 0;
  704.   for (font_pointer_list *f = font_list; f; f = f->next) {
  705.     int encoding_index = ((ps_font *)f->p)->encoding_index;
  706.     if (encoding_index >= 0) {
  707.       assert(encoding_index < next_encoding_index);
  708.       if (!done_encoding[encoding_index]) {
  709.     done_encoding[encoding_index] = 1;
  710.     define_encoding(((ps_font *)f->p)->encoding, encoding_index);
  711.       }
  712.       reencode_font((ps_font *)f->p);
  713.     }
  714.   }
  715.   a_delete done_encoding;
  716. }
  717.  
  718. void ps_printer::set_style(const style &sty)
  719. {
  720.   char buf[1 + INT_DIGITS + 1];
  721.   for (int i = 0; i < ndefined_styles; i++)
  722.     if (sty == defined_styles[i]) {
  723.       sprintf(buf, "F%d", i);
  724.       out.put_symbol(buf);
  725.       return;
  726.     }
  727.   if (ndefined_styles >= MAX_DEFINED_STYLES)
  728.     ndefined_styles = 0;
  729.   sprintf(buf, "F%d", ndefined_styles);
  730.   out.put_literal_symbol(buf);
  731.   const char *psname = sty.f->get_internal_name();
  732.   if (psname == 0)
  733.     fatal("no internalname specified for font `%1'", sty.f->get_name());
  734.   char *encoding = ((ps_font *)sty.f)->encoding;
  735.   if (encoding != 0) {
  736.     char *s = ((ps_font *)sty.f)->reencoded_name;
  737.     if (s == 0) {
  738.       int ei = set_encoding_index((ps_font *)sty.f);
  739.       char *tem = new char[strlen(psname) + 1 + INT_DIGITS + 1];
  740.       sprintf(tem, "%s@%d", psname, ei);
  741.       psname = tem;
  742.       ((ps_font *)sty.f)->reencoded_name = tem;
  743.     }
  744.     else
  745.       psname = s;
  746.   }
  747.   out.put_fix_number((font::res/(72*font::sizescale))*sty.point_size);
  748.   if (sty.height != 0 || sty.slant != 0) {
  749.     int h = sty.height == 0 ? sty.point_size : sty.height;
  750.     h *= font::res/(72*font::sizescale);
  751.     int c = int(h*tan(radians(sty.slant)) + .5);
  752.     out.put_fix_number(c).put_fix_number(h).put_literal_symbol(psname)
  753.        .put_symbol("MF");
  754.   }
  755.   else {
  756.     out.put_literal_symbol(psname).put_symbol("SF");
  757.   }
  758.   defined_styles[ndefined_styles++] = sty;
  759. }
  760.  
  761. void ps_printer::set_space_code(unsigned char c)
  762. {
  763.   out.put_literal_symbol("SC").put_number(c).put_symbol("def");
  764. }
  765.  
  766. void ps_printer::end_of_line()
  767. {
  768.   flush_sbuf();
  769.   // this ensures that we do an absolute motion to the beginning of a line
  770.   output_vpos = output_hpos = -1;
  771. }
  772.  
  773. void ps_printer::flush_sbuf()
  774. {
  775.   enum {
  776.     NONE,
  777.     RELATIVE_H,
  778.     RELATIVE_V,
  779.     RELATIVE_HV,
  780.     ABSOLUTE
  781.     } motion = NONE;
  782.   int space_flag = 0;
  783.   if (sbuf_len == 0)
  784.     return;
  785.   if (output_style != sbuf_style) {
  786.     set_style(sbuf_style);
  787.     output_style = sbuf_style;
  788.   }
  789.   int extra_space = 0;
  790.   if (output_hpos < 0 || output_vpos < 0)
  791.     motion = ABSOLUTE;
  792.   else {
  793.     if (output_hpos != sbuf_start_hpos)
  794.       motion = RELATIVE_H;
  795.     if (output_vpos != sbuf_vpos) {
  796.       if  (motion != NONE)
  797.     motion = RELATIVE_HV;
  798.       else
  799.     motion = RELATIVE_V;
  800.     }
  801.   }
  802.   if (sbuf_space_code >= 0) {
  803.     int w = sbuf_style.f->get_width(space_char_index, sbuf_style.point_size);
  804.     if (w + sbuf_kern != sbuf_space_width) {
  805.       if (sbuf_space_code != output_space_code) {
  806.     set_space_code(sbuf_space_code);
  807.     output_space_code = sbuf_space_code;
  808.       }
  809.       space_flag = 1;
  810.       extra_space = sbuf_space_width - w - sbuf_kern;
  811.       if (sbuf_space_diff_count > sbuf_space_count/2)
  812.     extra_space++;
  813.       else if (sbuf_space_diff_count < -(sbuf_space_count/2))
  814.     extra_space--;
  815.     }
  816.   }
  817.   if (space_flag)
  818.     out.put_fix_number(extra_space);
  819.   if (sbuf_kern != 0)
  820.     out.put_fix_number(sbuf_kern);
  821.   out.put_string(sbuf, sbuf_len);
  822.   char sym[2];
  823.   sym[0] = 'A' + motion*4 + space_flag + 2*(sbuf_kern != 0);
  824.   sym[1] = '\0';
  825.   switch (motion) {
  826.   case NONE:
  827.     break;
  828.   case ABSOLUTE:
  829.     out.put_fix_number(sbuf_start_hpos)
  830.        .put_fix_number(sbuf_vpos);
  831.     break;
  832.   case RELATIVE_H:
  833.     out.put_fix_number(sbuf_start_hpos - output_hpos);
  834.     break;
  835.   case RELATIVE_V:
  836.     out.put_fix_number(sbuf_vpos - output_vpos);
  837.     break;
  838.   case RELATIVE_HV:
  839.     out.put_fix_number(sbuf_start_hpos - output_hpos)
  840.        .put_fix_number(sbuf_vpos - output_vpos);
  841.     break;
  842.   default:
  843.     assert(0);
  844.   }
  845.   out.put_symbol(sym);
  846.   output_hpos = sbuf_end_hpos;
  847.   output_vpos = sbuf_vpos;
  848.   sbuf_len = 0;
  849. }
  850.  
  851.  
  852. void ps_printer::set_line_thickness(const environment *env)
  853. {
  854.   if (line_thickness < 0) {
  855.     if (output_draw_point_size != env->size) {
  856.       // we ought to check for overflow here
  857.       int lw = ((font::res/(72*font::sizescale))*linewidth*env->size)/1000;
  858.       out.put_fix_number(lw).put_symbol("LW");
  859.       output_draw_point_size = env->size;
  860.       output_line_thickness = -1;
  861.     }
  862.   }
  863.   else {
  864.     if (output_line_thickness != line_thickness) {
  865.       out.put_fix_number(line_thickness).put_symbol("LW");
  866.       output_line_thickness = line_thickness;
  867.       output_draw_point_size = -1;
  868.     }
  869.   }
  870. }
  871.  
  872. void ps_printer::fill_path()
  873. {
  874.   if (fill > FILL_MAX)
  875.     out.put_symbol("BL");
  876.   else
  877.     out.put_float(transform_fill(fill)).put_symbol("FL");
  878. }
  879.  
  880. void ps_printer::draw(int code, int *p, int np, const environment *env)
  881. {
  882.   if (invis_count > 0)
  883.     return;
  884.   int fill_flag = 0;
  885.   switch (code) {
  886.   case 'C':
  887.     fill_flag = 1;
  888.     // fall through
  889.   case 'c':
  890.     // troff adds an extra argument to C
  891.     if (np != 1 && !(code == 'C' && np == 2)) {
  892.       error("1 argument required for circle");
  893.       break;
  894.     }
  895.     out.put_fix_number(env->hpos + p[0]/2)
  896.        .put_fix_number(env->vpos)
  897.        .put_fix_number(p[0]/2)
  898.        .put_symbol("DC");
  899.     if (fill_flag) {
  900.       fill_path();
  901.     }
  902.     else {
  903.       set_line_thickness(env);
  904.       out.put_symbol("ST");
  905.     }
  906.     break;
  907.   case 'l':
  908.     if (np != 2) {
  909.       error("2 arguments required for line");
  910.       break;
  911.     }
  912.     set_line_thickness(env);
  913.     out.put_fix_number(p[0] + env->hpos)
  914.        .put_fix_number(p[1] + env->vpos)
  915.        .put_fix_number(env->hpos)
  916.        .put_fix_number(env->vpos)
  917.        .put_symbol("DL");
  918.     break;
  919.   case 'E':
  920.     fill_flag = 1;
  921.     // fall through
  922.   case 'e':
  923.     if (np != 2) {
  924.       error("2 arguments required for ellipse");
  925.       break;
  926.     }
  927.     out.put_fix_number(p[0])
  928.        .put_fix_number(p[1])
  929.        .put_fix_number(env->hpos + p[0]/2)
  930.        .put_fix_number(env->vpos)
  931.        .put_symbol("DE");
  932.     if (fill_flag) {
  933.       fill_path();
  934.     }
  935.     else {
  936.       set_line_thickness(env);
  937.       out.put_symbol("ST");
  938.     }
  939.     break;
  940.   case 'P':
  941.     fill_flag = 1;
  942.     // fall through
  943.   case 'p':
  944.     {
  945.       if (np & 1) {
  946.     error("even number of arguments required for polygon");
  947.     break;
  948.       }
  949.       if (np == 0) {
  950.     error("no arguments for polygon");
  951.     break;
  952.       }
  953.       out.put_fix_number(env->hpos)
  954.      .put_fix_number(env->vpos)
  955.      .put_symbol("MT");
  956.       for (int i = 0; i < np; i += 2)
  957.     out.put_fix_number(p[i])
  958.        .put_fix_number(p[i+1])
  959.        .put_symbol("RL");
  960.       out.put_symbol("CL");
  961.       if (fill_flag) {
  962.     fill_path();
  963.       }
  964.       else {
  965.     set_line_thickness(env);
  966.     out.put_symbol("ST");
  967.       }
  968.       break;
  969.     }
  970.   case '~':
  971.     {
  972.       if (np & 1) {
  973.     error("even number of arguments required for spline");
  974.     break;
  975.       }
  976.       if (np == 0) {
  977.     error("no arguments for spline");
  978.     break;
  979.       }
  980.       out.put_fix_number(env->hpos)
  981.      .put_fix_number(env->vpos)
  982.      .put_symbol("MT");
  983.       out.put_fix_number(p[0]/2)
  984.      .put_fix_number(p[1]/2)
  985.      .put_symbol("RL");
  986.       /* tnum/tden should be between 0 and 1; the closer it is to 1
  987.      the tighter the curve will be to the guiding lines; 2/3
  988.      is the standard value */
  989.       const int tnum = 2;
  990.       const int tden = 3;
  991.       for (int i = 0; i < np - 2; i += 2) {
  992.     out.put_fix_number((p[i]*tnum)/(2*tden))
  993.        .put_fix_number((p[i + 1]*tnum)/(2*tden))
  994.        .put_fix_number(p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden))
  995.        .put_fix_number(p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden))
  996.        .put_fix_number((p[i] - p[i]/2) + p[i + 2]/2)
  997.        .put_fix_number((p[i + 1] - p[i + 1]/2) + p[i + 3]/2)
  998.        .put_symbol("RC");
  999.       }
  1000.       out.put_fix_number(p[np - 2] - p[np - 2]/2)
  1001.      .put_fix_number(p[np - 1] - p[np - 1]/2)
  1002.      .put_symbol("RL");
  1003.       set_line_thickness(env);
  1004.       out.put_symbol("ST");
  1005.     }
  1006.     break;
  1007.   case 'a':
  1008.     {
  1009.       if (np != 4) {
  1010.     error("4 arguments required for arc");
  1011.     break;
  1012.       }
  1013.       set_line_thickness(env);
  1014.       double c[2];
  1015.       if (adjust_arc_center(p, c))
  1016.     out.put_fix_number(env->hpos + int(c[0]))
  1017.        .put_fix_number(env->vpos + int(c[1]))
  1018.        .put_fix_number(int(sqrt(c[0]*c[0] + c[1]*c[1])))
  1019.        .put_float(degrees(atan2(-c[1], -c[0])))
  1020.        .put_float(degrees(atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])))
  1021.        .put_symbol("DA");
  1022.       else
  1023.     out.put_fix_number(p[0] + p[2] + env->hpos)
  1024.        .put_fix_number(p[1] + p[3] + env->vpos)
  1025.        .put_fix_number(env->hpos)
  1026.        .put_fix_number(env->vpos)
  1027.        .put_symbol("DL");
  1028.     }
  1029.     break;
  1030.   case 't':
  1031.     {
  1032.       if (np == 0) {
  1033.     line_thickness = -1;
  1034.       }
  1035.       else {
  1036.     // troff gratuitously adds an extra 0
  1037.     if (np != 1 && np != 2) {
  1038.       error("0 or 1 argument required for thickness");
  1039.       break;
  1040.     }
  1041.     line_thickness = p[0];
  1042.       }
  1043.       break;
  1044.     }
  1045.   case 'f':
  1046.     {
  1047.       if (np != 1 && np != 2) {
  1048.     error("1 argument required for fill");
  1049.     break;
  1050.       }
  1051.       fill = p[0];
  1052.       if (fill < 0 || fill > FILL_MAX) {
  1053.     // This means fill with the current color.
  1054.     fill = FILL_MAX + 1;
  1055.       }
  1056.       break;
  1057.     }      
  1058.   default:
  1059.     error("unrecognised drawing command `%1'", char(code));
  1060.     break;
  1061.   }
  1062.  
  1063.   output_hpos = output_vpos = -1;
  1064. }
  1065.  
  1066.  
  1067. void ps_printer::begin_page(int n)
  1068. {
  1069.   out.begin_comment("Page:").comment_arg(itoa(n));
  1070.   out.comment_arg(itoa(++pages_output)).end_comment();
  1071.   output_style.f = 0;
  1072.   output_space_code = 32;
  1073.   output_draw_point_size = -1;
  1074.   output_line_thickness = -1;
  1075.   output_hpos = output_vpos = -1;
  1076.   ndefined_styles = 0;
  1077.   out.simple_comment("BeginPageSetup");
  1078.   out.put_symbol("BP");
  1079.   out.simple_comment("EndPageSetup");
  1080. }
  1081.  
  1082. void ps_printer::end_page(int)
  1083. {
  1084.   flush_sbuf();
  1085.   out.put_symbol("EP");
  1086.   if (invis_count != 0) {
  1087.     error("missing `endinvis' command");
  1088.     invis_count = 0;
  1089.   }
  1090. }
  1091.  
  1092. font *ps_printer::make_font(const char *nm)
  1093. {
  1094.   return ps_font::load_ps_font(nm);
  1095. }
  1096.  
  1097. ps_printer::~ps_printer()
  1098. {
  1099.   out.simple_comment("Trailer");
  1100.   out.put_symbol("end");
  1101.   out.simple_comment("EOF");
  1102.   if (fseek(tempfp, 0L, 0) < 0)
  1103.     fatal("fseek on temporary file failed");
  1104.   fputs("%!PS-Adobe-", stdout);
  1105.   fputs((broken_flags & USE_PS_ADOBE_2_0) ? "2.0" : "3.0", stdout);
  1106.   putchar('\n');
  1107.   out.set_file(stdout);
  1108.   {
  1109.     extern const char *version_string;
  1110.     out.begin_comment("Creator:")
  1111.        .comment_arg("groff")
  1112.        .comment_arg("version")
  1113.        .comment_arg(version_string)
  1114.        .end_comment();
  1115.   }
  1116.   {
  1117.     fputs("%%CreationDate: ", out.get_file());
  1118. #ifdef LONG_FOR_TIME_T
  1119.     long
  1120. #else
  1121.     time_t
  1122. #endif
  1123.     t = time(0);
  1124.     fputs(ctime(&t), out.get_file());
  1125.   }
  1126.   for (font_pointer_list *f = font_list; f; f = f->next) {
  1127.     ps_font *psf = (ps_font *)(f->p);
  1128.     rm.need_font(psf->get_internal_name());
  1129.   }
  1130.   rm.print_header_comments(out);
  1131.   out.begin_comment("Pages:").comment_arg(itoa(pages_output)).end_comment();
  1132.   out.begin_comment("PageOrder:").comment_arg("Ascend").end_comment();
  1133. #if 0
  1134.   fprintf(out.get_file(), "%%%%DocumentMedia: () %g %g 0 () ()\n",
  1135.       font::paperwidth*72.0/font::res,
  1136.       paper_length*72.0/font::res);
  1137. #endif
  1138.   out.begin_comment("Orientation:")
  1139.      .comment_arg(landscape_flag ? "Landscape" : "Portrait")
  1140.      .end_comment(); 
  1141.   if (ncopies != 1) {
  1142.     out.end_line();
  1143.     fprintf(out.get_file(), "%%%%Requirements: numcopies(%d)\n", ncopies);
  1144.   }
  1145.   out.simple_comment("EndComments");
  1146.   out.simple_comment("BeginProlog");
  1147.   rm.output_prolog(out);
  1148.   if (!(broken_flags & NO_SETUP_SECTION)) {
  1149.     out.simple_comment("EndProlog");
  1150.     out.simple_comment("BeginSetup");
  1151.   }
  1152.   rm.document_setup(out);
  1153.   out.put_symbol(dict_name).put_symbol("begin");
  1154.   if (ndefs > 0)
  1155.     ndefs += DEFS_DICT_SPARE;
  1156.   out.put_literal_symbol(defs_dict_name)
  1157.      .put_number(ndefs + 1)
  1158.      .put_symbol("dict")
  1159.      .put_symbol("def");
  1160.   out.put_symbol(defs_dict_name)
  1161.      .put_symbol("begin");
  1162.   out.put_literal_symbol("u")
  1163.      .put_delimiter('{')
  1164.      .put_fix_number(1)
  1165.      .put_symbol("mul")
  1166.      .put_delimiter('}')
  1167.      .put_symbol("bind")
  1168.      .put_symbol("def");
  1169.   defs += '\0';
  1170.   out.special(defs.contents());
  1171.   out.put_symbol("end");
  1172.   if (ncopies != 1)
  1173.     out.put_literal_symbol("#copies").put_number(ncopies).put_symbol("def");
  1174.   out.put_literal_symbol("RES").put_number(res).put_symbol("def");
  1175.   out.put_literal_symbol("PL");
  1176.   if (guess_flag)
  1177.     out.put_symbol("PLG");
  1178.   else
  1179.     out.put_fix_number(paper_length);
  1180.   out.put_symbol("def");
  1181.   out.put_literal_symbol("LS")
  1182.      .put_symbol(landscape_flag ? "true" : "false")
  1183.      .put_symbol("def");
  1184.   if (manual_feed_flag) {
  1185.     out.begin_comment("BeginFeature:")
  1186.        .comment_arg("*ManualFeed")
  1187.        .comment_arg("True")
  1188.        .end_comment()
  1189.        .put_symbol("MANUAL")
  1190.        .simple_comment("EndFeature");
  1191.   }
  1192.   encode_fonts();
  1193.   out.simple_comment((broken_flags & NO_SETUP_SECTION)
  1194.              ? "EndProlog"
  1195.              : "EndSetup");
  1196.   out.end_line();
  1197.   out.copy_file(tempfp);
  1198.   fclose(tempfp);
  1199. }
  1200.  
  1201. void ps_printer::special(char *arg, const environment *env)
  1202. {
  1203.   typedef void (ps_printer::*SPECIAL_PROCP)(char *, const environment *);
  1204.   static struct {
  1205.     const char *name;
  1206.     SPECIAL_PROCP proc;
  1207.   } proc_table[] = {
  1208.     { "exec", &ps_printer::do_exec },
  1209.     { "def", &ps_printer::do_def },
  1210.     { "mdef", &ps_printer::do_mdef },
  1211.     { "import", &ps_printer::do_import },
  1212.     { "file", &ps_printer::do_file },
  1213.     { "invis", &ps_printer::do_invis },
  1214.     { "endinvis", &ps_printer::do_endinvis },
  1215.   };
  1216.   char *p;
  1217.   for (p = arg; *p == ' ' || *p == '\n'; p++)
  1218.     ;
  1219.   char *tag = p;
  1220.   for (; *p != '\0' && *p != ':' && *p != ' ' && *p != '\n'; p++)
  1221.     ;
  1222.   if (*p == '\0' || strncmp(tag, "ps", p - tag) != 0) {
  1223.     error("X command without `ps:' tag ignored");
  1224.     return;
  1225.   }
  1226.   p++;
  1227.   for (; *p == ' ' || *p == '\n'; p++)
  1228.     ;
  1229.   char *command = p;
  1230.   for (; *p != '\0' && *p != ' ' && *p != '\n'; p++)
  1231.     ;
  1232.   if (*command == '\0') {
  1233.     error("X command without `ps:' tag ignored");
  1234.     return;
  1235.   }
  1236.   for (int i = 0; i < sizeof(proc_table)/sizeof(proc_table[0]); i++)
  1237.     if (strncmp(command, proc_table[i].name, p - command) == 0) {
  1238.       (this->*(proc_table[i].proc))(p, env);
  1239.       return;
  1240.     }
  1241.   error("X command `%1' not recognised", command);
  1242. }
  1243.  
  1244. // A conforming PostScript document must not have lines longer
  1245. // than 255 characters (excluding line termination characters).
  1246.  
  1247. static int check_line_lengths(const char *p)
  1248. {
  1249.   for (;;) {
  1250.     const char *end = strchr(p, '\n');
  1251.     if (end == 0)
  1252.       end = strchr(p, '\0');
  1253.     if (end - p > 255)
  1254.       return 0;
  1255.     if (*end == '\0')
  1256.       break;
  1257.     p = end + 1;
  1258.   }
  1259.   return 1;
  1260. }
  1261.  
  1262. void ps_printer::do_exec(char *arg, const environment *env)
  1263. {
  1264.   flush_sbuf();
  1265.   while (csspace(*arg))
  1266.     arg++;
  1267.   if (*arg == '\0') {
  1268.     error("missing argument to X exec command");
  1269.     return;
  1270.   }
  1271.   if (!check_line_lengths(arg)) {
  1272.     error("lines in X exec command must not be more than 255 characters long");
  1273.     return;
  1274.   }
  1275.   out.put_fix_number(env->hpos)
  1276.      .put_fix_number(env->vpos)
  1277.      .put_symbol("EBEGIN")
  1278.      .special(arg)
  1279.      .put_symbol("EEND");
  1280.   output_hpos = output_vpos = -1;
  1281.   output_style.f = 0;
  1282.   output_draw_point_size = -1;
  1283.   output_line_thickness = -1;
  1284.   ndefined_styles = 0;
  1285.   if (!ndefs)
  1286.     ndefs = 1;
  1287. }
  1288.  
  1289. void ps_printer::do_file(char *arg, const environment *env)
  1290. {
  1291.   flush_sbuf();
  1292.   while (csspace(*arg))
  1293.     arg++;
  1294.   if (*arg == '\0') {
  1295.     error("missing argument to X file command");
  1296.     return;
  1297.   }
  1298.   const char *filename = arg;
  1299.   do {
  1300.     ++arg;
  1301.   } while (*arg != '\0' && *arg != ' ' && *arg != '\n');
  1302.   out.put_fix_number(env->hpos)
  1303.      .put_fix_number(env->vpos)
  1304.      .put_symbol("EBEGIN");
  1305.   rm.import_file(filename, out);
  1306.   out.put_symbol("EEND");
  1307.   output_hpos = output_vpos = -1;
  1308.   output_style.f = 0;
  1309.   output_draw_point_size = -1;
  1310.   output_line_thickness = -1;
  1311.   ndefined_styles = 0;
  1312.   if (!ndefs)
  1313.     ndefs = 1;
  1314. }
  1315.  
  1316. void ps_printer::do_def(char *arg, const environment *)
  1317. {
  1318.   flush_sbuf();
  1319.   while (csspace(*arg))
  1320.     arg++;
  1321.   if (!check_line_lengths(arg)) {
  1322.     error("lines in X def command must not be more than 255 characters long");
  1323.     return;
  1324.   }
  1325.   defs += arg;
  1326.   if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
  1327.     defs += '\n';
  1328.   ndefs++;
  1329. }
  1330.  
  1331. // Like def, but the first argument says how many definitions it contains.
  1332.  
  1333. void ps_printer::do_mdef(char *arg, const environment *)
  1334. {
  1335.   flush_sbuf();
  1336.   char *p;
  1337.   int n = (int)strtol(arg, &p, 10);
  1338.   if (n == 0 && p == arg) {
  1339.     error("first argument to X mdef must be an integer");
  1340.     return;
  1341.   }
  1342.   if (n < 0) {
  1343.     error("out of range argument `%1' to X mdef command", int(n));
  1344.     return;
  1345.   }
  1346.   arg = p;
  1347.   while (csspace(*arg))
  1348.     arg++;
  1349.   if (!check_line_lengths(arg)) {
  1350.     error("lines in X mdef command must not be more than 255 characters long");
  1351.     return;
  1352.   }
  1353.   defs += arg;
  1354.   if (*arg != '\0' && strchr(arg, '\0')[-1] != '\n')
  1355.     defs += '\n';
  1356.   ndefs += n;
  1357. }
  1358.  
  1359. void ps_printer::do_import(char *arg, const environment *env)
  1360. {
  1361.   flush_sbuf();
  1362.   while (*arg == ' ' || *arg == '\n')
  1363.     arg++;
  1364.   char *p;
  1365.   for (p = arg; *p != '\0' && *p != ' ' && *p != '\n'; p++)
  1366.     ;
  1367.   if (*p != '\0')
  1368.     *p++ = '\0';
  1369.   int parms[6];
  1370.   int nparms = 0;
  1371.   while (nparms < 6) {
  1372.     char *end;
  1373.     long n = strtol(p, &end, 10);
  1374.     if (n == 0 && end == p)
  1375.       break;
  1376.     parms[nparms++] = int(n);
  1377.     p = end;
  1378.   }
  1379.   if (csalpha(*p) && (p[1] == '\0' || p[1] == ' ' || p[1] == '\n')) {
  1380.     error("scaling indicators not allowed in arguments for X import command");
  1381.     return;
  1382.   }
  1383.   while (*p == ' ' || *p == '\n')
  1384.     p++;
  1385.   if (nparms < 5) {
  1386.     if (*p == '\0')
  1387.       error("too few arguments for X import command");
  1388.     else
  1389.       error("invalid argument `%1' for X import command", p);
  1390.     return;
  1391.   }
  1392.   if (*p != '\0') {
  1393.     error("superflous argument `%1' for X import command", p);
  1394.     return;
  1395.   }
  1396.   int llx = parms[0];
  1397.   int lly = parms[1];
  1398.   int urx = parms[2];
  1399.   int ury = parms[3];
  1400.   int desired_width = parms[4];
  1401.   int desired_height = parms[5];
  1402.   if (desired_width <= 0) {
  1403.     error("bad width argument `%1' for X import command: must be > 0",
  1404.       desired_width);
  1405.     return;
  1406.   }
  1407.   if (nparms == 6 && desired_height <= 0) {
  1408.     error("bad height argument `%1' for X import command: must be > 0",
  1409.       desired_height);
  1410.     return;
  1411.   }
  1412.   if (llx == urx) {
  1413.     error("llx and urx arguments for X import command must not be equal");
  1414.     return;
  1415.   }
  1416.   if (lly == ury) {
  1417.     error("lly and ury arguments for X import command must not be equal");
  1418.     return;
  1419.   }
  1420.   if (nparms == 5) {
  1421.     int old_wid = urx - llx;
  1422.     int old_ht = ury - lly;
  1423.     if (old_wid < 0)
  1424.       old_wid = -old_wid;
  1425.     if (old_ht < 0)
  1426.       old_ht = -old_ht;
  1427.     desired_height = int(desired_width*(double(old_ht)/double(old_wid)) + .5);
  1428.   }
  1429.   if (env->vpos - desired_height < 0)
  1430.     warning("top of imported graphic is above the top of the page");
  1431.   out.put_number(llx)
  1432.      .put_number(lly)
  1433.      .put_fix_number(desired_width)
  1434.      .put_number(urx - llx)
  1435.      .put_fix_number(-desired_height)
  1436.      .put_number(ury - lly)
  1437.      .put_fix_number(env->hpos)
  1438.      .put_fix_number(env->vpos)
  1439.      .put_symbol("PBEGIN");
  1440.   rm.import_file(arg, out);
  1441.   // do this here just in case application defines PEND
  1442.   out.put_symbol("end");
  1443.   out.put_symbol("PEND");
  1444. }
  1445.  
  1446. void ps_printer::do_invis(char *, const environment *)
  1447. {
  1448.   invis_count++;
  1449. }
  1450.  
  1451. void ps_printer::do_endinvis(char *, const environment *)
  1452. {
  1453.   if (invis_count == 0)
  1454.     error("unbalanced `endinvis' command");
  1455.   else
  1456.     --invis_count;
  1457. }
  1458.  
  1459. printer *make_printer()
  1460. {
  1461.   return new ps_printer;
  1462. }
  1463.  
  1464. static void usage();
  1465.  
  1466. int main(int argc, char **argv)
  1467. {
  1468.   program_name = argv[0];
  1469.   static char stderr_buf[BUFSIZ];
  1470.   setbuf(stderr, stderr_buf);
  1471.   int c;
  1472.   while ((c = getopt(argc, argv, "F:glmc:w:vb:")) != EOF)
  1473.     switch(c) {
  1474.     case 'v':
  1475.       {
  1476.     extern const char *version_string;
  1477.     fprintf(stderr, "grops version %s\n", version_string);
  1478.     fflush(stderr);
  1479.     break;
  1480.       }
  1481.     case 'c':
  1482.       if (sscanf(optarg, "%d", &ncopies) != 1 || ncopies <= 0) {
  1483.     error("bad number of copies `%s'", optarg);
  1484.     ncopies = 1;
  1485.       }
  1486.       break;
  1487.     case 'g':
  1488.       guess_flag = 1;
  1489.       break;
  1490.     case 'l':
  1491.       landscape_flag = 1;
  1492.       break;
  1493.     case 'm':
  1494.       manual_feed_flag = 1;
  1495.       break;
  1496.     case 'F':
  1497.       font::command_line_font_dir(optarg);
  1498.       break;
  1499.     case 'w':
  1500.       if (sscanf(optarg, "%d", &linewidth) != 1 || linewidth < 0) {
  1501.     error("bad linewidth `%1'", optarg);
  1502.     linewidth = -1;
  1503.       }
  1504.       break;
  1505.     case 'b':
  1506.       // XXX check this
  1507.       broken_flags = atoi(optarg);
  1508.       bflag = 1;
  1509.       break;
  1510.     case '?':
  1511.       usage();
  1512.       break;
  1513.     default:
  1514.       assert(0);
  1515.     }
  1516.   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
  1517.   if (optind >= argc)
  1518.     do_file("-");
  1519.   else {
  1520.     for (int i = optind; i < argc; i++)
  1521.       do_file(argv[i]);
  1522.   }
  1523.   delete pr;
  1524.   return 0;
  1525. }
  1526.  
  1527. static void usage()
  1528. {
  1529.   fprintf(stderr, "usage: %s [-glmv] [-b n] [-c n] [-w n] [-F dir] [files ...]\n",
  1530.       program_name);
  1531.   exit(1);
  1532. }
  1533.