LCOV - code coverage report
Current view: top level - devices/grodvi - dvi.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 246 595 41.3 %
Date: 2026-01-16 17:51:41 Functions: 34 43 79.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 1989-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             : #ifdef HAVE_CONFIG_H
      20             : #include <config.h>
      21             : #endif
      22             : 
      23             : #include <assert.h>
      24             : #include <limits.h> // CHAR_MAX
      25             : #include <locale.h> // setlocale()
      26             : #include <math.h> // atan2(), sqrt()
      27             : #include <stdio.h> // EOF, FILE, fprintf(), printf(), putc(), setbuf(),
      28             :                    // sprintf(), stderr, stdout
      29             : #include <stdlib.h> // exit(), EXIT_SUCCESS, strtol()
      30             : #include <string.h> // strcmp(), strlen()
      31             : 
      32             : #include <getopt.h> // getopt_long()
      33             : 
      34             : #include "driver.h"
      35             : #include "nonposix.h"
      36             : #include "paper.h"
      37             : 
      38             : extern "C" const char *Version_string;
      39             : 
      40             : #define DEFAULT_LINEWIDTH 40
      41             : static int linewidth = DEFAULT_LINEWIDTH;
      42             : 
      43             : static int draw_flag = 1;
      44             : 
      45             : static int landscape_flag = 0;
      46             : static double user_paper_length = 0;
      47             : static double user_paper_width = 0;
      48             : 
      49             : /* These values were chosen because:
      50             : 
      51             : (MULTIPLIER*SIZESCALE)/(RES*UNITWIDTH) == 1/(2^20 * 72.27)
      52             : 
      53             : and 57816 is an exact multiple of both 72.27*SIZESCALE and 72.
      54             : 
      55             : The width in the groff font file is the product of MULTIPLIER and the
      56             : width in the tfm file. */
      57             : 
      58             : #define RES 57816
      59             : #define RES_7227 (RES/7227)
      60             : #define UNITWIDTH 131072
      61             : #define SIZESCALE 100
      62             : #define MULTIPLIER 1
      63             : 
      64             : class dvi_font : public font {
      65             :   dvi_font(const char *);
      66             : public:
      67             :   int checksum;
      68             :   int design_size;
      69             :   ~dvi_font();
      70             :   void handle_unknown_font_command(const char * /* command */,
      71             :                                    const char * /* arg */,
      72             :                                    const char * /* fn */,
      73             :                                    int /* lineno */);
      74             :   static dvi_font *load_dvi_font(const char *);
      75             : };
      76             : 
      77           1 : dvi_font *dvi_font::load_dvi_font(const char *s)
      78             : {
      79           1 :   dvi_font *f = new dvi_font(s);
      80           1 :   if (!f->load()) {
      81           0 :     delete f;
      82           0 :     return 0 /* nullptr */;
      83             :   }
      84           1 :   return f;
      85             : }
      86             : 
      87           1 : dvi_font::dvi_font(const char *nm)
      88           1 : : font(nm), checksum(0), design_size(0)
      89             : {
      90           1 : }
      91             : 
      92           2 : dvi_font::~dvi_font()
      93             : {
      94           2 : }
      95             : 
      96           1 : void dvi_font::handle_unknown_font_command(const char *command,
      97             :                                            const char *arg,
      98             :                                            const char *fn,
      99             :                                            int lineno)
     100             : {
     101             :   char *ptr;
     102           1 :   if (strcmp(command, "checksum") == 0) {
     103           0 :     if (arg == 0)
     104           0 :       fatal_with_file_and_line(fn, lineno,
     105             :                                "'checksum' command requires an argument");
     106           0 :     checksum = int(strtol(arg, &ptr, 10));
     107           0 :     if (ptr == arg) {
     108           0 :       fatal_with_file_and_line(fn, lineno, "bad checksum");
     109             :     }
     110             :   }
     111           1 :   else if (strcmp(command, "designsize") == 0) {
     112           1 :     if (arg == 0)
     113           0 :       fatal_with_file_and_line(fn, lineno,
     114             :                                "'designsize' command requires an argument");
     115           1 :     design_size = int(strtol(arg, &ptr, 10));
     116           1 :     if (ptr == arg) {
     117           0 :       fatal_with_file_and_line(fn, lineno, "bad design size");
     118             :     }
     119             :   }
     120           1 : }
     121             : 
     122             : #define FONTS_MAX 256
     123             : 
     124             : struct output_font {
     125             :   dvi_font *f;
     126             :   int point_size;
     127         256 :   output_font() : f(0 /* nullptr */) { }
     128             : };
     129             : 
     130             : class dvi_printer : public printer {
     131             :   FILE *fp;
     132             :   int max_drift;
     133             :   int byte_count;
     134             :   int last_bop;
     135             :   int page_count;
     136             :   int cur_h;
     137             :   int cur_v;
     138             :   int end_h;
     139             :   int max_h;
     140             :   int max_v;
     141             :   output_font output_font_table[FONTS_MAX];
     142             :   font *cur_font;
     143             :   int cur_point_size;
     144             :   color cur_color;
     145             :   int pushed;
     146             :   int pushed_h;
     147             :   int pushed_v;
     148             :   int have_pushed;
     149             :   void preamble();
     150             :   void postamble();
     151             :   void define_font(int /* mounting_position */);
     152             :   void set_font(int /* mounting_position */);
     153             :   void possibly_begin_line();
     154             :   void set_color(color * /* col */);
     155             : protected:
     156             :   enum {
     157             :     id_byte = 2,
     158             :     set1 = 128,
     159             :     put1 = 133,
     160             :     put_rule = 137,
     161             :     bop = 139,
     162             :     eop = 140,
     163             :     push = 141,
     164             :     pop = 142,
     165             :     right1 = 143,
     166             :     down1 = 157,
     167             :     fnt_num_0 = 171,
     168             :     fnt1 = 235,
     169             :     xxx1 = 239,
     170             :     fnt_def1 = 243,
     171             :     pre = 247,
     172             :     post = 248,
     173             :     post_post = 249,
     174             :     filler = 223
     175             :   };
     176             :   int line_thickness;
     177             : 
     178             :   void out1(int);
     179             :   void out2(int);
     180             :   void out3(int);
     181             :   void out4(int);
     182             :   void moveto(int, int);
     183             :   void out_string(const char *);
     184             :   void out_signed(unsigned char, int);
     185             :   void out_unsigned(unsigned char, int);
     186             :   void do_special(const char *);
     187             : public:
     188             :   dvi_printer();
     189             :   ~dvi_printer();
     190             :   font *make_font(const char *);
     191             :   void begin_page(int);
     192             :   void end_page(int);
     193             :   void set_char(glyph * /* g */, font * /* f */,
     194             :                 const environment * /* env */ , int /* w */,
     195             :                 const char *);
     196             :   void special(char * /* arg */, const environment * /* env* */,
     197             :                char /* type */);
     198             :   void end_of_line();
     199             :   void draw(int /* code */, int * /* p */, int /* np */,
     200             :             const environment * /* env */);
     201             : };
     202             : 
     203             : 
     204             : class draw_dvi_printer : public dvi_printer {
     205             :   int output_pen_size;
     206             :   void set_line_thickness(const environment * /* env */);
     207             :   void fill_next(const environment * /* env */);
     208             : public:
     209             :   draw_dvi_printer();
     210             :   ~draw_dvi_printer();
     211             :   void draw(int /* code */, int * /* p */, int /* np */,
     212             :             const environment * /* env */);
     213             :   void end_page(int);
     214             : };
     215             : 
     216           1 : dvi_printer::dvi_printer()
     217             : : fp(stdout), byte_count(0), last_bop(-1), page_count(0),
     218             :   max_h(0), max_v(0), cur_font(0 /* nullptr */), cur_point_size(-1),
     219         257 :   pushed(0), line_thickness(-1)
     220             : {
     221           1 :   if (font::res != RES)
     222           0 :     fatal("resolution must be %1", RES);
     223           1 :   if (font::unitwidth != UNITWIDTH)
     224           0 :     fatal("unitwidth must be %1", UNITWIDTH);
     225           1 :   if (font::hor != 1)
     226           0 :     fatal("hor must be equal to 1");
     227           1 :   if (font::vert != 1)
     228           0 :     fatal("vert must be equal to 1");
     229           1 :   if (font::sizescale != SIZESCALE)
     230           0 :     fatal("sizescale must be equal to %1", SIZESCALE);
     231           1 :   max_drift = font::res/1000;   // this is fairly arbitrary
     232           1 :   preamble();
     233           1 : }
     234             : 
     235           1 : dvi_printer::~dvi_printer()
     236             : {
     237           1 :   current_lineno = 0; // At this point, we've read all the input.
     238           1 :   postamble();
     239           1 : }
     240             : 
     241             : 
     242           1 : draw_dvi_printer::draw_dvi_printer()
     243           1 : : output_pen_size(-1)
     244             : {
     245           1 : }
     246             : 
     247           2 : draw_dvi_printer::~draw_dvi_printer()
     248             : {
     249           2 : }
     250             : 
     251             : 
     252          87 : void dvi_printer::out1(int n)
     253             : {
     254          87 :   byte_count += 1;
     255          87 :   putc(n & 0xff, fp);
     256          87 : }
     257             : 
     258           5 : void dvi_printer::out2(int n)
     259             : {
     260           5 :   byte_count += 2;
     261           5 :   putc((n >> 8) & 0xff, fp);
     262           5 :   putc(n & 0xff, fp);
     263           5 : }
     264             : 
     265           1 : void dvi_printer::out3(int n)
     266             : {
     267           1 :   byte_count += 3;
     268           1 :   putc((n >> 16) & 0xff, fp);
     269           1 :   putc((n >> 8) & 0xff, fp);
     270           1 :   putc(n & 0xff, fp);
     271           1 : }
     272             : 
     273          27 : void dvi_printer::out4(int n)
     274             : {
     275          27 :   byte_count += 4;
     276          27 :   putc((n >> 24) & 0xff, fp);
     277          27 :   putc((n >> 16) & 0xff, fp);
     278          27 :   putc((n >> 8) & 0xff, fp);
     279          27 :   putc(n & 0xff, fp);
     280          27 : }
     281             : 
     282           2 : void dvi_printer::out_string(const char *s)
     283             : {
     284           2 :   out1(strlen(s));
     285          16 :   while (*s != 0)
     286          14 :     out1(*s++);
     287           2 : }
     288             : 
     289             : 
     290           2 : void dvi_printer::end_of_line()
     291             : {
     292           2 :   if (pushed) {
     293           2 :     out1(pop);
     294           2 :     pushed = 0;
     295           2 :     cur_h = pushed_h;
     296           2 :     cur_v = pushed_v;
     297             :   }
     298           2 : }
     299             : 
     300           5 : void dvi_printer::possibly_begin_line()
     301             : {
     302           5 :   if (!pushed) {
     303           2 :     have_pushed = pushed = 1;
     304           2 :     pushed_h = cur_h;
     305           2 :     pushed_v = cur_v;
     306           2 :     out1(push);
     307             :   }
     308           5 : }
     309             : 
     310           3 : int scale(int x, int z)
     311             : {
     312             :   int sw;
     313             :   int a, b, c, d;
     314             :   int alpha, beta;
     315           3 :   alpha = 16*z; beta = 16;
     316           3 :   while (z >= 040000000L) {
     317           0 :     z /= 2; beta /= 2;
     318             :   }
     319           3 :   d = x & 255;
     320           3 :   c = (x >> 8) & 255;
     321           3 :   b = (x >> 16) & 255;
     322           3 :   a = (x >> 24) & 255;
     323           3 :   sw = (((((d * z) / 0400) + (c * z)) / 0400) + (b * z)) / beta;
     324           3 :   if (a == 255)
     325           0 :     sw -= alpha;
     326             :   else
     327           3 :     assert(a == 0);
     328           3 :   return sw;
     329             : }
     330             : 
     331           1 : void dvi_printer::set_color(color *col)
     332             : {
     333           1 :   cur_color = *col;
     334             :   char buf[256];
     335             :   unsigned int components[4];
     336           1 :   color_scheme cs = col->get_components(components);
     337           1 :   switch (cs) {
     338           1 :   case DEFAULT:
     339           1 :     sprintf(buf, "color gray 0");
     340           1 :     break;
     341           0 :   case RGB:
     342           0 :     sprintf(buf, "color rgb %.3g %.3g %.3g",
     343           0 :                  double(Red) / double(color::MAX_COLOR_VAL),
     344           0 :                  double(Green) / double(color::MAX_COLOR_VAL),
     345           0 :                  double(Blue) / double(color::MAX_COLOR_VAL));
     346           0 :     break;
     347           0 :   case CMY:
     348           0 :     col->get_cmyk(&Cyan, &Magenta, &Yellow, &Black);
     349             :     // fall through
     350           0 :   case CMYK:
     351           0 :     sprintf(buf, "color cmyk %.3g %.3g %.3g %.3g",
     352           0 :                  double(Cyan) / double(color::MAX_COLOR_VAL),
     353           0 :                  double(Magenta) / double(color::MAX_COLOR_VAL),
     354           0 :                  double(Yellow) / double(color::MAX_COLOR_VAL),
     355           0 :                  double(Black) / double(color::MAX_COLOR_VAL));
     356           0 :     break;
     357           0 :   case GRAY:
     358           0 :     sprintf(buf, "color gray %.3g",
     359           0 :                  double(Gray) / double(color::MAX_COLOR_VAL));
     360           0 :     break;
     361             :   }
     362           1 :   do_special(buf);
     363           1 : }
     364             : 
     365           3 : void dvi_printer::set_char(glyph *g, font *f, const environment *env,
     366             :                            int w, const char *)
     367             : {
     368           3 :   if (*env->col != cur_color)
     369           0 :     set_color(env->col);
     370           3 :   int code = f->get_code(g);
     371           3 :   if (env->size != cur_point_size || f != cur_font) {
     372           1 :     cur_font = f;
     373           1 :     cur_point_size = env->size;
     374             :     int i;
     375           1 :     for (i = 0;; i++) {
     376           1 :       if (i >= FONTS_MAX) {
     377           0 :         fatal("too many output fonts required");
     378             :       }
     379           1 :       if (output_font_table[i].f == 0) {
     380           1 :         output_font_table[i].f = (dvi_font *)cur_font;
     381           1 :         output_font_table[i].point_size = cur_point_size;
     382           1 :         define_font(i);
     383             :       }
     384           1 :       if (output_font_table[i].f == cur_font
     385           1 :           && output_font_table[i].point_size == cur_point_size)
     386           1 :         break;
     387             :     }
     388           1 :     set_font(i);
     389             :   }
     390           3 :   int distance = env->hpos - cur_h;
     391           3 :   if (env->hpos != end_h && distance != 0) {
     392           0 :     out_signed(right1, distance);
     393           0 :     cur_h = env->hpos;
     394             :   }
     395           3 :   else if (distance > max_drift) {
     396           0 :     out_signed(right1, distance - max_drift);
     397           0 :     cur_h = env->hpos - max_drift;
     398             :   }
     399           3 :   else if (distance < -max_drift) {
     400           0 :     out_signed(right1, distance + max_drift);
     401           0 :     cur_h = env->hpos + max_drift;
     402             :   }
     403           3 :   if (env->vpos != cur_v) {
     404           1 :     out_signed(down1, env->vpos - cur_v);
     405           1 :     cur_v = env->vpos;
     406             :   }
     407           3 :   possibly_begin_line();
     408           3 :   end_h = env->hpos + w;
     409           3 :   cur_h += scale(f->get_width(g, UNITWIDTH) / MULTIPLIER,
     410           3 :                  cur_point_size * RES_7227);
     411           3 :   if (cur_h > max_h)
     412           3 :     max_h = cur_h;
     413           3 :   if (cur_v > max_v)
     414           1 :     max_v = cur_v;
     415           3 :   if (code >= 0 && code <= 127)
     416           0 :     out1(code);
     417             :   else
     418           3 :     out_unsigned(set1, code);
     419           3 : }
     420             : 
     421           2 : void dvi_printer::define_font(int mounting_position)
     422             : {
     423           2 :   out_unsigned(fnt_def1, mounting_position);
     424           2 :   dvi_font *f = output_font_table[mounting_position].f;
     425           2 :   out4(f->checksum);
     426           2 :   out4(output_font_table[mounting_position].point_size*RES_7227);
     427           2 :   out4(int((double(f->design_size)/(1<<20))*RES_7227*100 + .5));
     428           2 :   const char *nm = f->get_internal_name();
     429           2 :   out1(0);
     430           2 :   out_string(nm);
     431           2 : }
     432             : 
     433           1 : void dvi_printer::set_font(int mounting_position)
     434             : {
     435           1 :   if (mounting_position >= 0 && mounting_position <= 63)
     436           1 :     out1(fnt_num_0 + mounting_position);
     437             :   else
     438           0 :     out_unsigned(fnt1, mounting_position);
     439           1 : }
     440             : 
     441           1 : void dvi_printer::out_signed(unsigned char base, int param)
     442             : {
     443           1 :   if (-128 <= param && param < 128) {
     444           0 :     out1(base);
     445           0 :     out1(param);
     446             :   }
     447           1 :   else if (-32768 <= param && param < 32768) {
     448           0 :     out1(base+1);
     449           0 :     out2(param);
     450             :   }
     451           1 :   else if (-(1 << 23) <= param && param < (1 << 23)) {
     452           1 :     out1(base+2);
     453           1 :     out3(param);
     454             :   }
     455             :   else {
     456           0 :     out1(base+3);
     457           0 :     out4(param);
     458             :   }
     459           1 : }
     460             : 
     461           7 : void dvi_printer::out_unsigned(unsigned char base, int param)
     462             : {
     463           7 :   if (param >= 0) {
     464           7 :     if (param < 256) {
     465           4 :       out1(base);
     466           4 :       out1(param);
     467             :     }
     468           3 :     else if (param < 65536) {
     469           3 :       out1(base+1);
     470           3 :       out2(param);
     471             :     }
     472           0 :     else if (param < (1 << 24)) {
     473           0 :       out1(base+2);
     474           0 :       out3(param);
     475             :     }
     476             :     else {
     477           0 :       out1(base+3);
     478           0 :       out4(param);
     479             :     }
     480             :   }
     481             :   else {
     482           0 :     out1(base+3);
     483           0 :     out4(param);
     484             :   }
     485           7 : }
     486             : 
     487           1 : void dvi_printer::preamble()
     488             : {
     489           1 :   out1(pre);
     490           1 :   out1(id_byte);
     491           1 :   out4(254000);
     492           1 :   out4(font::res);
     493           1 :   out4(1000);
     494           1 :   out1(0);
     495           1 : }
     496             : 
     497           1 : void dvi_printer::postamble()
     498             : {
     499           1 :   int tem = byte_count;
     500           1 :   out1(post);
     501           1 :   out4(last_bop);
     502           1 :   out4(254000);
     503           1 :   out4(font::res);
     504           1 :   out4(1000);
     505           1 :   out4(max_v);
     506           1 :   out4(max_h);
     507           1 :   out2(have_pushed); // stack depth
     508           1 :   out2(page_count);
     509             :   int i;
     510           2 :   for (i = 0; i < FONTS_MAX && output_font_table[i].f != 0; i++)
     511           1 :     define_font(i);
     512           1 :   out1(post_post);
     513           1 :   out4(tem);
     514           1 :   out1(id_byte);
     515           7 :   for (i = 0; i < 4 || byte_count % 4 != 0; i++)
     516           6 :     out1(filler);
     517           1 : }  
     518             :   
     519           1 : void dvi_printer::begin_page(int i)
     520             : {
     521           1 :   page_count++;
     522           1 :   int tem = byte_count;
     523           1 :   out1(bop);
     524           1 :   out4(i);
     525          10 :   for (int j = 1; j < 10; j++)
     526           9 :     out4(0);
     527           1 :   out4(last_bop);
     528           1 :   last_bop = tem;
     529             :   // By convention position (0,0) in a dvi file is placed at (1in, 1in).
     530           1 :   cur_h = font::res;
     531           1 :   cur_v = font::res;
     532           1 :   end_h = 0;
     533           1 :   if (page_count == 1) {
     534             :     char buf[256];
     535             :     // at least dvips uses this
     536           1 :     double length = user_paper_length ? user_paper_length
     537           1 :                                       : (double(font::paperlength)
     538           1 :                                          / font::res);
     539           1 :     double width = user_paper_width ? user_paper_width
     540           1 :                                       : (double(font::paperwidth)
     541           1 :                                          / font::res);
     542           1 :     if (width > 0 && length > 0) {
     543           2 :       sprintf(buf, "papersize=%.3fin,%.3fin",
     544           1 :               landscape_flag ? length : width,
     545           1 :               landscape_flag ? width : length);
     546           1 :       do_special(buf);
     547             :     }
     548             :   }
     549           1 :   if (cur_color != default_color)
     550           0 :     set_color(&cur_color);
     551           1 : }
     552             : 
     553           1 : void dvi_printer::end_page(int)
     554             : {
     555           1 :   set_color(&default_color);
     556           1 :   if (pushed)
     557           1 :     end_of_line();
     558           1 :   out1(eop);
     559           1 :   cur_font = 0 /* nullptr */;
     560           1 : }
     561             : 
     562           1 : void draw_dvi_printer::end_page(int len)
     563             : {
     564           1 :   dvi_printer::end_page(len);
     565           1 :   output_pen_size = -1;
     566           1 : }
     567             : 
     568           2 : void dvi_printer::do_special(const char *s)
     569             : {
     570           2 :   size_t len = strlen(s);
     571           2 :   if (len == 0)
     572           0 :     return;
     573           2 :   possibly_begin_line();
     574           2 :   out_unsigned(xxx1, len);
     575          40 :   while (*s)
     576          38 :     out1(*s++);
     577             : }
     578             : 
     579           0 : void dvi_printer::special(char *arg, const environment *env, char type)
     580             : {
     581           0 :   if (type != 'p')
     582           0 :     return;
     583           0 :   moveto(env->hpos, env->vpos);
     584           0 :   do_special(arg);
     585             : }
     586             : 
     587           0 : void dvi_printer::moveto(int h, int v)
     588             : {
     589           0 :   if (h != cur_h) {
     590           0 :     out_signed(right1, h - cur_h);
     591           0 :     cur_h = h;
     592           0 :     if (cur_h > max_h)
     593           0 :       max_h = cur_h;
     594             :   }
     595           0 :   if (v != cur_v) {
     596           0 :     out_signed(down1, v - cur_v);
     597           0 :     cur_v = v;
     598           0 :     if (cur_v > max_v)
     599           0 :       max_v = cur_v;
     600             :   }
     601           0 :   end_h = 0;
     602           0 : }
     603             : 
     604           0 : void dvi_printer::draw(int code, int *p, int np, const environment *env)
     605             : {
     606           0 :   if (code == 'l') {
     607           0 :     int x = 0, y = 0;
     608           0 :     int height = 0, width = 0;
     609             :     int thickness;
     610           0 :     if (line_thickness < 0)
     611           0 :       thickness = env->size*RES_7227*linewidth/1000;
     612           0 :     else if (line_thickness > 0)
     613           0 :       thickness = line_thickness;
     614             :     else
     615           0 :       thickness = 1;
     616           0 :     if (np != 2) {
     617           0 :       error("2 arguments required for line");
     618             :     }
     619           0 :     else if (p[0] == 0) {
     620             :       // vertical rule
     621           0 :       if (p[1] > 0) {
     622           0 :         x = env->hpos - thickness/2;
     623           0 :         y = env->vpos + p[1] + thickness/2;
     624           0 :         height = p[1] + thickness;
     625           0 :         width = thickness;
     626             :       }
     627           0 :       else if (p[1] < 0) {
     628           0 :         x = env->hpos - thickness/2;
     629           0 :         y = env->vpos + thickness/2;
     630           0 :         height = thickness - p[1];
     631           0 :         width = thickness;
     632             :       }
     633             :     }
     634           0 :     else if (p[1] == 0) {
     635           0 :       if (p[0] > 0) {
     636           0 :         x = env->hpos - thickness/2;
     637           0 :         y = env->vpos + thickness/2;
     638           0 :         height = thickness;
     639           0 :         width = p[0] + thickness;
     640             :       }
     641           0 :       else if (p[0] < 0) {
     642           0 :         x = env->hpos - p[0] - thickness/2;
     643           0 :         y = env->vpos + thickness/2;
     644           0 :         height = thickness;
     645           0 :         width = thickness - p[0];
     646             :       }
     647             :     }
     648           0 :     if (height != 0) {
     649           0 :       moveto(x, y);
     650           0 :       out1(put_rule);
     651           0 :       out4(height);
     652           0 :       out4(width);
     653             :     }
     654             :   }
     655           0 :   else if (code == 't') {
     656           0 :     if (np == 0) {
     657           0 :       line_thickness = -1;
     658             :     }
     659             :     else {
     660             :       // troff gratuitously adds an extra 0
     661           0 :       if (np != 1 && np != 2)
     662           0 :         error("0 or 1 argument required for thickness");
     663             :       else
     664           0 :         line_thickness = p[0];
     665             :     }
     666             :   }
     667           0 :   else if (code == 'R') {
     668           0 :     if (np != 2)
     669           0 :       error("2 arguments required for rule");
     670           0 :     else if (p[0] != 0 || p[1] != 0) {
     671           0 :       int dh = p[0];
     672           0 :       int dv = p[1];
     673           0 :       int oh = env->hpos;
     674           0 :       int ov = env->vpos;
     675           0 :       if (dv > 0) {
     676           0 :         ov += dv;
     677           0 :         dv = -dv;
     678             :       }
     679           0 :       if (dh < 0) {
     680           0 :         oh += dh;
     681           0 :         dh = -dh;
     682             :       }
     683           0 :       moveto(oh, ov);
     684           0 :       out1(put_rule);
     685           0 :       out4(-dv);
     686           0 :       out4(dh);
     687             :     }
     688             :   }
     689           0 : }
     690             : 
     691             : // XXX Will this overflow?
     692             : 
     693           0 : inline int milliinches(int n)
     694             : {
     695           0 :   return (n*1000 + font::res/2)/font::res;
     696             : }
     697             : 
     698           0 : void draw_dvi_printer::set_line_thickness(const environment *env)
     699             : {
     700             :   int desired_pen_size
     701           0 :     = milliinches(line_thickness < 0
     702             :                   // Will this overflow?
     703           0 :                   ? env->size*RES_7227*linewidth/1000
     704             :                   : line_thickness);
     705           0 :   if (desired_pen_size != output_pen_size) {
     706             :     char buf[256];
     707           0 :     sprintf(buf, "pn %d", desired_pen_size);
     708           0 :     do_special(buf);
     709           0 :     output_pen_size = desired_pen_size;
     710             :   }
     711           0 : }
     712             : 
     713           0 : void draw_dvi_printer::fill_next(const environment *env)
     714             : {
     715             :   unsigned int g;
     716           0 :   if (env->fill->is_default())
     717           0 :     g = 0;
     718             :   else {
     719             :     // currently, only BW support
     720           0 :     env->fill->get_gray(&g);
     721             :   }
     722             :   char buf[256];
     723           0 :   sprintf(buf, "sh %.3g", 1 - double(g) / double(color::MAX_COLOR_VAL));
     724           0 :   do_special(buf);
     725           0 : }
     726             : 
     727           0 : void draw_dvi_printer::draw(int code, int *p, int np,
     728             :                             const environment *env)
     729             : {
     730             :   char buf[1024];
     731           0 :   int fill_flag = 0;
     732           0 :   switch (code) {
     733           0 :   case 'C':
     734           0 :     fill_flag = 1;
     735             :     // fall through
     736           0 :   case 'c':
     737             :     {
     738             :       // troff adds an extra argument to C
     739           0 :       if (np != 1 && !(code == 'C' && np == 2)) {
     740           0 :         error("1 argument required for circle");
     741           0 :         break;
     742             :       }
     743           0 :       moveto(env->hpos+p[0]/2, env->vpos);
     744           0 :       if (fill_flag)
     745           0 :         fill_next(env);
     746             :       else
     747           0 :         set_line_thickness(env);
     748             :       int rad;
     749           0 :       rad = milliinches(p[0]/2);
     750           0 :       sprintf(buf, "%s 0 0 %d %d 0 6.28319",
     751             :               (fill_flag ? "ia" : "ar"),
     752             :               rad,
     753             :               rad);
     754           0 :       do_special(buf);
     755           0 :       break;
     756             :     }
     757           0 :   case 'l':
     758           0 :     if (np != 2) {
     759           0 :       error("2 arguments required for line");
     760           0 :       break;
     761             :     }
     762           0 :     moveto(env->hpos, env->vpos);
     763           0 :     set_line_thickness(env);
     764           0 :     do_special("pa 0 0");
     765           0 :     sprintf(buf, "pa %d %d", milliinches(p[0]), milliinches(p[1]));
     766           0 :     do_special(buf);
     767           0 :     do_special("fp");
     768           0 :     break;
     769           0 :   case 'E':
     770           0 :     fill_flag = 1;
     771             :     // fall through
     772           0 :   case 'e':
     773           0 :     if (np != 2) {
     774           0 :       error("2 arguments required for ellipse");
     775           0 :       break;
     776             :     }
     777           0 :     moveto(env->hpos+p[0]/2, env->vpos);
     778           0 :     if (fill_flag)
     779           0 :       fill_next(env);
     780             :     else
     781           0 :       set_line_thickness(env);
     782           0 :     sprintf(buf, "%s 0 0 %d %d 0 6.28319",
     783             :             (fill_flag ? "ia" : "ar"),
     784           0 :             milliinches(p[0]/2),
     785           0 :             milliinches(p[1]/2));
     786           0 :     do_special(buf);
     787           0 :     break;
     788           0 :   case 'P':
     789           0 :     fill_flag = 1;
     790             :     // fall through
     791           0 :   case 'p':
     792             :     {
     793           0 :       if (np & 1) {
     794           0 :         error("even number of arguments required for polygon");
     795           0 :         break;
     796             :       }
     797           0 :       if (np == 0) {
     798           0 :         error("no arguments for polygon");
     799           0 :         break;
     800             :       }
     801           0 :       moveto(env->hpos, env->vpos);
     802           0 :       if (fill_flag)
     803           0 :         fill_next(env);
     804             :       else
     805           0 :         set_line_thickness(env);
     806           0 :       do_special("pa 0 0");
     807           0 :       int h = 0, v = 0;
     808           0 :       for (int i = 0; i < np; i += 2) {
     809           0 :         h += p[i];
     810           0 :         v += p[i+1];
     811           0 :         sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
     812           0 :         do_special(buf);
     813             :       }
     814           0 :       do_special("pa 0 0");
     815           0 :       do_special(fill_flag ? "ip" : "fp");
     816           0 :       break;
     817             :     }
     818           0 :   case '~':
     819             :     {
     820           0 :       if (np & 1) {
     821           0 :         error("even number of arguments required for spline");
     822           0 :         break;
     823             :       }
     824           0 :       if (np == 0) {
     825           0 :         error("no arguments for spline");
     826           0 :         break;
     827             :       }
     828           0 :       moveto(env->hpos, env->vpos);
     829           0 :       set_line_thickness(env);
     830           0 :       do_special("pa 0 0");
     831           0 :       int h = 0, v = 0;
     832           0 :       for (int i = 0; i < np; i += 2) {
     833           0 :         h += p[i];
     834           0 :         v += p[i+1];
     835           0 :         sprintf(buf, "pa %d %d", milliinches(h), milliinches(v));
     836           0 :         do_special(buf);
     837             :       }
     838           0 :       do_special("sp");
     839           0 :       break;
     840             :     }
     841           0 :   case 'a':
     842             :     {
     843           0 :       if (np != 4) {
     844           0 :         error("4 arguments required for arc");
     845           0 :         break;
     846             :       }
     847           0 :       set_line_thickness(env);
     848             :       double c[2];
     849           0 :       if (adjust_arc_center(p, c)) {
     850           0 :         int rad = milliinches(int(sqrt(c[0]*c[0] + c[1]*c[1]) + .5));
     851           0 :         moveto(env->hpos + int(c[0]), env->vpos + int(c[1]));
     852           0 :         double start = atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]);
     853           0 :         double end = atan2(-c[1], -c[0]);
     854           0 :         if (end - start < 0)
     855           0 :           start -= 2 * 3.14159265358;
     856           0 :         sprintf(buf, "ar 0 0 %d %d %f %f", rad, rad, start, end);
     857           0 :         do_special(buf);
     858             :       }
     859             :       else {
     860           0 :         moveto(env->hpos, env->vpos);
     861           0 :         do_special("pa 0 0");
     862           0 :         sprintf(buf,
     863             :                 "pa %d %d",
     864           0 :                 milliinches(p[0] + p[2]),
     865           0 :                 milliinches(p[1] + p[3]));
     866           0 :         do_special(buf);
     867           0 :         do_special("fp");
     868             :       }
     869           0 :       break;
     870             :     }
     871           0 :   case 't':
     872             :     {
     873           0 :       if (np == 0) {
     874           0 :         line_thickness = -1;
     875             :       }
     876             :       else {
     877             :         // troff gratuitously adds an extra 0
     878           0 :         if (np != 1 && np != 2) {
     879           0 :           error("0 or 1 argument required for thickness");
     880           0 :           break;
     881             :         }
     882           0 :         line_thickness = p[0];
     883             :       }
     884           0 :       break;
     885             :     }
     886           0 :   case 'R':
     887             :     {
     888           0 :       if (np != 2) {
     889           0 :         error("2 arguments required for rule");
     890           0 :         break;
     891             :       }
     892           0 :       int dh = p[0];
     893           0 :       if (dh == 0)
     894           0 :         break;
     895           0 :       int dv = p[1];
     896           0 :       if (dv == 0)
     897           0 :         break;
     898           0 :       int oh = env->hpos;
     899           0 :       int ov = env->vpos;
     900           0 :       if (dv > 0) {
     901           0 :         ov += dv;
     902           0 :         dv = -dv;
     903             :       }
     904           0 :       if (dh < 0) {
     905           0 :         oh += dh;
     906           0 :         dh = -dh;
     907             :       }
     908           0 :       moveto(oh, ov);
     909           0 :       out1(put_rule);
     910           0 :       out4(-dv);
     911           0 :       out4(dh);
     912           0 :       break;
     913             :     }
     914           0 :   default:
     915           0 :     error("unrecognised drawing command '%1'", char(code));
     916           0 :     break;
     917             :   }
     918           0 : }
     919             : 
     920           1 : font *dvi_printer::make_font(const char *nm)
     921             : {
     922           1 :   return dvi_font::load_dvi_font(nm);
     923             : }
     924             : 
     925           1 : printer *make_printer()
     926             : {
     927           1 :   if (draw_flag)
     928           1 :     return new draw_dvi_printer;
     929             :   else
     930           0 :     return new dvi_printer;
     931             : }
     932             : 
     933             : static void usage(FILE *stream);
     934             : 
     935           1 : int main(int argc, char **argv)
     936             : {
     937           1 :   setlocale(LC_NUMERIC, "C");
     938           1 :   program_name = argv[0];
     939             :   static char stderr_buf[BUFSIZ];
     940           1 :   setbuf(stderr, stderr_buf);
     941             :   int c;
     942             :   static const struct option long_options[] = {
     943             :     { "help", no_argument, 0 /* nullptr */, CHAR_MAX + 1 },
     944             :     { "version", no_argument, 0 /* nullptr */, 'v' },
     945             :     { 0 /* nullptr */, 0, 0, 0 }
     946             :   };
     947           1 :   while ((c = getopt_long(argc, argv, ":dF:I:lp:vw:", long_options,
     948             :                           0 /* nullptr */))
     949           1 :          != EOF)
     950           0 :     switch (c) {
     951           0 :     case 'd':
     952           0 :       draw_flag = 0;
     953           0 :       break;
     954           0 :     case 'l':
     955           0 :       landscape_flag = 1;
     956           0 :       break;
     957           0 :     case 'F':
     958           0 :       font::command_line_font_dir(optarg);
     959           0 :       break;
     960           0 :     case 'I':
     961             :       // ignore include search path
     962           0 :       break;
     963           0 :     case 'p':
     964           0 :       if (!font::scan_papersize(optarg, 0,
     965             :                                 &user_paper_length, &user_paper_width))
     966           0 :         error("ignoring invalid paper format '%1'", optarg);
     967           0 :       break;
     968           0 :     case 'v':
     969             :       {
     970           0 :         printf("GNU grodvi (groff) version %s\n", Version_string);
     971           0 :         exit(EXIT_SUCCESS);
     972             :         break;
     973             :       }
     974           0 :     case 'w':
     975           0 :       if (sscanf(optarg, "%d", &linewidth) != 1
     976           0 :           || linewidth < 0 || linewidth > 1000) {
     977           0 :         error("invalid line width '%1' ignored", optarg);
     978           0 :         linewidth = DEFAULT_LINEWIDTH;
     979             :       }
     980           0 :       break;
     981           0 :     case CHAR_MAX + 1: // --help
     982           0 :       usage(stdout);
     983           0 :       exit(EXIT_SUCCESS);
     984             :       break;
     985           0 :     case '?':
     986           0 :       if (optopt != 0)
     987           0 :         error("unrecognized command-line option '%1'", char(optopt));
     988             :       else
     989           0 :         error("unrecognized command-line option '%1'",
     990           0 :               argv[(optind - 1)]);
     991           0 :       usage(stderr);
     992           0 :       exit(2);
     993             :       break;
     994           0 :     case ':':
     995           0 :       error("command-line option '%1' requires an argument",
     996           0 :             char(optopt));
     997           0 :       usage(stderr);
     998           0 :       exit(2);
     999             :       break;
    1000           0 :     default:
    1001           0 :       assert(0 == "unhandled getopt_long return value");
    1002             :     }
    1003             :   SET_BINARY(fileno(stdout));
    1004           1 :   if (optind >= argc)
    1005           1 :     do_file("-");
    1006             :   else {
    1007           0 :     for (int i = optind; i < argc; i++)
    1008           0 :       do_file(argv[i]);
    1009             :   }
    1010           1 :   return 0;
    1011             : }
    1012             : 
    1013           0 : static void usage(FILE *stream)
    1014             : {
    1015           0 :   fprintf(stream,
    1016             : "usage: %s [-dl] [-F dir] [-p paper-format] [-w n] [file ...]\n"
    1017             : "usage: %s {-v | --version}\n"
    1018             : "usage: %s --help\n",
    1019             :           program_name, program_name, program_name);
    1020           0 :   if (stdout == stream)
    1021           0 :     fputs("\n"
    1022             : "Translate the output of troff(1) into TeX DVI format.  See the\n"
    1023             : "grodvi(1) manual page.\n", stream);
    1024           0 : }
    1025             : 
    1026             : // Local Variables:
    1027             : // fill-column: 72
    1028             : // mode: C++
    1029             : // End:
    1030             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14