LCOV - code coverage report
Current view: top level - devices/grops - ps.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 946 1216 77.8 %
Date: 2026-01-16 17:51:41 Functions: 67 73 91.8 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14