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:
|