LCOV - code coverage report
Current view: top level - devices/grolj4 - lj4.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 110 384 28.6 %
Date: 2026-01-16 17:51:41 Functions: 19 25 76.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 1994-2025 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             : TODO
      21             : 
      22             : option to use beziers for circle/ellipse/arc
      23             : option to use lines for spline (for LJ3)
      24             : left/top offset registration
      25             : output bin selection option
      26             : paper source option
      27             : output non-integer parameters using fixed point numbers
      28             : X command to insert contents of file
      29             : X command to specify inline escape sequence (how to specify unprintable chars?)
      30             : X command to include bitmap graphics
      31             : */
      32             : 
      33             : #ifdef HAVE_CONFIG_H
      34             : #include <config.h>
      35             : #endif
      36             : 
      37             : #include <assert.h>
      38             : #include <locale.h> // setlocale()
      39             : #include <math.h> // atan2(), floor()
      40             : #include <stdcountof.h>
      41             : #include <stdio.h> // EOF, FILE, fflush(), fprintf(), printf(),
      42             :                    // setbuf(), stderr, stdout
      43             : #include <stdlib.h> // exit(), EXIT_SUCCESS, strtol()
      44             : #include <string.h> // strcmp()
      45             : #include <strings.h> // strcasecmp()
      46             : 
      47             : #include <getopt.h> // getopt_long()
      48             : 
      49             : #include "nonposix.h"
      50             : 
      51             : #include "cset.h" // csdigit()
      52             : #include "driver.h"
      53             : #include "lib.h" // array_size(), PI
      54             : 
      55             : extern "C" const char *Version_string;
      56             : 
      57             : static struct lj4_paper_sizes {
      58             :   const char *name;
      59             :   int code;
      60             :   // at 300dpi
      61             :   int x_offset_portrait;
      62             :   int x_offset_landscape;
      63             : } paper_table[] = {
      64             :   { "letter", 2, 75, 60 },
      65             :   { "legal", 3, 75, 60 },
      66             :   { "executive", 1, 75, 60 },
      67             :   { "a4", 26, 71, 59 },
      68             :   { "com10", 81, 75, 60 },
      69             :   { "monarch", 80, 75, 60 },
      70             :   { "c5", 91, 71, 59 },
      71             :   { "b5", 100, 71, 59 },
      72             :   { "dl", 90, 71, 59 },
      73             : };
      74             : 
      75             : static int user_paper_size = -1;
      76             : static int landscape_flag = 0;
      77             : static int duplex_flag = 0;
      78             : 
      79             : // An upper limit on the paper dimensions in centipoints,
      80             : // used for setting HPGL picture frame.
      81             : #define MAX_PAPER_WIDTH (12*720)
      82             : #define MAX_PAPER_HEIGHT (17*720)
      83             : 
      84             : // Dotted lines that are thinner than this don't work right.
      85             : #define MIN_DOT_PEN_WIDTH .351
      86             : 
      87             : #ifndef DEFAULT_LINE_WIDTH_FACTOR
      88             : // in ems/1000
      89             : #define DEFAULT_LINE_WIDTH_FACTOR 40
      90             : #endif
      91             : 
      92             : const int DEFAULT_HPGL_UNITS = 1016;
      93             : int line_width_factor = DEFAULT_LINE_WIDTH_FACTOR;
      94             : unsigned ncopies = 0;           // 0 means don't send ncopies command
      95             : 
      96             : class lj4_font : public font {
      97             : public:
      98             :   ~lj4_font();
      99             :   void handle_unknown_font_command(const char * /* command */,
     100             :                                    const char * /* arg */,
     101             :                                    const char * /* fn */,
     102             :                                    int lineno);
     103             :   static lj4_font *load_lj4_font(const char * /* s */);
     104             :   int weight;
     105             :   int style;
     106             :   int proportional;
     107             :   int typeface;
     108             : private:
     109             :   lj4_font(const char * /* nm */);
     110             : };
     111             : 
     112           1 : lj4_font::lj4_font(const char *nm)
     113           1 : : font(nm), weight(0), style(0), proportional(0), typeface(0)
     114             : {
     115           1 : }
     116             : 
     117           2 : lj4_font::~lj4_font()
     118             : {
     119           2 : }
     120             : 
     121           1 : lj4_font *lj4_font::load_lj4_font(const char *s)
     122             : {
     123           1 :   lj4_font *f = new lj4_font(s);
     124           1 :   if (!f->load()) {
     125           0 :     delete f;
     126           0 :     return 0 /* nullptr */;
     127             :   }
     128           1 :   return f;
     129             : }
     130             : 
     131             : static struct lj4_command_table {
     132             :   const char *s;
     133             :   int lj4_font::*ptr;
     134             :   int min;
     135             :   int max;
     136             : } command_table[] = {
     137             :   { "pclweight", &lj4_font::weight, -7, 7 },
     138             :   { "pclstyle", &lj4_font::style, 0, 32767 },
     139             :   { "pclproportional", &lj4_font::proportional, 0, 1 },
     140             :   { "pcltypeface", &lj4_font::typeface, 0, 65535 },
     141             : };
     142             : 
     143           4 : void lj4_font::handle_unknown_font_command(const char *command,
     144             :                                            const char *arg,
     145             :                                            const char *fn,
     146             :                                            int lineno)
     147             : {
     148          10 :   for (size_t i = 0; i < countof(command_table); i++) {
     149          10 :     if (strcmp(command, command_table[i].s) == 0) {
     150           4 :       if (0 /* nullptr */ == arg)
     151           0 :         fatal_with_file_and_line(fn, lineno,
     152             :                                  "'%1' command requires an argument",
     153           0 :                                  command);
     154             :       char *ptr;
     155           4 :       long n = strtol(arg, &ptr, 10);
     156           4 :       if (ptr == arg)
     157           0 :         fatal_with_file_and_line(fn, lineno,
     158             :                                  "'%1' command requires numeric"
     159           0 :                                  " argument", command);
     160           4 :       if (n < command_table[i].min) {
     161           0 :         error_with_file_and_line(fn, lineno,
     162             :                                  "'%1' command argument must not be"
     163             :                                  " less than %2", command,
     164           0 :                                  command_table[i].min);
     165           0 :         n = command_table[i].min;
     166             :       }
     167           4 :       else if (n > command_table[i].max) {
     168           0 :         error_with_file_and_line(fn, lineno,
     169             :                                  "'%1' command argument must not be"
     170             :                                  " greater than %2", command,
     171           0 :                                  command_table[i].max);
     172           0 :         n = command_table[i].max;
     173             :       }
     174           4 :       this->*command_table[i].ptr = int(n);
     175           4 :       break;
     176             :     }
     177             :   }
     178           4 : }
     179             : 
     180           1 : static ssize_t lookup_paper_size(const char *s)
     181             : {
     182             :   // C++11: constexpr
     183           1 :   const size_t paper_table_length = countof(paper_table);
     184             :   // ...and once it's a constexpr, we can do this...
     185             :   //static_assert(paper_table_length < INT_MAX);
     186             :   // ...but until then...
     187             :   assert(paper_table_length < INT_MAX);
     188           1 :   for (size_t i = 0; i < paper_table_length; i++) {
     189             :     // FIXME Perhaps allow unique prefix.
     190           1 :     if (strcasecmp(s, paper_table[i].name) == 0)
     191           1 :       return ssize_t(i);
     192             :   }
     193           0 :   return ssize_t(-1);
     194             : }
     195             : 
     196             : class lj4_printer : public printer {
     197             : public:
     198             :   lj4_printer(int);
     199             :   ~lj4_printer();
     200             :   void set_char(glyph * /* g */,
     201             :                 font * /* f */,
     202             :                 const environment * /* env */,
     203             :                 int /* w */,
     204             :                 const char * /* UNUSED */);
     205             :   void draw(int /* code */, int * /* p */, int  /* np */,
     206             :             const environment * /* env */);
     207             :   void begin_page(int /* UNUSED */);
     208             :   void end_page(int /* page_length */);
     209             :   font *make_font(const char * /* nm */);
     210             :   void end_of_line();
     211             : private:
     212             :   void set_line_thickness(int /* size */, int /* dot */ = 0);
     213             :   void hpgl_init();
     214             :   void hpgl_start();
     215             :   void hpgl_end();
     216             :   int moveto(int /* hpos */, int /* vpos */);
     217             :   int moveto1(int /* hpos */, int /* vpos */);
     218             : 
     219             :   int cur_hpos;
     220             :   int cur_vpos;
     221             :   lj4_font *cur_font;
     222             :   int cur_size;
     223             :   unsigned short cur_symbol_set;
     224             :   int x_offset;
     225             :   int line_thickness;
     226             :   double pen_width;
     227             :   double hpgl_scale;
     228             :   int hpgl_inited;
     229             :   int paper_size;
     230             : };
     231             : 
     232             : inline
     233          12 : int lj4_printer::moveto(int hpos, int vpos)
     234             : {
     235          12 :   if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0)
     236           4 :     return moveto1(hpos, vpos);
     237             :   else
     238           8 :     return 1;
     239             : }
     240             : 
     241             : inline
     242           0 : void lj4_printer::hpgl_start()
     243             : {
     244           0 :   fputs("\033%1B", stdout);
     245           0 : }
     246             : 
     247             : inline
     248           0 : void lj4_printer::hpgl_end()
     249             : {
     250           0 :   fputs(";\033%0A", stdout);
     251           0 : }
     252             : 
     253           1 : lj4_printer::lj4_printer(int ps)
     254             : : cur_hpos(-1),
     255             :   cur_font(0 /* nullptr */),
     256             :   cur_size(0),
     257             :   cur_symbol_set(0),
     258             :   line_thickness(-1),
     259             :   pen_width(-1.0),
     260           1 :   hpgl_inited(0)
     261             : {
     262           1 :   if (7200 % font::res != 0)
     263           0 :     fatal("invalid resolution %1: resolution must be a factor of 7200",
     264           0 :           font::res);
     265           1 :   fputs("\033E", stdout);             // reset
     266           1 :   if (font::res != 300)
     267           1 :     printf("\033&u%dD", font::res);       // unit of measure
     268           1 :   if (ncopies > 0)
     269           0 :     printf("\033&l%uX", ncopies);
     270           1 :   paper_size = 0;               // default to letter
     271           1 :   if (font::papersize) {
     272           1 :     int n = lookup_paper_size(font::papersize);
     273           1 :     if (n < 0)
     274           0 :       error("ignoring invalid paper format '%1'", font::papersize);
     275             :     else
     276           1 :       paper_size = n;
     277             :   }
     278           1 :   if (ps >= 0)
     279           0 :     paper_size = ps;
     280           1 :   printf("\033&l%dA"              // paper format
     281             :          "\033&l%dO"              // orientation
     282             :          "\033&l0E",              // no top margin
     283           1 :          paper_table[paper_size].code,
     284             :          landscape_flag != 0);
     285           1 :   if (landscape_flag)
     286           0 :     x_offset = paper_table[paper_size].x_offset_landscape;
     287             :   else
     288           1 :     x_offset = paper_table[paper_size].x_offset_portrait;
     289           1 :   x_offset = (x_offset * font::res) / 300;
     290           1 :   if (duplex_flag)
     291           0 :      printf("\033&l%dS", duplex_flag);
     292           1 : }
     293             : 
     294           2 : lj4_printer::~lj4_printer()
     295             : {
     296           1 :   current_lineno = 0; // At this point, we've read all the input.
     297           1 :   fputs("\033E", stdout);
     298           2 : }
     299             : 
     300           1 : void lj4_printer::begin_page(int)
     301             : {
     302           1 : }
     303             : 
     304           1 : void lj4_printer::end_page(int)
     305             : {
     306           1 :   putchar('\f');
     307           1 :   cur_hpos = -1;
     308           1 : }
     309             : 
     310           1 : void lj4_printer::end_of_line()
     311             : {
     312           1 :   cur_hpos = -1;                // force absolute motion
     313           1 : }
     314             : 
     315             : inline
     316          12 : int is_unprintable(unsigned char c)
     317             : {
     318          12 :   return c < 32 && (0 == c || (7 <= c && c <= 15) || 27 == c);
     319             : }
     320             : 
     321          12 : void lj4_printer::set_char(glyph *g, font *f, const environment *env,
     322             :                            int w, const char *)
     323             : {
     324          12 :   int code = f->get_code(g);
     325             : 
     326          12 :   unsigned char ch = code & 0xff;
     327          12 :   unsigned short symbol_set = code >> 8;
     328          12 :   if (symbol_set != cur_symbol_set) {
     329           1 :     printf("\033(%d%c", symbol_set/32, (symbol_set & 31) + 64);
     330           1 :     cur_symbol_set = symbol_set;
     331             :   }
     332          12 :   if (f != cur_font) {
     333           1 :     lj4_font *psf = (lj4_font *)f;
     334             :     // FIXME only output those that are needed
     335           1 :     printf("\033(s%dp%ds%db%dT",
     336             :            psf->proportional,
     337             :            psf->style,
     338             :            psf->weight,
     339             :            psf->typeface);
     340           1 :     if (!psf->proportional || !cur_font || !cur_font->proportional)
     341           1 :       cur_size = 0;
     342           1 :     cur_font = psf;
     343             :   }
     344          12 :   if (env->size != cur_size) {
     345           1 :     if (cur_font->proportional) {
     346             :       static const char *quarters[] = { "", ".25", ".5", ".75" };
     347           1 :       printf("\033(s%d%sV", env->size/4, quarters[env->size & 3]);
     348             :     }
     349             :     else {
     350           0 :       double pitch = double(font::res)/w;
     351             :       // PCL uses the next largest pitch, so round it down.
     352           0 :       pitch = floor(pitch*100.0)/100.0;
     353           0 :       printf("\033(s%.2fH", pitch);
     354             :     }
     355           1 :     cur_size = env->size;
     356             :   }
     357          12 :   if (!moveto(env->hpos, env->vpos))
     358           0 :     return;
     359          12 :   if (is_unprintable(ch))
     360           0 :     fputs("\033&p1X", stdout);
     361          12 :   putchar(ch);
     362          12 :   cur_hpos += w;
     363             : }
     364             : 
     365           4 : int lj4_printer::moveto1(int hpos, int vpos)
     366             : {
     367           4 :   if (hpos < x_offset || vpos < 0)
     368           0 :     return 0;
     369           4 :   fputs("\033*p", stdout);
     370           4 :   if (cur_hpos < 0)
     371           1 :     printf("%dx%dY", hpos - x_offset, vpos);
     372             :   else {
     373           3 :     if (cur_hpos != hpos)
     374           6 :       printf("%s%d%c", (hpos > cur_hpos) ? "+" : "",
     375           6 :              hpos - cur_hpos, (vpos == cur_vpos) ? 'X' : 'x');
     376           3 :     if (cur_vpos != vpos)
     377           0 :       printf("%s%dY", (vpos > cur_vpos) ? "+" : "", vpos - cur_vpos);
     378             :   }
     379           4 :   cur_hpos = hpos;
     380           4 :   cur_vpos = vpos;
     381           4 :   return 1;
     382             : }
     383             : 
     384           0 : void lj4_printer::draw(int code, int *p, int np, const environment *env)
     385             : {
     386           0 :   switch (code) {
     387           0 :   case 'R':
     388             :     {
     389           0 :       if (np != 2) {
     390           0 :         error("2 arguments required for rule");
     391           0 :         break;
     392             :       }
     393           0 :       int hpos = env->hpos;
     394           0 :       int vpos = env->vpos;
     395           0 :       int hsize = p[0];
     396           0 :       int vsize = p[1];
     397           0 :       if (hsize < 0) {
     398           0 :         hpos += hsize;
     399           0 :         hsize = -hsize;
     400             :       }
     401           0 :       if (vsize < 0) {
     402           0 :         vpos += vsize;
     403           0 :         vsize = -vsize;
     404             :       }
     405           0 :       if (!moveto(hpos, vpos))
     406           0 :         return;
     407           0 :       printf("\033*c%da%db0P", hsize, vsize);
     408           0 :       break;
     409             :     }
     410           0 :   case 'l':
     411           0 :     if (np != 2) {
     412           0 :       error("2 arguments required for line");
     413           0 :       break;
     414             :     }
     415           0 :     hpgl_init();
     416           0 :     if (!moveto(env->hpos, env->vpos))
     417           0 :       return;
     418           0 :     hpgl_start();
     419           0 :     set_line_thickness(env->size, 0 == p[0] && 0 == p[1]);
     420           0 :     printf("PD%d,%d", p[0], p[1]);
     421           0 :     hpgl_end();
     422           0 :     break;
     423           0 :   case 'p':
     424             :   case 'P':
     425             :     {
     426           0 :       if ((np % 2) == 1) {
     427           0 :         error("even number of arguments required for polygon");
     428           0 :         break;
     429             :       }
     430           0 :       if (0 == np) {
     431           0 :         error("no arguments for polygon");
     432           0 :         break;
     433             :       }
     434           0 :       hpgl_init();
     435           0 :       if (!moveto(env->hpos, env->vpos))
     436           0 :         return;
     437           0 :       hpgl_start();
     438           0 :       if ('p' == code)
     439           0 :         set_line_thickness(env->size);
     440           0 :       printf("PMPD%d", p[0]);
     441           0 :       for (int i = 1; i < np; i++)
     442           0 :         printf(",%d", p[i]);
     443           0 :       printf("PM2%cP", ('p' == code) ? 'E' : 'F');
     444           0 :       hpgl_end();
     445           0 :       break;
     446             :     }
     447           0 :   case '~':
     448             :     {
     449           0 :       if ((np % 2) == 1) {
     450           0 :         error("even number of arguments required for spline");
     451           0 :         break;
     452             :       }
     453           0 :       if (0 == np) {
     454           0 :         error("no arguments for spline");
     455           0 :         break;
     456             :       }
     457           0 :       hpgl_init();
     458           0 :       if (!moveto(env->hpos, env->vpos))
     459           0 :         return;
     460           0 :       hpgl_start();
     461           0 :       set_line_thickness(env->size);
     462           0 :       printf("PD%d,%d", p[0]/2, p[1]/2);
     463           0 :       const int tnum = 2;
     464           0 :       const int tden = 3;
     465           0 :       if (np > 2) {
     466           0 :         fputs("BR", stdout);
     467           0 :         for (int i = 0; i < np - 2; i += 2) {
     468           0 :           if (i != 0)
     469           0 :             putchar(',');
     470           0 :           printf("%d,%d,%d,%d,%d,%d",
     471           0 :                  (p[i]*tnum)/(2*tden),
     472           0 :                  (p[i + 1]*tnum)/(2*tden),
     473           0 :                  p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden),
     474           0 :                  p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden),
     475           0 :                  (p[i] - p[i]/2) + p[i + 2]/2,
     476           0 :                  (p[i + 1] - p[i + 1]/2) + p[i + 3]/2);
     477             :         }
     478             :       }
     479           0 :       printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2);
     480           0 :       hpgl_end();
     481           0 :       break;
     482             :     }
     483           0 :   case 'c':
     484             :   case 'C':
     485             :     // troff adds an extra argument to C
     486           0 :     if (np != 1 && !(('C' == code) && (2 == np))) {
     487           0 :       error("1 argument required for circle");
     488           0 :       break;
     489             :     }
     490           0 :     hpgl_init();
     491           0 :     if (!moveto(env->hpos + p[0]/2, env->vpos))
     492           0 :       return;
     493           0 :     hpgl_start();
     494           0 :     if ('c' == code) {
     495           0 :       set_line_thickness(env->size);
     496           0 :       printf("CI%d", p[0]/2);
     497             :     }
     498             :     else
     499           0 :       printf("WG%d,0,360", p[0]/2);
     500           0 :     hpgl_end();
     501           0 :     break;
     502           0 :   case 'e':
     503             :   case 'E':
     504           0 :     if (np != 2) {
     505           0 :       error("2 arguments required for ellipse");
     506           0 :       break;
     507             :     }
     508           0 :     hpgl_init();
     509           0 :     if (!moveto(env->hpos + p[0]/2, env->vpos))
     510           0 :       return;
     511           0 :     hpgl_start();
     512           0 :     printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale);
     513           0 :     if ('e' == code) {
     514           0 :       set_line_thickness(env->size);
     515           0 :       printf("CI%d", p[1]/2);
     516             :     }
     517             :     else
     518           0 :       printf("WG%d,0,360", p[1]/2);
     519           0 :     printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale);
     520           0 :     hpgl_end();
     521           0 :     break;
     522           0 :   case 'a':
     523             :     {
     524           0 :       if (np != 4) {
     525           0 :         error("4 arguments required for arc");
     526           0 :         break;
     527             :       }
     528           0 :       hpgl_init();
     529           0 :       if (!moveto(env->hpos, env->vpos))
     530           0 :         return;
     531           0 :       hpgl_start();
     532           0 :       set_line_thickness(env->size);
     533             :       double c[2];
     534           0 :       if (adjust_arc_center(p, c)) {
     535           0 :         double sweep = ((atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])
     536           0 :                          - atan2(-c[1], -c[0]))
     537           0 :                         * 180.0/PI);
     538           0 :         if (sweep > 0.0)
     539           0 :           sweep -= 360.0;
     540           0 :         printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep);
     541             :       }
     542             :       else
     543           0 :         printf("PD%d,%d", p[0] + p[2], p[1] + p[3]);
     544           0 :       hpgl_end();
     545             :     }
     546           0 :     break;
     547           0 :   case 'f':
     548           0 :     if (np != 1 && np != 2) {
     549           0 :       error("1 argument required for fill");
     550           0 :       break;
     551             :     }
     552           0 :     hpgl_init();
     553           0 :     hpgl_start();
     554           0 :     if (p[0] >= 0 && p[0] <= 1000)
     555           0 :       printf("FT10,%d", p[0]/10);
     556           0 :     hpgl_end();
     557           0 :     break;
     558           0 :   case 'F':
     559             :     // not implemented yet
     560           0 :     break;
     561           0 :   case 't':
     562             :     {
     563           0 :       if (0 == np) {
     564           0 :         line_thickness = -1;
     565             :       }
     566             :       else {
     567             :         // troff gratuitously adds an extra 0
     568           0 :         if (np != 1 && np != 2) {
     569           0 :           error("0 or 1 argument required for thickness");
     570           0 :           break;
     571             :         }
     572           0 :         line_thickness = p[0];
     573             :       }
     574           0 :       break;
     575             :     }
     576           0 :   default:
     577           0 :     error("unrecognized drawing command '%1'", char(code));
     578           0 :     break;
     579             :   }
     580             : }
     581             : 
     582           0 : void lj4_printer::hpgl_init()
     583             : {
     584           0 :   if (hpgl_inited)
     585           0 :     return;
     586           0 :   hpgl_inited = 1;
     587           0 :   hpgl_scale = double(DEFAULT_HPGL_UNITS)/font::res;
     588           0 :   printf("\033&f0S"               // push position
     589             :          "\033*p0x0Y"         // move to 0,0
     590             :          "\033*c%dx%dy0T"     // establish picture frame
     591             :          "\033%%1B"           // switch to HPGL
     592             :          "SP1SC0,%.4f,0,-%.4f,2IR0,100,0,100" // set up scaling
     593             :          "LA1,4,2,4"          // round line ends and joins
     594             :          "PR"                 // relative plotting
     595             :          "TR0"                        // opaque
     596             :          ";\033%%1A"          // back to PCL
     597             :          "\033&f1S",              // pop position
     598             :          MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT,
     599             :          hpgl_scale, hpgl_scale);
     600             : }
     601             : 
     602           0 : void lj4_printer::set_line_thickness(int size, int dot)
     603             : {
     604             :   double pw;
     605           0 :   if (line_thickness < 0)
     606           0 :     pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0);
     607             :   else
     608           0 :     pw = line_thickness*25.4/font::res;
     609           0 :   if (dot && pw < MIN_DOT_PEN_WIDTH)
     610           0 :     pw = MIN_DOT_PEN_WIDTH;
     611           0 :   if (pw != pen_width) {
     612           0 :     printf("PW%f", pw);
     613           0 :     pen_width = pw;
     614             :   }
     615           0 : }
     616             : 
     617           1 : font *lj4_printer::make_font(const char *nm)
     618             : {
     619           1 :   return lj4_font::load_lj4_font(nm);
     620             : }
     621             : 
     622           1 : printer *make_printer()
     623             : {
     624           1 :   return new lj4_printer(user_paper_size);
     625             : }
     626             : 
     627             : static void usage(FILE *stream);
     628             : 
     629             : extern "C" int optopt, optind;
     630             : 
     631           1 : int main(int argc, char **argv)
     632             : {
     633           1 :   setlocale(LC_NUMERIC, "C");
     634           1 :   program_name = argv[0];
     635             :   static char stderr_buf[BUFSIZ];
     636           1 :   setbuf(stderr, stderr_buf);
     637             :   int c;
     638             :   static const struct option long_options[] = {
     639             :     { "help", no_argument, 0 /* nullptr */, CHAR_MAX + 1 },
     640             :     { "version", no_argument, 0 /* nullptr */, 'v' },
     641             :     { 0 /* nullptr */, 0, 0 /* nullptr */, 0 }
     642             :   };
     643           1 :   while ((c = getopt_long(argc, argv, ":c:d:F:I:lp:vw:", long_options,
     644             :                           0 /* nullptr */))
     645           1 :          != EOF)
     646           0 :     switch (c) {
     647           0 :     case 'l':
     648           0 :       landscape_flag = 1;
     649           0 :       break;
     650           0 :     case 'I':
     651             :       // ignore include search path
     652           0 :       break;
     653           0 :     case 'd':
     654           0 :       if (!csdigit(*optarg))    // this ugly hack prevents -d without
     655           0 :         optind--;               //  args from messing up the arg list
     656           0 :       duplex_flag = atoi(optarg);
     657           0 :       if (duplex_flag != 1 && duplex_flag != 2) {
     658           0 :         fprintf(stderr, "argument to command-line option 'd' out of"
     659             :                 " range; assuming long-side duplexing\n");
     660           0 :         duplex_flag = 1;
     661             :       }
     662           0 :       break;
     663           0 :     case 'p':
     664             :       {
     665           0 :         ssize_t n = lookup_paper_size(optarg);
     666           0 :         if (n < 0)
     667           0 :           error("ignoring invalid paper format '%1'", font::papersize);
     668             :         else
     669           0 :           user_paper_size = n;
     670           0 :         break;
     671             :       }
     672           0 :     case 'v':
     673           0 :       printf("GNU grolj4 (groff) version %s\n", Version_string);
     674           0 :       exit(EXIT_SUCCESS);
     675             :       break;
     676           0 :     case 'F':
     677           0 :       font::command_line_font_dir(optarg);
     678           0 :       break;
     679           0 :     case 'c':
     680             :       {
     681             :         char *ptr;
     682           0 :         long n = strtol(optarg, &ptr, 10);
     683           0 :         if (ptr == optarg)
     684           0 :           error("argument to command-line option 'c' option must be a"
     685             :                 " positive integer");
     686           0 :         else if (n <= 0 || n > 32767)
     687           0 :           error("argument to command-line option 'c' must be in range"
     688           0 :                 " (0, 32767]; got '%1'", optarg);
     689             :         else
     690           0 :           ncopies = unsigned(n);
     691           0 :         break;
     692             :       }
     693           0 :     case 'w':
     694             :       {
     695             :         char *ptr;
     696           0 :         long n = strtol(optarg, &ptr, 10);
     697           0 :         if (ptr == optarg)
     698           0 :           error("argument to command-line option 'w' must be a"
     699             :                 " non-negative integer");
     700           0 :         else if (n < 0 || n > INT_MAX)
     701           0 :           error("argument to command-line option 'w' must be in range"
     702           0 :                 " [0, %1]; got '%2'", INT_MAX, optarg);
     703             :         else
     704           0 :           line_width_factor = int(n);
     705           0 :         break;
     706             :       }
     707           0 :     case CHAR_MAX + 1: // --help
     708           0 :       usage(stdout);
     709           0 :       exit(EXIT_SUCCESS);
     710             :       break;
     711           0 :     case '?':
     712           0 :       if (optopt != 0)
     713           0 :         error("unrecognized command-line option '%1'", char(optopt));
     714             :       else
     715           0 :         error("unrecognized command-line option '%1'",
     716           0 :               argv[(optind - 1)]);
     717           0 :       usage(stderr);
     718           0 :       exit(2);
     719             :       break;
     720           0 :     case ':':
     721           0 :       if ('d' == optopt) {
     722           0 :         warning("command-line option 'd' requires an argument; assuming"
     723             :                 " duplexing on long side");
     724           0 :         duplex_flag = 1;
     725             :       }
     726             :       else {
     727           0 :         error("command-line option '%1' requires an argument",
     728           0 :               char(optopt));
     729           0 :         usage(stderr);
     730           0 :         exit(2);
     731             :       }
     732           0 :       break;
     733           0 :     default:
     734           0 :       assert(0 == "unhandled getopt_long return value");
     735             :     }
     736             :   SET_BINARY(fileno(stdout));
     737           1 :   if (optind >= argc)
     738           1 :     do_file("-");
     739             :   else {
     740           0 :     for (int i = optind; i < argc; i++)
     741           0 :       do_file(argv[i]);
     742             :   }
     743           1 :   return 0;
     744             : }
     745             : 
     746           0 : static void usage(FILE *stream)
     747             : {
     748           0 :   fprintf(stream,
     749             : "usage: %s [-l] [-c n] [-d [n]] [-F dir] [-p paper-format] [-w n]"
     750             : " [file ...]\n"
     751             : "usage: %s {-v | --version}\n"
     752             : "usage: %s --help\n",
     753             :           program_name, program_name, program_name);
     754           0 :   if (stdout == stream)
     755           0 :     fputs("\n"
     756             : "Translate the output of troff(1) into PCL 5/LaserJet 4 format.  See\n"
     757             : "the grolj4(1) manual page.\n", stream);
     758           0 : }
     759             : 
     760             : // Local Variables:
     761             : // fill-column: 72
     762             : // mode: C++
     763             : // End:
     764             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14