2 /* Copyright (C) 1994, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
7 groff is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
12 groff is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 You should have received a copy of the GNU General Public License along
18 with groff; see the file COPYING. If not, write to the Free Software
19 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
24 option to use beziers for circle/ellipse/arc
25 option to use lines for spline (for LJ3)
26 left/top offset registration
27 output bin selection option
29 output non-integer parameters using fixed point numbers
30 X command to insert contents of file
31 X command to specify inline escape sequence (how to specify unprintable chars?)
32 X command to include bitmap graphics
38 extern "C" const char *Version_string;
44 int x_offset_portrait;
45 int x_offset_landscape;
47 { "letter", 2, 75, 60 },
48 { "legal", 3, 75, 60 },
49 { "executive", 1, 75, 60 },
51 { "com10", 81, 75, 60 },
52 { "monarch", 80, 75, 60 },
54 { "b5", 100, 71, 59 },
58 static int user_paper_size = -1;
59 static int landscape_flag = 0;
60 static int duplex_flag = 0;
62 // An upper limit on the paper size in centipoints,
63 // used for setting HPGL picture frame.
64 #define MAX_PAPER_WIDTH (12*720)
65 #define MAX_PAPER_HEIGHT (17*720)
67 // Dotted lines that are thinner than this don't work right.
68 #define MIN_DOT_PEN_WIDTH .351
70 #ifndef DEFAULT_LINE_WIDTH_FACTOR
72 #define DEFAULT_LINE_WIDTH_FACTOR 40
75 const int DEFAULT_HPGL_UNITS = 1016;
76 int line_width_factor = DEFAULT_LINE_WIDTH_FACTOR;
77 unsigned ncopies = 0; // 0 means don't send ncopies command
79 static int lookup_paper_size(const char *);
81 class lj4_font : public font {
84 void handle_unknown_font_command(const char *command, const char *arg,
85 const char *filename, int lineno);
86 static lj4_font *load_lj4_font(const char *);
92 lj4_font(const char *);
95 lj4_font::lj4_font(const char *nm)
96 : font(nm), weight(0), style(0), proportional(0), typeface(0)
100 lj4_font::~lj4_font()
104 lj4_font *lj4_font::load_lj4_font(const char *s)
106 lj4_font *f = new lj4_font(s);
119 } command_table[] = {
120 { "pclweight", &lj4_font::weight, -7, 7 },
121 { "pclstyle", &lj4_font::style, 0, 32767 },
122 { "pclproportional", &lj4_font::proportional, 0, 1 },
123 { "pcltypeface", &lj4_font::typeface, 0, 65535 },
126 void lj4_font::handle_unknown_font_command(const char *command,
128 const char *filename, int lineno)
130 for (unsigned int i = 0;
131 i < sizeof(command_table)/sizeof(command_table[0]); i++) {
132 if (strcmp(command, command_table[i].s) == 0) {
134 fatal_with_file_and_line(filename, lineno,
135 "`%1' command requires an argument",
138 long n = strtol(arg, &ptr, 10);
139 if (n == 0 && ptr == arg)
140 fatal_with_file_and_line(filename, lineno,
141 "`%1' command requires numeric argument",
143 if (n < command_table[i].min) {
144 error_with_file_and_line(filename, lineno,
145 "argument for `%1' command must not be less than %2",
146 command, command_table[i].min);
147 n = command_table[i].min;
149 else if (n > command_table[i].max) {
150 error_with_file_and_line(filename, lineno,
151 "argument for `%1' command must not be greater than %2",
152 command, command_table[i].max);
153 n = command_table[i].max;
155 this->*command_table[i].ptr = int(n);
161 class lj4_printer : public printer {
165 void set_char(int, font *, const environment *, int, const char *name);
166 void draw(int code, int *p, int np, const environment *env);
167 void begin_page(int);
168 void end_page(int page_length);
169 font *make_font(const char *);
172 void set_line_thickness(int size, int dot = 0);
176 int moveto(int hpos, int vpos);
177 int moveto1(int hpos, int vpos);
183 unsigned short cur_symbol_set;
193 int lj4_printer::moveto(int hpos, int vpos)
195 if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0)
196 return moveto1(hpos, vpos);
202 void lj4_printer::hpgl_start()
204 fputs("\033%1B", stdout);
208 void lj4_printer::hpgl_end()
210 fputs(";\033%0A", stdout);
213 lj4_printer::lj4_printer(int ps)
222 if (7200 % font::res != 0)
223 fatal("invalid resolution %1: resolution must be a factor of 7200",
225 fputs("\033E", stdout); // reset
226 if (font::res != 300)
227 printf("\033&u%dD", font::res); // unit of measure
229 printf("\033&l%uX", ncopies);
230 paper_size = 0; // default to letter
231 if (font::papersize) {
232 int n = lookup_paper_size(font::papersize);
234 error("unknown paper size `%1'", font::papersize);
240 printf("\033&l%dA" // paper size
241 "\033&l%dO" // orientation
242 "\033&l0E", // no top margin
243 paper_table[paper_size].code,
244 landscape_flag != 0);
246 x_offset = paper_table[paper_size].x_offset_landscape;
248 x_offset = paper_table[paper_size].x_offset_portrait;
249 x_offset = (x_offset * font::res) / 300;
251 printf("\033&l%dS", duplex_flag);
254 lj4_printer::~lj4_printer()
256 fputs("\033E", stdout);
259 void lj4_printer::begin_page(int)
263 void lj4_printer::end_page(int)
269 void lj4_printer::end_of_line()
271 cur_hpos = -1; // force absolute motion
275 int is_unprintable(unsigned char c)
277 return c < 32 && (c == 0 || (7 <= c && c <= 15) || c == 27);
280 void lj4_printer::set_char(int index, font *f, const environment *env,
283 int code = f->get_code(index);
285 unsigned char ch = code & 0xff;
286 unsigned short symbol_set = code >> 8;
287 if (symbol_set != cur_symbol_set) {
288 printf("\033(%d%c", symbol_set/32, (symbol_set & 31) + 64);
289 cur_symbol_set = symbol_set;
292 lj4_font *psf = (lj4_font *)f;
293 // FIXME only output those that are needed
294 printf("\033(s%dp%ds%db%dT",
299 if (!psf->proportional || !cur_font || !cur_font->proportional)
303 if (env->size != cur_size) {
304 if (cur_font->proportional) {
305 static const char *quarters[] = { "", ".25", ".5", ".75" };
306 printf("\033(s%d%sV", env->size/4, quarters[env->size & 3]);
309 double pitch = double(font::res)/w;
310 // PCL uses the next largest pitch, so round it down.
311 pitch = floor(pitch*100.0)/100.0;
312 printf("\033(s%.2fH", pitch);
314 cur_size = env->size;
316 if (!moveto(env->hpos, env->vpos))
318 if (is_unprintable(ch))
319 fputs("\033&p1X", stdout);
324 int lj4_printer::moveto1(int hpos, int vpos)
326 if (hpos < x_offset || vpos < 0)
328 fputs("\033*p", stdout);
330 printf("%dx%dY", hpos - x_offset, vpos);
332 if (cur_hpos != hpos)
333 printf("%s%d%c", hpos > cur_hpos ? "+" : "",
334 hpos - cur_hpos, vpos == cur_vpos ? 'X' : 'x');
335 if (cur_vpos != vpos)
336 printf("%s%dY", vpos > cur_vpos ? "+" : "", vpos - cur_vpos);
343 void lj4_printer::draw(int code, int *p, int np, const environment *env)
349 error("2 arguments required for rule");
352 int hpos = env->hpos;
353 int vpos = env->vpos;
364 if (!moveto(hpos, vpos))
366 printf("\033*c%da%db0P", hsize, vsize);
371 error("2 arguments required for line");
375 if (!moveto(env->hpos, env->vpos))
378 set_line_thickness(env->size, p[0] == 0 && p[1] == 0);
379 printf("PD%d,%d", p[0], p[1]);
386 error("even number of arguments required for polygon");
390 error("no arguments for polygon");
394 if (!moveto(env->hpos, env->vpos))
398 set_line_thickness(env->size);
399 printf("PMPD%d", p[0]);
400 for (int i = 1; i < np; i++)
402 printf("PM2%cP", code == 'p' ? 'E' : 'F');
409 error("even number of arguments required for spline");
413 error("no arguments for spline");
417 if (!moveto(env->hpos, env->vpos))
420 set_line_thickness(env->size);
421 printf("PD%d,%d", p[0]/2, p[1]/2);
426 for (int i = 0; i < np - 2; i += 2) {
429 printf("%d,%d,%d,%d,%d,%d",
430 (p[i]*tnum)/(2*tden),
431 (p[i + 1]*tnum)/(2*tden),
432 p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden),
433 p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden),
434 (p[i] - p[i]/2) + p[i + 2]/2,
435 (p[i + 1] - p[i + 1]/2) + p[i + 3]/2);
438 printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2);
444 // troff adds an extra argument to C
445 if (np != 1 && !(code == 'C' && np == 2)) {
446 error("1 argument required for circle");
450 if (!moveto(env->hpos + p[0]/2, env->vpos))
454 set_line_thickness(env->size);
455 printf("CI%d", p[0]/2);
458 printf("WG%d,0,360", p[0]/2);
464 error("2 arguments required for ellipse");
468 if (!moveto(env->hpos + p[0]/2, env->vpos))
471 printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale);
473 set_line_thickness(env->size);
474 printf("CI%d", p[1]/2);
477 printf("WG%d,0,360", p[1]/2);
478 printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale);
484 error("4 arguments required for arc");
488 if (!moveto(env->hpos, env->vpos))
491 set_line_thickness(env->size);
493 if (adjust_arc_center(p, c)) {
494 double sweep = ((atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])
495 - atan2(-c[1], -c[0]))
499 printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep);
502 printf("PD%d,%d", p[0] + p[2], p[1] + p[3]);
507 if (np != 1 && np != 2) {
508 error("1 argument required for fill");
513 if (p[0] >= 0 && p[0] <= 1000)
514 printf("FT10,%d", p[0]/10);
518 // not implemented yet
526 // troff gratuitously adds an extra 0
527 if (np != 1 && np != 2) {
528 error("0 or 1 argument required for thickness");
531 line_thickness = p[0];
536 error("unrecognised drawing command `%1'", char(code));
541 void lj4_printer::hpgl_init()
546 hpgl_scale = double(DEFAULT_HPGL_UNITS)/font::res;
547 printf("\033&f0S" // push position
548 "\033*p0x0Y" // move to 0,0
549 "\033*c%dx%dy0T" // establish picture frame
550 "\033%%1B" // switch to HPGL
551 "SP1SC0,%.4f,0,-%.4f,2IR0,100,0,100" // set up scaling
552 "LA1,4,2,4" // round line ends and joins
553 "PR" // relative plotting
555 ";\033%%1A" // back to PCL
556 "\033&f1S", // pop position
557 MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT,
558 hpgl_scale, hpgl_scale);
561 void lj4_printer::set_line_thickness(int size, int dot)
564 if (line_thickness < 0)
565 pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0);
567 pw = line_thickness*25.4/font::res;
568 if (dot && pw < MIN_DOT_PEN_WIDTH)
569 pw = MIN_DOT_PEN_WIDTH;
570 if (pw != pen_width) {
576 font *lj4_printer::make_font(const char *nm)
578 return lj4_font::load_lj4_font(nm);
581 printer *make_printer()
583 return new lj4_printer(user_paper_size);
587 int lookup_paper_size(const char *s)
589 for (unsigned int i = 0;
590 i < sizeof(paper_table)/sizeof(paper_table[0]); i++) {
591 // FIXME Perhaps allow unique prefix.
592 if (strcasecmp(s, paper_table[i].name) == 0)
598 static void usage(FILE *stream);
600 extern "C" int optopt, optind;
602 int main(int argc, char **argv)
604 setlocale(LC_NUMERIC, "C");
605 program_name = argv[0];
606 static char stderr_buf[BUFSIZ];
607 setbuf(stderr, stderr_buf);
609 static const struct option long_options[] = {
610 { "help", no_argument, 0, CHAR_MAX + 1 },
611 { "version", no_argument, 0, 'v' },
614 while ((c = getopt_long(argc, argv, ":F:p:d:lvw:c:", long_options, NULL))
622 fprintf(stderr, "duplex assumed to be long-side\n");
625 fprintf(stderr, "option -%c requires an operand\n", optopt);
629 if (!isdigit(*optarg)) // this ugly hack prevents -d without
630 optind--; // args from messing up the arg list
631 duplex_flag = atoi(optarg);
632 if (duplex_flag != 1 && duplex_flag != 2) {
633 fprintf(stderr, "odd value for duplex; assumed to be long-side\n");
639 int n = lookup_paper_size(optarg);
641 error("unknown paper size `%1'", optarg);
648 printf("GNU grolj4 (groff) version %s\n", Version_string);
653 font::command_line_font_dir(optarg);
658 long n = strtol(optarg, &ptr, 10);
659 if (n == 0 && ptr == optarg)
660 error("argument for -c must be a positive integer");
661 else if (n <= 0 || n > 32767)
662 error("out of range argument for -c");
664 ncopies = unsigned(n);
670 long n = strtol(optarg, &ptr, 10);
671 if (n == 0 && ptr == optarg)
672 error("argument for -w must be a non-negative integer");
673 else if (n < 0 || n > INT_MAX)
674 error("out of range argument for -w");
676 line_width_factor = int(n);
679 case CHAR_MAX + 1: // --help
690 SET_BINARY(fileno(stdout));
694 for (int i = optind; i < argc; i++)
700 static void usage(FILE *stream)
703 "usage: %s [-lv] [-d [n]] [-c n] [-p paper_size]\n"
704 " [-w n] [-F dir] [files ...]\n",