PageRenderTime 205ms CodeModel.GetById 33ms app.highlight 158ms RepoModel.GetById 1ms app.codeStats 1ms

/contrib/groff/src/devices/grops/ps.cpp

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