]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/groff/grolj4/lj4.cc
Virgin import of FSF groff v1.10
[FreeBSD/FreeBSD.git] / contrib / groff / grolj4 / lj4.cc
1 // -*- C++ -*-
2 /* Copyright (C) 1994 Free Software Foundation, Inc.
3      Written by James Clark (jjc@jclark.com)
4
5 This file is part of groff.
6
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
10 version.
11
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
15 for more details.
16
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. */
20
21 /*
22 TODO
23
24 option to use beziers for circle/ellipse/arc
25 option to use lines for spline (for LJ3)
26 duplex option
27 duplex short/long edge options
28 left/top offset registration
29 output bin selection option
30 paper source option
31 output non-integer parameters using fixed point numbers
32 X command to insert contents of file
33 X command to specify inline escape sequence (how to specify unprintable chars?)
34 X command to include bitmap graphics
35 */
36
37 #include "driver.h"
38
39 static struct {
40   const char *name;
41   int code;
42   // at 300dpi
43   int x_offset_portrait;
44   int x_offset_landscape;
45 } paper_table[] = {
46   { "letter", 2, 75, 60 },
47   { "legal", 3, 75, 60 },
48   { "executive", 1, 75, 60 },
49   { "a4", 26, 71, 59 },
50   { "com10", 81, 75, 60 },
51   { "monarch", 80, 75, 60 },
52   { "c5", 91, 71, 59 },
53   { "b5", 100, 71, 59 },
54   { "dl", 90, 71, 59 },
55 };
56
57 static int paper_size = -1;
58 static int landscape_flag = 0;
59
60 // An upper limit on the paper size in centipoints,
61 // used for setting HPGL picture frame.
62 #define MAX_PAPER_WIDTH (12*720)
63 #define MAX_PAPER_HEIGHT (17*720)
64
65 // Dotted lines that are thinner than this don't work right.
66 #define MIN_DOT_PEN_WIDTH .351
67
68 #ifndef DEFAULT_LINE_WIDTH_FACTOR
69 // in ems/1000
70 #define DEFAULT_LINE_WIDTH_FACTOR 40
71 #endif
72
73 const int DEFAULT_HPGL_UNITS = 1016;
74 int line_width_factor = DEFAULT_LINE_WIDTH_FACTOR;
75 unsigned ncopies = 0;           // 0 means don't send ncopies command
76
77 class lj4_font : public font {
78 public:
79   ~lj4_font();
80   void handle_unknown_font_command(const char *command, const char *arg,
81                                    const char *filename, int lineno);
82   static lj4_font *load_lj4_font(const char *);
83   int weight;
84   int style;
85   int proportional;
86   int typeface;
87 private:
88   lj4_font(const char *);
89 };
90
91 lj4_font::lj4_font(const char *nm)
92 : font(nm), weight(0), style(0), proportional(0), typeface(0)
93 {
94 }
95
96 lj4_font::~lj4_font()
97 {
98 }
99
100 lj4_font *lj4_font::load_lj4_font(const char *s)
101 {
102   lj4_font *f = new lj4_font(s);
103   if (!f->load()) {
104     delete f;
105     return 0;
106   }
107   return f;
108 }
109
110 static struct {
111   const char *s;
112   int lj4_font::*ptr;
113   int min;
114   int max;
115 } command_table[] = {
116   { "pclweight", &lj4_font::weight, -7, 7 },
117   { "pclstyle", &lj4_font::style, 0, 32767 },
118   { "pclproportional", &lj4_font::proportional, 0, 1 },
119   { "pcltypeface", &lj4_font::typeface, 0, 65535 },
120 };
121
122 void lj4_font::handle_unknown_font_command(const char *command,
123                                            const char *arg,
124                                            const char *filename, int lineno)
125 {
126   for (int i = 0; i < sizeof(command_table)/sizeof(command_table[0]); i++) {
127     if (strcmp(command, command_table[i].s) == 0) {
128       if (arg == 0)
129         fatal_with_file_and_line(filename, lineno,
130                                  "`%1' command requires an argument",
131                                  command);
132       char *ptr;
133       long n = strtol(arg, &ptr, 10);
134       if (n == 0 && ptr == arg)
135         fatal_with_file_and_line(filename, lineno,
136                                  "`%1' command requires numeric argument",
137                                  command);
138       if (n < command_table[i].min) {
139         error_with_file_and_line(filename, lineno,
140                                  "argument for `%1' command must not be less than %2",
141                                  command, command_table[i].min);
142         n = command_table[i].min;
143       }
144       else if (n > command_table[i].max) {
145         error_with_file_and_line(filename, lineno,
146                                  "argument for `%1' command must not be greater than %2",
147                                  command, command_table[i].max);
148         n = command_table[i].max;
149       }
150       this->*command_table[i].ptr = int(n);
151       break;
152     }
153   }
154 }
155
156 class lj4_printer : public printer {
157 public:
158   lj4_printer();
159   ~lj4_printer();
160   void set_char(int, font *, const environment *, int);
161   void draw(int code, int *p, int np, const environment *env);
162   void begin_page(int);
163   void end_page(int page_length);
164   font *make_font(const char *);
165   void end_of_line();
166 private:
167   void set_line_thickness(int size, int dot = 0);
168   void hpgl_init();
169   void hpgl_start();
170   void hpgl_end();
171   int moveto(int hpos, int vpos);
172   int moveto1(int hpos, int vpos);
173
174   int cur_hpos;
175   int cur_vpos;
176   lj4_font *cur_font;
177   int cur_size;
178   unsigned short cur_symbol_set;
179   int x_offset;
180   int line_thickness;
181   double pen_width;
182   double hpgl_scale;
183   int hpgl_inited;
184 };
185
186 inline
187 int lj4_printer::moveto(int hpos, int vpos)
188 {
189   if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0)
190     return moveto1(hpos, vpos);
191   else
192     return 1;
193 }
194
195 inline
196 void lj4_printer::hpgl_start()
197 {
198   fputs("\033%1B", stdout);
199 }
200
201 inline
202 void lj4_printer::hpgl_end()
203 {
204   fputs(";\033%0A", stdout);
205 }
206
207 lj4_printer::lj4_printer()
208 : cur_hpos(-1),
209   cur_font(0),
210   cur_size(0),
211   cur_symbol_set(0),
212   line_thickness(-1),
213   pen_width(-1.0),
214   hpgl_inited(0)
215 {
216   if (7200 % font::res != 0)
217     fatal("invalid resolution %1: resolution must be a factor of 7200",
218           font::res);
219   fputs("\033E", stdout);               // reset
220   if (font::res != 300)
221     printf("\033&u%dD", font::res); // unit of measure
222   if (ncopies > 0)
223     printf("\033&l%uX", ncopies);
224   if (paper_size < 0)
225     paper_size = 0;             // default to letter
226   printf("\033&l%dA"            // paper size
227          "\033&l%dO"            // orientation
228          "\033&l0E",            // no top margin
229          paper_table[paper_size].code,
230          landscape_flag != 0);
231   if (landscape_flag)
232     x_offset = paper_table[paper_size].x_offset_landscape;
233   else
234     x_offset = paper_table[paper_size].x_offset_portrait;
235   x_offset = (x_offset * font::res) / 300;
236 }
237
238 lj4_printer::~lj4_printer()
239 {
240   fputs("\033E", stdout);
241 }
242
243 void lj4_printer::begin_page(int)
244 {
245 }
246
247 void lj4_printer::end_page(int)
248 {
249   putchar('\f');
250   cur_hpos = -1;
251 }
252
253 void lj4_printer::end_of_line()
254 {
255   cur_hpos = -1;                // force absolute motion
256 }
257
258 inline
259 int is_unprintable(unsigned char c)
260 {
261   return c < 32 && (c == 0 || (7 <= c && c <= 15) || c == 27);
262 }
263
264 void lj4_printer::set_char(int index, font *f, const environment *env, int w)
265 {
266   int code = f->get_code(index);
267
268   unsigned char ch = code & 0xff;
269   unsigned short symbol_set = code >> 8;
270   if (symbol_set != cur_symbol_set) {
271     printf("\033(%d%c", symbol_set/32, (symbol_set & 31) + 64);
272     cur_symbol_set = symbol_set;
273   }
274   if (f != cur_font) {
275     lj4_font *psf = (lj4_font *)f;
276     // FIXME only output those that are needed
277     printf("\033(s%dp%ds%db%dT",
278            psf->proportional,
279            psf->style,
280            psf->weight,
281            psf->typeface);
282     if (!psf->proportional || !cur_font || !cur_font->proportional)
283       cur_size = 0;
284     cur_font = psf;
285   }
286   if (env->size != cur_size) {
287     if (cur_font->proportional) {
288       static const char *quarters[] = { "", ".25", ".5", ".75" };
289       printf("\033(s%d%sV", env->size/4, quarters[env->size & 3]);
290     }
291     else {
292       double pitch = double(font::res)/w;
293       // PCL uses the next largest pitch, so round it down.
294       pitch = floor(pitch*100.0)/100.0;
295       printf("\033(s%.2fH", pitch);
296     }
297     cur_size = env->size;
298   }
299   if (!moveto(env->hpos, env->vpos))
300     return;
301   if (is_unprintable(ch))
302     fputs("\033&p1X", stdout);
303   putchar(ch);
304   cur_hpos += w;
305 }
306
307 int lj4_printer::moveto1(int hpos, int vpos)
308 {
309   if (hpos < x_offset || vpos < 0)
310     return 0;
311   fputs("\033*p", stdout);
312   if (cur_hpos < 0)
313     printf("%dx%dY", hpos - x_offset, vpos);
314   else {
315     if (cur_hpos != hpos)
316       printf("%s%d%c", hpos > cur_hpos ? "+" : "",
317              hpos - cur_hpos, vpos == cur_vpos ? 'X' : 'x');
318     if (cur_vpos != vpos)
319       printf("%s%dY", vpos > cur_vpos ? "+" : "", vpos - cur_vpos);
320   }
321   cur_hpos = hpos;
322   cur_vpos = vpos;
323   return 1;
324 }
325
326 void lj4_printer::draw(int code, int *p, int np, const environment *env)
327 {
328   switch (code) {
329   case 'R':
330     {
331       if (np != 2) {
332         error("2 arguments required for rule");
333         break;
334       }
335       int hpos = env->hpos;
336       int vpos = env->vpos;
337       int hsize = p[0];
338       int vsize = p[1];
339       if (hsize < 0) {
340         hpos += hsize;
341         hsize = -hsize;
342       }
343       if (vsize < 0) {
344         vpos += vsize;
345         vsize = -vsize;
346       }
347       if (!moveto(hpos, vpos))
348         return;
349       printf("\033*c%da%db0P", hsize, vsize);
350       break;
351     }
352   case 'l':
353     if (np != 2) {
354       error("2 arguments required for line");
355       break;
356     }
357     hpgl_init();
358     if (!moveto(env->hpos, env->vpos))
359       return;
360     hpgl_start();
361     set_line_thickness(env->size, p[0] == 0 && p[1] == 0);
362     printf("PD%d,%d", p[0], p[1]);
363     hpgl_end();
364     break;
365   case 'p':
366   case 'P':
367     {
368       if (np & 1) {
369         error("even number of arguments required for polygon");
370         break;
371       }
372       if (np == 0) {
373         error("no arguments for polygon");
374         break;
375       }
376       hpgl_init();
377       if (!moveto(env->hpos, env->vpos))
378         return;
379       hpgl_start();
380       if (code == 'p')
381         set_line_thickness(env->size);
382       printf("PMPD%d", p[0]);
383       for (int i = 1; i < np; i++)
384         printf(",%d", p[i]);
385       printf("PM2%cP", code == 'p' ? 'E' : 'F');
386       hpgl_end();
387       break;
388     }
389   case '~':
390     {
391       if (np & 1) {
392         error("even number of arguments required for spline");
393         break;
394       }
395       if (np == 0) {
396         error("no arguments for spline");
397         break;
398       }
399       hpgl_init();
400       if (!moveto(env->hpos, env->vpos))
401         return;
402       hpgl_start();
403       set_line_thickness(env->size);
404       printf("PD%d,%d", p[0]/2, p[1]/2);
405       const int tnum = 2;
406       const int tden = 3;
407       if (np > 2) {
408         fputs("BR", stdout);
409         for (int i = 0; i < np - 2; i += 2) {
410           if (i != 0)
411             putchar(',');
412           printf("%d,%d,%d,%d,%d,%d",
413                  (p[i]*tnum)/(2*tden),
414                  (p[i + 1]*tnum)/(2*tden),
415                  p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden),
416                  p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden),
417                  (p[i] - p[i]/2) + p[i + 2]/2,
418                  (p[i + 1] - p[i + 1]/2) + p[i + 3]/2);
419         }
420       }
421       printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2);
422       hpgl_end();
423       break;
424     }
425   case 'c':
426   case 'C':
427     // troff adds an extra argument to C
428     if (np != 1 && !(code == 'C' && np == 2)) {
429       error("1 argument required for circle");
430       break;
431     }
432     hpgl_init();
433     if (!moveto(env->hpos + p[0]/2, env->vpos))
434       return;
435     hpgl_start();
436     if (code == 'c') {
437       set_line_thickness(env->size);
438       printf("CI%d", p[0]/2);
439     }
440     else
441       printf("WG%d,0,360", p[0]/2);
442     hpgl_end();
443     break;
444   case 'e':
445   case 'E':
446     if (np != 2) {
447       error("2 arguments required for ellipse");
448       break;
449     }
450     hpgl_init();
451     if (!moveto(env->hpos + p[0]/2, env->vpos))
452       return;
453     hpgl_start();
454     printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale);
455     if (code == 'e') {
456       set_line_thickness(env->size);
457       printf("CI%d", p[1]/2);
458     }
459     else
460       printf("WG%d,0,360", p[1]/2);
461     printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale);
462     hpgl_end();
463     break;
464   case 'a':
465     {
466       if (np != 4) {
467         error("4 arguments required for arc");
468         break;
469       }
470       hpgl_init();
471       if (!moveto(env->hpos, env->vpos))
472         return;
473       hpgl_start();
474       set_line_thickness(env->size);
475       double c[2];
476       if (adjust_arc_center(p, c)) {
477         double sweep = ((atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])
478                          - atan2(-c[1], -c[0]))
479                         * 180.0/PI);
480         if (sweep > 0.0)
481           sweep -= 360.0;
482         printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep);
483       }
484       else
485         printf("PD%d,%d", p[0] + p[2], p[1] + p[3]);
486       hpgl_end();
487     }
488     break;
489   case 'f':
490     if (np != 1 && np != 2) {
491       error("1 argument required for fill");
492       break;
493     }
494     hpgl_init();
495     hpgl_start();
496     if (p[0] >= 0 && p[0] <= 1000)
497       printf("FT10,%d", p[0]/10);
498     hpgl_end();
499     break;
500   case 't':
501     {
502       if (np == 0) {
503         line_thickness = -1;
504       }
505       else {
506         // troff gratuitously adds an extra 0
507         if (np != 1 && np != 2) {
508           error("0 or 1 argument required for thickness");
509           break;
510         }
511         line_thickness = p[0];
512       }
513       break;
514     }
515   default:
516     error("unrecognised drawing command `%1'", char(code));
517     break;
518   }
519 }
520
521 void lj4_printer::hpgl_init()
522 {
523   if (hpgl_inited)
524     return;
525   hpgl_inited = 1;
526   hpgl_scale = double(DEFAULT_HPGL_UNITS)/font::res;
527   printf("\033&f0S"             // push position
528          "\033*p0x0Y"           // move to 0,0
529          "\033*c%dx%dy0T" // establish picture frame
530          "\033%%1B"               // switch to HPGL
531          "SP1SC0,%.4f,0,-%.4f,2IR0,100,0,100" // set up scaling
532          "LA1,4,2,4"            // round line ends and joins
533          "PR"                   // relative plotting
534          "TR0"                  // opaque
535          ";\033%%1A"            // back to PCL
536          "\033&f1S",            // pop position
537          MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT,
538          hpgl_scale, hpgl_scale);
539 }
540
541 void lj4_printer::set_line_thickness(int size, int dot)
542 {
543   double pw;
544   if (line_thickness < 0)
545     pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0);
546   else
547     pw = line_thickness*25.4/font::res;
548   if (dot && pw < MIN_DOT_PEN_WIDTH)
549     pw = MIN_DOT_PEN_WIDTH;
550   if (pw != pen_width) {
551     printf("PW%f", pw);
552     pen_width = pw;
553   }
554 }
555
556 font *lj4_printer::make_font(const char *nm)
557 {
558   return lj4_font::load_lj4_font(nm);
559 }
560
561 printer *make_printer()
562 {
563   return new lj4_printer;
564 }
565
566 static
567 int lookup_paper_size(const char *s)
568 {
569   for (int i = 0; i < sizeof(paper_table)/sizeof(paper_table[0]); i++)
570     // FIXME Do this case-insensitively.
571     // Perhaps allow unique prefix.
572     if (strcmp(s, paper_table[i].name) == 0)
573       return i;
574   return -1;
575 }
576
577 static
578 void handle_unknown_desc_command(const char *command, const char *arg,
579                                  const char *filename, int lineno)
580 {
581   if (strcmp(command, "papersize") == 0) {
582     if (arg == 0)
583       error_with_file_and_line(filename, lineno,
584                                "`papersize' command requires an argument");
585     else if (paper_size < 0) {
586       int n = lookup_paper_size(arg);
587       if (n < 0)
588         error_with_file_and_line(filename, lineno,
589                                  "unknown paper size `%1'", arg);
590       else
591         paper_size = n;
592     }
593   }
594 }
595
596 static void usage();
597
598 int main(int argc, char **argv)
599 {
600   program_name = argv[0];
601   static char stderr_buf[BUFSIZ];
602   setbuf(stderr, stderr_buf);
603   font::set_unknown_desc_command_handler(handle_unknown_desc_command);
604   int c;
605   while ((c = getopt(argc, argv, "F:p:lvw:c:")) != EOF)
606     switch(c) {
607     case 'l':
608       landscape_flag = 1;
609       break;
610     case 'p':
611       {
612         int n = lookup_paper_size(optarg);
613         if (n < 0)
614           error("unknown paper size `%1'", optarg);
615         else
616           paper_size = n;
617         break;
618       }
619     case 'v':
620       {
621         extern const char *version_string;
622         fprintf(stderr, "grolj4 version %s\n", version_string);
623         fflush(stderr);
624         break;
625       }
626     case 'F':
627       font::command_line_font_dir(optarg);
628       break;
629     case 'c':
630       {
631         char *ptr;
632         long n = strtol(optarg, &ptr, 10);
633         if (n == 0 && ptr == optarg)
634           error("argument for -c must be a positive integer");
635         else if (n <= 0 || n > 32767)
636           error("out of range argument for -c");
637         else
638           ncopies = unsigned(n);
639         break;
640       }
641     case 'w':
642       {
643         char *ptr;
644         long n = strtol(optarg, &ptr, 10);
645         if (n == 0 && ptr == optarg)
646           error("argument for -w must be a non-negative integer");
647         else if (n < 0 || n > INT_MAX)
648           error("out of range argument for -w");
649         else
650           line_width_factor = int(n);
651         break;
652       }
653     case '?':
654       usage();
655       break;
656     default:
657       assert(0);
658     }
659   if (optind >= argc)
660     do_file("-");
661   else {
662     for (int i = optind; i < argc; i++)
663       do_file(argv[i]);
664   }
665   delete pr;
666   return 0;
667 }
668
669 static void usage()
670 {
671   fprintf(stderr,
672           "usage: %s [-lv] [-c n] [-p paper_size] [-w n] [-F dir] [files ...]\n",
673           program_name);
674   exit(1);
675 }