3 * Copyright (c) 2014 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
5 * Copyright (C) 1989 - 1992, 2000 - 2004, 2006 - 2007
6 * Free Software Foundation, Inc.
7 * Written by James Clark (jjc@jclark.com)
9 * This is free software; you can redistribute it and/or modify it under
10 * the terms of the GNU General Public License as published by the Free
11 * Software Foundation; either version 2, or (at your option) any later
14 * This is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 * You should have received a copy of the GNU General Public License along
20 * with groff; see the file COPYING. If not, write to the Free Software
21 * Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
25 #include "dvi-config.h"
31 static int linewidth
= DEFAULT_LINEWIDTH
;
33 static int draw_flag
= 1;
35 static int landscape_flag
= 0;
36 static double user_paper_length
= 0;
37 static double user_paper_width
= 0;
42 dvi_font(const char *);
49 void handle_unknown_font_command(const char *command
, const char *arg
,
50 const char *filename
, int lineno
);
52 static dvi_font
*load_dvi_font(const char *);
55 dvi_font
*dvi_font::load_dvi_font(const char *s
)
57 dvi_font
*f
= new dvi_font(s
);
65 dvi_font::dvi_font(const char *nm
)
66 : font(nm
), checksum(0), design_size(0)
74 void dvi_font::handle_unknown_font_command(const char *command
,
76 const char *filename
, int lineno
)
79 if (strcmp(command
, "checksum") == 0) {
81 fatal_with_file_and_line(filename
, lineno
,
82 "`checksum' command requires an argument");
83 checksum
= int(strtol(arg
, &ptr
, 10));
84 if (checksum
== 0 && ptr
== arg
) {
85 fatal_with_file_and_line(filename
, lineno
, "bad checksum");
88 else if (strcmp(command
, "designsize") == 0) {
90 fatal_with_file_and_line(filename
, lineno
,
91 "`designsize' command requires an argument");
92 design_size
= int(strtol(arg
, &ptr
, 10));
93 if (design_size
== 0 && ptr
== arg
) {
94 fatal_with_file_and_line(filename
, lineno
, "bad design size");
104 output_font() : f(0) { }
120 output_font output_font_table
[FONTS_MAX
];
131 void define_font(int);
133 void possibly_begin_line();
134 void set_color(color
*);
163 void moveto(int, int);
164 void out_string(const char *);
165 void out_signed(unsigned char, int);
166 void out_unsigned(unsigned char, int);
167 void do_special(const char *);
172 font
*make_font(const char *);
173 void begin_page(int);
175 void set_char(glyph
*, font
*, const environment
*, int, const char *);
176 void special(char *, const environment
*, char);
178 void draw(int, int *, int, const environment
*);
181 class draw_dvi_printer
185 void set_line_thickness(const environment
*);
186 void fill_next(const environment
*);
191 void draw(int code
, int *p
, int np
, const environment
*env
);
195 dvi_printer::dvi_printer()
196 : fp(stdout
), byte_count(0), last_bop(-1), page_count(0), max_h(0), max_v(0),
197 cur_font(0), cur_point_size(-1), pushed(0), line_thickness(-1)
199 if (font::res
!= RES
)
200 fatal("resolution must be %1", RES
);
201 if (font::unitwidth
!= UNITWIDTH
)
202 fatal("unitwidth must be %1", UNITWIDTH
);
204 fatal("hor must be equal to 1");
206 fatal("vert must be equal to 1");
207 if (font::sizescale
!= SIZESCALE
)
208 fatal("sizescale must be equal to %1", SIZESCALE
);
209 max_drift
= font::res
/1000; // this is fairly arbitrary
213 dvi_printer::~dvi_printer()
218 draw_dvi_printer::draw_dvi_printer()
219 : output_pen_size(-1)
223 draw_dvi_printer::~draw_dvi_printer()
227 void dvi_printer::out1(int n
)
233 void dvi_printer::out2(int n
)
236 putc((n
>> 8) & 0xff, fp
);
240 void dvi_printer::out3(int n
)
243 putc((n
>> 16) & 0xff, fp
);
244 putc((n
>> 8) & 0xff, fp
);
248 void dvi_printer::out4(int n
)
251 putc((n
>> 24) & 0xff, fp
);
252 putc((n
>> 16) & 0xff, fp
);
253 putc((n
>> 8) & 0xff, fp
);
257 void dvi_printer::out_string(const char *s
)
264 void dvi_printer::end_of_line()
274 void dvi_printer::possibly_begin_line()
277 have_pushed
= pushed
= 1;
284 int scale(int x
, int z
)
289 alpha
= 16*z
; beta
= 16;
290 while (z
>= 040000000L) {
297 sw
= (((((d
* z
) / 0400) + (c
* z
)) / 0400) + (b
* z
)) / beta
;
305 void dvi_printer::set_color(color
*col
)
309 unsigned int components
[4];
310 color_scheme cs
= col
->get_components(components
);
313 sprintf(buf
, "color gray 0");
316 sprintf(buf
, "color rgb %.3g %.3g %.3g",
317 double(Red
) / color::MAX_COLOR_VAL
,
318 double(Green
) / color::MAX_COLOR_VAL
,
319 double(Blue
) / color::MAX_COLOR_VAL
);
322 col
->get_cmyk(&Cyan
, &Magenta
, &Yellow
, &Black
);
325 sprintf(buf
, "color cmyk %.3g %.3g %.3g %.3g",
326 double(Cyan
) / color::MAX_COLOR_VAL
,
327 double(Magenta
) / color::MAX_COLOR_VAL
,
328 double(Yellow
) / color::MAX_COLOR_VAL
,
329 double(Black
) / color::MAX_COLOR_VAL
);
332 sprintf(buf
, "color gray %.3g",
333 double(Gray
) / color::MAX_COLOR_VAL
);
339 void dvi_printer::set_char(glyph
*g
, font
*f
, const environment
*env
,
342 if (*env
->col
!= cur_color
)
344 int code
= f
->get_code(g
);
345 if (env
->size
!= cur_point_size
|| f
!= cur_font
) {
347 cur_point_size
= env
->size
;
350 if (i
>= FONTS_MAX
) {
351 fatal("too many output fonts required");
353 if (output_font_table
[i
].f
== 0) {
354 output_font_table
[i
].f
= (dvi_font
*)cur_font
;
355 output_font_table
[i
].point_size
= cur_point_size
;
358 if (output_font_table
[i
].f
== cur_font
359 && output_font_table
[i
].point_size
== cur_point_size
)
364 int distance
= env
->hpos
- cur_h
;
365 if (env
->hpos
!= end_h
&& distance
!= 0) {
366 out_signed(right1
, distance
);
369 else if (distance
> max_drift
) {
370 out_signed(right1
, distance
- max_drift
);
371 cur_h
= env
->hpos
- max_drift
;
373 else if (distance
< -max_drift
) {
374 out_signed(right1
, distance
+ max_drift
);
375 cur_h
= env
->hpos
+ max_drift
;
377 if (env
->vpos
!= cur_v
) {
378 out_signed(down1
, env
->vpos
- cur_v
);
381 possibly_begin_line();
382 end_h
= env
->hpos
+ w
;
383 cur_h
+= scale(f
->get_width(g
, UNITWIDTH
) / MULTIPLIER
,
384 cur_point_size
* RES_7227
);
389 if (code
>= 0 && code
<= 127)
392 out_unsigned(set1
, code
);
395 void dvi_printer::define_font(int i
)
397 out_unsigned(fnt_def1
, i
);
398 dvi_font
*f
= output_font_table
[i
].f
;
400 out4(output_font_table
[i
].point_size
*RES_7227
);
401 out4(int((double(f
->design_size
)/(1<<20))*RES_7227
*100 + .5));
402 const char *nm
= f
->get_internal_name();
407 void dvi_printer::set_font(int i
)
409 if (i
>= 0 && i
<= 63)
412 out_unsigned(fnt1
, i
);
415 void dvi_printer::out_signed(unsigned char base
, int param
)
417 if (-128 <= param
&& param
< 128) {
421 else if (-32768 <= param
&& param
< 32768) {
425 else if (-(1 << 23) <= param
&& param
< (1 << 23)) {
435 void dvi_printer::out_unsigned(unsigned char base
, int param
)
442 else if (param
< 65536) {
446 else if (param
< (1 << 24)) {
461 void dvi_printer::preamble()
471 void dvi_printer::postamble()
473 int tem
= byte_count
;
481 out2(have_pushed
); // stack depth
484 for (i
= 0; i
< FONTS_MAX
&& output_font_table
[i
].f
!= 0; i
++)
489 for (i
= 0; i
< 4 || byte_count
% 4 != 0; i
++)
493 void dvi_printer::begin_page(int i
)
496 int tem
= byte_count
;
499 for (int j
= 1; j
< 10; j
++)
503 // By convention position (0,0) in a dvi file is placed at (1in, 1in).
507 if (page_count
== 1) {
509 // at least dvips uses this
510 double length
= user_paper_length
? user_paper_length
:
511 double(font::paperlength
) / font::res
;
512 double width
= user_paper_width
? user_paper_width
:
513 double(font::paperwidth
) / font::res
;
514 if (width
> 0 && length
> 0) {
515 sprintf(buf
, "papersize=%.3fin,%.3fin",
516 landscape_flag
? length
: width
,
517 landscape_flag
? width
: length
);
521 if (cur_color
!= default_color
)
522 set_color(&cur_color
);
525 void dvi_printer::end_page(int)
527 set_color(&default_color
);
534 void draw_dvi_printer::end_page(int len
)
536 dvi_printer::end_page(len
);
537 output_pen_size
= -1;
540 void dvi_printer::do_special(const char *s
)
545 possibly_begin_line();
546 out_unsigned(xxx1
, len
);
551 void dvi_printer::special(char *arg
, const environment
*env
, char type
)
555 moveto(env
->hpos
, env
->vpos
);
559 void dvi_printer::moveto(int h
, int v
)
562 out_signed(right1
, h
- cur_h
);
568 out_signed(down1
, v
- cur_v
);
576 void dvi_printer::draw(int code
, int *p
, int np
, const environment
*env
)
580 int height
= 0, width
= 0;
582 if (line_thickness
< 0)
583 thickness
= env
->size
*RES_7227
*linewidth
/1000;
584 else if (line_thickness
> 0)
585 thickness
= line_thickness
;
589 error("2 arguments required for line");
591 else if (p
[0] == 0) {
594 x
= env
->hpos
- thickness
/2;
595 y
= env
->vpos
+ p
[1] + thickness
/2;
596 height
= p
[1] + thickness
;
600 x
= env
->hpos
- thickness
/2;
601 y
= env
->vpos
+ thickness
/2;
602 height
= thickness
- p
[1];
606 else if (p
[1] == 0) {
608 x
= env
->hpos
- thickness
/2;
609 y
= env
->vpos
+ thickness
/2;
611 width
= p
[0] + thickness
;
614 x
= env
->hpos
- p
[0] - thickness
/2;
615 y
= env
->vpos
+ thickness
/2;
617 width
= thickness
- p
[0];
627 else if (code
== 't') {
632 // troff gratuitously adds an extra 0
633 if (np
!= 1 && np
!= 2)
634 error("0 or 1 argument required for thickness");
636 line_thickness
= p
[0];
639 else if (code
== 'R') {
641 error("2 arguments required for rule");
642 else if (p
[0] != 0 || p
[1] != 0) {
663 // XXX Will this overflow?
665 inline int milliinches(int n
)
667 return (n
*1000 + font::res
/2)/font::res
;
670 void draw_dvi_printer::set_line_thickness(const environment
*env
)
673 = milliinches(line_thickness
< 0
674 // Will this overflow?
675 ? env
->size
*RES_7227
*linewidth
/1000
677 if (desired_pen_size
!= output_pen_size
) {
679 sprintf(buf
, "pn %d", desired_pen_size
);
681 output_pen_size
= desired_pen_size
;
685 void draw_dvi_printer::fill_next(const environment
*env
)
688 if (env
->fill
->is_default())
691 // currently, only BW support
692 env
->fill
->get_gray(&g
);
695 sprintf(buf
, "sh %.3g", 1 - double(g
)/color::MAX_COLOR_VAL
);
699 void draw_dvi_printer::draw(int code
, int *p
, int np
, const environment
*env
)
709 // troff adds an extra argument to C
710 if (np
!= 1 && !(code
== 'C' && np
== 2)) {
711 error("1 argument required for circle");
714 moveto(env
->hpos
+p
[0]/2, env
->vpos
);
718 set_line_thickness(env
);
720 rad
= milliinches(p
[0]/2);
721 sprintf(buf
, "%s 0 0 %d %d 0 6.28319",
722 (fill_flag
? "ia" : "ar"),
730 error("2 arguments required for line");
733 moveto(env
->hpos
, env
->vpos
);
734 set_line_thickness(env
);
735 do_special("pa 0 0");
736 sprintf(buf
, "pa %d %d", milliinches(p
[0]), milliinches(p
[1]));
745 error("2 arguments required for ellipse");
748 moveto(env
->hpos
+p
[0]/2, env
->vpos
);
752 set_line_thickness(env
);
753 sprintf(buf
, "%s 0 0 %d %d 0 6.28319",
754 (fill_flag
? "ia" : "ar"),
756 milliinches(p
[1]/2));
765 error("even number of arguments required for polygon");
769 error("no arguments for polygon");
772 moveto(env
->hpos
, env
->vpos
);
776 set_line_thickness(env
);
777 do_special("pa 0 0");
779 for (int i
= 0; i
< np
; i
+= 2) {
782 sprintf(buf
, "pa %d %d", milliinches(h
), milliinches(v
));
785 do_special("pa 0 0");
786 do_special(fill_flag
? "ip" : "fp");
792 error("even number of arguments required for spline");
796 error("no arguments for spline");
799 moveto(env
->hpos
, env
->vpos
);
800 set_line_thickness(env
);
801 do_special("pa 0 0");
803 for (int i
= 0; i
< np
; i
+= 2) {
806 sprintf(buf
, "pa %d %d", milliinches(h
), milliinches(v
));
815 error("4 arguments required for arc");
818 set_line_thickness(env
);
820 if (adjust_arc_center(p
, c
)) {
821 int rad
= milliinches(int(sqrt(c
[0]*c
[0] + c
[1]*c
[1]) + .5));
822 moveto(env
->hpos
+ int(c
[0]), env
->vpos
+ int(c
[1]));
823 double start
= atan2(p
[1] + p
[3] - c
[1], p
[0] + p
[2] - c
[0]);
824 double end
= atan2(-c
[1], -c
[0]);
826 start
-= 2 * 3.14159265358;
827 sprintf(buf
, "ar 0 0 %d %d %f %f", rad
, rad
, start
, end
);
831 moveto(env
->hpos
, env
->vpos
);
832 do_special("pa 0 0");
835 milliinches(p
[0] + p
[2]),
836 milliinches(p
[1] + p
[3]));
848 // troff gratuitously adds an extra 0
849 if (np
!= 1 && np
!= 2) {
850 error("0 or 1 argument required for thickness");
853 line_thickness
= p
[0];
860 error("2 arguments required for rule");
886 error("unrecognised drawing command `%1'", char(code
));
891 font
*dvi_printer::make_font(const char *nm
)
893 return dvi_font::load_dvi_font(nm
);
896 printer
*make_printer()
899 return new draw_dvi_printer
;
901 return new dvi_printer
;
904 static void usage(FILE *stream
);
906 int main(int argc
, char **argv
)
908 setlocale(LC_NUMERIC
, "C"); // FIXME this is the default, noone else sets it!?
909 program_name
= argv
[0];
910 static char stderr_buf
[BUFSIZ
];
911 setbuf(stderr
, stderr_buf
);
913 static const struct option long_options
[] = {
914 { "help", no_argument
, 0, CHAR_MAX
+ 1 },
915 { "version", no_argument
, 0, 'v' },
918 while ((c
= getopt_long(argc
, argv
, "dF:I:lp:vw:", long_options
, NULL
))
928 font::command_line_font_dir(optarg
);
931 // ignore include search path
934 if (!font::scan_papersize(optarg
, 0,
935 &user_paper_length
, &user_paper_width
))
936 error("invalid custom paper size `%1' ignored", optarg
);
940 puts(L_D_DVI
" (" T_ROFF
") v" VERSION
);
945 if (sscanf(optarg
, "%d", &linewidth
) != 1
946 || linewidth
< 0 || linewidth
> 1000) {
947 error("bad line width");
948 linewidth
= DEFAULT_LINEWIDTH
;
951 case CHAR_MAX
+ 1: // --help
962 SET_BINARY(fileno(stdout
));
966 for (int i
= optind
; i
< argc
; i
++)
972 static void usage(FILE *stream
)
974 fprintf(stream
, "Synopsis: %s [-dv] [-F dir] [-w n] [files ...]\n",