Remove no longer needed catman periodic via 'make upgrade'.
[dragonfly.git] / contrib / groff / src / devices / grolj4 / lj4.cpp
1 // -*- C++ -*-
2 /* Copyright (C) 1994, 2000, 2001, 2002, 2003, 2004, 2006, 2009
3    Free Software Foundation, Inc.
4      Written by James Clark (jjc@jclark.com)
5
6 This file is part of groff.
7
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation, either version 3 of the License, or
11 (at your option) any later version.
12
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20
21 /*
22 TODO
23
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
28 paper source 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
33 */
34
35 #include "driver.h"
36 #include "nonposix.h"
37
38 extern "C" const char *Version_string;
39
40 static struct {
41   const char *name;
42   int code;
43   // at 300dpi
44   int x_offset_portrait;
45   int x_offset_landscape;
46 } paper_table[] = {
47   { "letter", 2, 75, 60 },
48   { "legal", 3, 75, 60 },
49   { "executive", 1, 75, 60 },
50   { "a4", 26, 71, 59 },
51   { "com10", 81, 75, 60 },
52   { "monarch", 80, 75, 60 },
53   { "c5", 91, 71, 59 },
54   { "b5", 100, 71, 59 },
55   { "dl", 90, 71, 59 },
56 };
57
58 static int user_paper_size = -1;
59 static int landscape_flag = 0;
60 static int duplex_flag = 0;
61
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)
66
67 // Dotted lines that are thinner than this don't work right.
68 #define MIN_DOT_PEN_WIDTH .351
69
70 #ifndef DEFAULT_LINE_WIDTH_FACTOR
71 // in ems/1000
72 #define DEFAULT_LINE_WIDTH_FACTOR 40
73 #endif
74
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
78
79 static int lookup_paper_size(const char *);
80
81 class lj4_font : public font {
82 public:
83   ~lj4_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 *);
87   int weight;
88   int style;
89   int proportional;
90   int typeface;
91 private:
92   lj4_font(const char *);
93 };
94
95 lj4_font::lj4_font(const char *nm)
96 : font(nm), weight(0), style(0), proportional(0), typeface(0)
97 {
98 }
99
100 lj4_font::~lj4_font()
101 {
102 }
103
104 lj4_font *lj4_font::load_lj4_font(const char *s)
105 {
106   lj4_font *f = new lj4_font(s);
107   if (!f->load()) {
108     delete f;
109     return 0;
110   }
111   return f;
112 }
113
114 static struct {
115   const char *s;
116   int lj4_font::*ptr;
117   int min;
118   int max;
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 },
124 };
125
126 void lj4_font::handle_unknown_font_command(const char *command,
127                                            const char *arg,
128                                            const char *filename, int lineno)
129 {
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) {
133       if (arg == 0)
134         fatal_with_file_and_line(filename, lineno,
135                                  "`%1' command requires an argument",
136                                  command);
137       char *ptr;
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",
142                                  command);
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;
148       }
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;
154       }
155       this->*command_table[i].ptr = int(n);
156       break;
157     }
158   }
159 }
160
161 class lj4_printer : public printer {
162 public:
163   lj4_printer(int);
164   ~lj4_printer();
165   void set_char(glyph *, 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 *);
170   void end_of_line();
171 private:
172   void set_line_thickness(int size, int dot = 0);
173   void hpgl_init();
174   void hpgl_start();
175   void hpgl_end();
176   int moveto(int hpos, int vpos);
177   int moveto1(int hpos, int vpos);
178
179   int cur_hpos;
180   int cur_vpos;
181   lj4_font *cur_font;
182   int cur_size;
183   unsigned short cur_symbol_set;
184   int x_offset;
185   int line_thickness;
186   double pen_width;
187   double hpgl_scale;
188   int hpgl_inited;
189   int paper_size;
190 };
191
192 inline
193 int lj4_printer::moveto(int hpos, int vpos)
194 {
195   if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0)
196     return moveto1(hpos, vpos);
197   else
198     return 1;
199 }
200
201 inline
202 void lj4_printer::hpgl_start()
203 {
204   fputs("\033%1B", stdout);
205 }
206
207 inline
208 void lj4_printer::hpgl_end()
209 {
210   fputs(";\033%0A", stdout);
211 }
212
213 lj4_printer::lj4_printer(int ps)
214 : cur_hpos(-1),
215   cur_font(0),
216   cur_size(0),
217   cur_symbol_set(0),
218   line_thickness(-1),
219   pen_width(-1.0),
220   hpgl_inited(0)
221 {
222   if (7200 % font::res != 0)
223     fatal("invalid resolution %1: resolution must be a factor of 7200",
224           font::res);
225   fputs("\033E", stdout);               // reset
226   if (font::res != 300)
227     printf("\033&u%dD", font::res);     // unit of measure
228   if (ncopies > 0)
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);
233     if (n < 0)
234       error("unknown paper size `%1'", font::papersize);
235     else
236       paper_size = n;
237   }
238   if (ps >= 0)
239     paper_size = ps;
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);
245   if (landscape_flag)
246     x_offset = paper_table[paper_size].x_offset_landscape;
247   else
248     x_offset = paper_table[paper_size].x_offset_portrait;
249   x_offset = (x_offset * font::res) / 300;
250   if (duplex_flag)
251      printf("\033&l%dS", duplex_flag);
252 }
253
254 lj4_printer::~lj4_printer()
255 {
256   fputs("\033E", stdout);
257 }
258
259 void lj4_printer::begin_page(int)
260 {
261 }
262
263 void lj4_printer::end_page(int)
264 {
265   putchar('\f');
266   cur_hpos = -1;
267 }
268
269 void lj4_printer::end_of_line()
270 {
271   cur_hpos = -1;                // force absolute motion
272 }
273
274 inline
275 int is_unprintable(unsigned char c)
276 {
277   return c < 32 && (c == 0 || (7 <= c && c <= 15) || c == 27);
278 }
279
280 void lj4_printer::set_char(glyph *g, font *f, const environment *env,
281                            int w, const char *)
282 {
283   int code = f->get_code(g);
284
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;
290   }
291   if (f != cur_font) {
292     lj4_font *psf = (lj4_font *)f;
293     // FIXME only output those that are needed
294     printf("\033(s%dp%ds%db%dT",
295            psf->proportional,
296            psf->style,
297            psf->weight,
298            psf->typeface);
299     if (!psf->proportional || !cur_font || !cur_font->proportional)
300       cur_size = 0;
301     cur_font = psf;
302   }
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]);
307     }
308     else {
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);
313     }
314     cur_size = env->size;
315   }
316   if (!moveto(env->hpos, env->vpos))
317     return;
318   if (is_unprintable(ch))
319     fputs("\033&p1X", stdout);
320   putchar(ch);
321   cur_hpos += w;
322 }
323
324 int lj4_printer::moveto1(int hpos, int vpos)
325 {
326   if (hpos < x_offset || vpos < 0)
327     return 0;
328   fputs("\033*p", stdout);
329   if (cur_hpos < 0)
330     printf("%dx%dY", hpos - x_offset, vpos);
331   else {
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);
337   }
338   cur_hpos = hpos;
339   cur_vpos = vpos;
340   return 1;
341 }
342
343 void lj4_printer::draw(int code, int *p, int np, const environment *env)
344 {
345   switch (code) {
346   case 'R':
347     {
348       if (np != 2) {
349         error("2 arguments required for rule");
350         break;
351       }
352       int hpos = env->hpos;
353       int vpos = env->vpos;
354       int hsize = p[0];
355       int vsize = p[1];
356       if (hsize < 0) {
357         hpos += hsize;
358         hsize = -hsize;
359       }
360       if (vsize < 0) {
361         vpos += vsize;
362         vsize = -vsize;
363       }
364       if (!moveto(hpos, vpos))
365         return;
366       printf("\033*c%da%db0P", hsize, vsize);
367       break;
368     }
369   case 'l':
370     if (np != 2) {
371       error("2 arguments required for line");
372       break;
373     }
374     hpgl_init();
375     if (!moveto(env->hpos, env->vpos))
376       return;
377     hpgl_start();
378     set_line_thickness(env->size, p[0] == 0 && p[1] == 0);
379     printf("PD%d,%d", p[0], p[1]);
380     hpgl_end();
381     break;
382   case 'p':
383   case 'P':
384     {
385       if (np & 1) {
386         error("even number of arguments required for polygon");
387         break;
388       }
389       if (np == 0) {
390         error("no arguments for polygon");
391         break;
392       }
393       hpgl_init();
394       if (!moveto(env->hpos, env->vpos))
395         return;
396       hpgl_start();
397       if (code == 'p')
398         set_line_thickness(env->size);
399       printf("PMPD%d", p[0]);
400       for (int i = 1; i < np; i++)
401         printf(",%d", p[i]);
402       printf("PM2%cP", code == 'p' ? 'E' : 'F');
403       hpgl_end();
404       break;
405     }
406   case '~':
407     {
408       if (np & 1) {
409         error("even number of arguments required for spline");
410         break;
411       }
412       if (np == 0) {
413         error("no arguments for spline");
414         break;
415       }
416       hpgl_init();
417       if (!moveto(env->hpos, env->vpos))
418         return;
419       hpgl_start();
420       set_line_thickness(env->size);
421       printf("PD%d,%d", p[0]/2, p[1]/2);
422       const int tnum = 2;
423       const int tden = 3;
424       if (np > 2) {
425         fputs("BR", stdout);
426         for (int i = 0; i < np - 2; i += 2) {
427           if (i != 0)
428             putchar(',');
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);
436         }
437       }
438       printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2);
439       hpgl_end();
440       break;
441     }
442   case 'c':
443   case 'C':
444     // troff adds an extra argument to C
445     if (np != 1 && !(code == 'C' && np == 2)) {
446       error("1 argument required for circle");
447       break;
448     }
449     hpgl_init();
450     if (!moveto(env->hpos + p[0]/2, env->vpos))
451       return;
452     hpgl_start();
453     if (code == 'c') {
454       set_line_thickness(env->size);
455       printf("CI%d", p[0]/2);
456     }
457     else
458       printf("WG%d,0,360", p[0]/2);
459     hpgl_end();
460     break;
461   case 'e':
462   case 'E':
463     if (np != 2) {
464       error("2 arguments required for ellipse");
465       break;
466     }
467     hpgl_init();
468     if (!moveto(env->hpos + p[0]/2, env->vpos))
469       return;
470     hpgl_start();
471     printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale);
472     if (code == 'e') {
473       set_line_thickness(env->size);
474       printf("CI%d", p[1]/2);
475     }
476     else
477       printf("WG%d,0,360", p[1]/2);
478     printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale);
479     hpgl_end();
480     break;
481   case 'a':
482     {
483       if (np != 4) {
484         error("4 arguments required for arc");
485         break;
486       }
487       hpgl_init();
488       if (!moveto(env->hpos, env->vpos))
489         return;
490       hpgl_start();
491       set_line_thickness(env->size);
492       double c[2];
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]))
496                         * 180.0/PI);
497         if (sweep > 0.0)
498           sweep -= 360.0;
499         printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep);
500       }
501       else
502         printf("PD%d,%d", p[0] + p[2], p[1] + p[3]);
503       hpgl_end();
504     }
505     break;
506   case 'f':
507     if (np != 1 && np != 2) {
508       error("1 argument required for fill");
509       break;
510     }
511     hpgl_init();
512     hpgl_start();
513     if (p[0] >= 0 && p[0] <= 1000)
514       printf("FT10,%d", p[0]/10);
515     hpgl_end();
516     break;
517   case 'F':
518     // not implemented yet
519     break;
520   case 't':
521     {
522       if (np == 0) {
523         line_thickness = -1;
524       }
525       else {
526         // troff gratuitously adds an extra 0
527         if (np != 1 && np != 2) {
528           error("0 or 1 argument required for thickness");
529           break;
530         }
531         line_thickness = p[0];
532       }
533       break;
534     }
535   default:
536     error("unrecognised drawing command `%1'", char(code));
537     break;
538   }
539 }
540
541 void lj4_printer::hpgl_init()
542 {
543   if (hpgl_inited)
544     return;
545   hpgl_inited = 1;
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
554          "TR0"                  // opaque
555          ";\033%%1A"            // back to PCL
556          "\033&f1S",            // pop position
557          MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT,
558          hpgl_scale, hpgl_scale);
559 }
560
561 void lj4_printer::set_line_thickness(int size, int dot)
562 {
563   double pw;
564   if (line_thickness < 0)
565     pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0);
566   else
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) {
571     printf("PW%f", pw);
572     pen_width = pw;
573   }
574 }
575
576 font *lj4_printer::make_font(const char *nm)
577 {
578   return lj4_font::load_lj4_font(nm);
579 }
580
581 printer *make_printer()
582 {
583   return new lj4_printer(user_paper_size);
584 }
585
586 static
587 int lookup_paper_size(const char *s)
588 {
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)
593       return i;
594   }
595   return -1;
596 }
597
598 static void usage(FILE *stream);
599
600 extern "C" int optopt, optind;
601
602 int main(int argc, char **argv)
603 {
604   setlocale(LC_NUMERIC, "C");
605   program_name = argv[0];
606   static char stderr_buf[BUFSIZ];
607   setbuf(stderr, stderr_buf);
608   int c;
609   static const struct option long_options[] = {
610     { "help", no_argument, 0, CHAR_MAX + 1 },
611     { "version", no_argument, 0, 'v' },
612     { NULL, 0, 0, 0 }
613   };
614   while ((c = getopt_long(argc, argv, "c:d:F:I:lp:vw:", long_options, NULL))
615          != EOF)
616     switch(c) {
617     case 'l':
618       landscape_flag = 1;
619       break;
620     case 'I':
621       // ignore include search path
622       break;
623     case ':':
624       if (optopt == 'd') {
625         fprintf(stderr, "duplex assumed to be long-side\n");
626         duplex_flag = 1;
627       } else
628         fprintf(stderr, "option -%c requires an argument\n", optopt);
629       fflush(stderr);
630       break;
631     case 'd':
632       if (!isdigit(*optarg))    // this ugly hack prevents -d without
633         optind--;               //  args from messing up the arg list
634       duplex_flag = atoi(optarg);
635       if (duplex_flag != 1 && duplex_flag != 2) {
636         fprintf(stderr, "odd value for duplex; assumed to be long-side\n");
637         duplex_flag = 1;
638       }
639       break;
640     case 'p':
641       {
642         int n = lookup_paper_size(optarg);
643         if (n < 0)
644           error("unknown paper size `%1'", optarg);
645         else
646           user_paper_size = n;
647         break;
648       }
649     case 'v':
650       printf("GNU grolj4 (groff) version %s\n", Version_string);
651       exit(0);
652       break;
653     case 'F':
654       font::command_line_font_dir(optarg);
655       break;
656     case 'c':
657       {
658         char *ptr;
659         long n = strtol(optarg, &ptr, 10);
660         if (n == 0 && ptr == optarg)
661           error("argument for -c must be a positive integer");
662         else if (n <= 0 || n > 32767)
663           error("out of range argument for -c");
664         else
665           ncopies = unsigned(n);
666         break;
667       }
668     case 'w':
669       {
670         char *ptr;
671         long n = strtol(optarg, &ptr, 10);
672         if (n == 0 && ptr == optarg)
673           error("argument for -w must be a non-negative integer");
674         else if (n < 0 || n > INT_MAX)
675           error("out of range argument for -w");
676         else
677           line_width_factor = int(n);
678         break;
679       }
680     case CHAR_MAX + 1: // --help
681       usage(stdout);
682       exit(0);
683       break;
684     case '?':
685       usage(stderr);
686       exit(1);
687       break;
688     default:
689       assert(0);
690     }
691   SET_BINARY(fileno(stdout));
692   if (optind >= argc)
693     do_file("-");
694   else {
695     for (int i = optind; i < argc; i++)
696       do_file(argv[i]);
697   }
698   return 0;
699 }
700
701 static void usage(FILE *stream)
702 {
703   fprintf(stream,
704           "usage: %s [-lv] [-d [n]] [-c n] [-p paper_size]\n"
705           "       [-w n] [-F dir] [files ...]\n",
706           program_name);
707 }