xref: /netbsd-src/external/gpl2/groff/dist/src/preproc/pic/troff.cpp (revision 89a07cf815a29524268025a1139fac4c5190f765)
1 /*	$NetBSD: troff.cpp,v 1.1.1.1 2016/01/13 18:41:49 christos Exp $	*/
2 
3 // -*- C++ -*-
4 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2005
5    Free Software Foundation, Inc.
6      Written by James Clark (jjc@jclark.com)
7 
8 This file is part of groff.
9 
10 groff is free software; you can redistribute it and/or modify it under
11 the terms of the GNU General Public License as published by the Free
12 Software Foundation; either version 2, or (at your option) any later
13 version.
14 
15 groff is distributed in the hope that it will be useful, but WITHOUT ANY
16 WARRANTY; without even the implied warranty of MERCHANTABILITY or
17 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18 for more details.
19 
20 You should have received a copy of the GNU General Public License along
21 with groff; see the file COPYING.  If not, write to the Free Software
22 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
23 
24 #include "pic.h"
25 #include "common.h"
26 
27 
28 const double RELATIVE_THICKNESS = -1.0;
29 const double BAD_THICKNESS = -2.0;
30 
31 class simple_output : public common_output {
32   virtual void simple_line(const position &, const position &) = 0;
33   virtual void simple_spline(const position &, const position *, int n) = 0;
34   virtual void simple_arc(const position &, const position &,
35 			  const position &) = 0;
36   virtual void simple_circle(int, const position &, double rad) = 0;
37   virtual void simple_ellipse(int, const position &, const distance &) = 0;
38   virtual void simple_polygon(int, const position *, int) = 0;
39   virtual void line_thickness(double) = 0;
40   virtual void set_fill(double) = 0;
41   virtual void set_color(char *, char *) = 0;
42   virtual void reset_color() = 0;
43   virtual char *get_last_filled() = 0;
44   void dot(const position &, const line_type &) = 0;
45 public:
46   void start_picture(double sc, const position &ll, const position &ur) = 0;
47   void finish_picture() = 0;
48   void text(const position &, text_piece *, int, double) = 0;
49   void line(const position &, const position *, int n,
50 	    const line_type &);
51   void polygon(const position *, int n,
52 	       const line_type &, double);
53   void spline(const position &, const position *, int n,
54 	      const line_type &);
55   void arc(const position &, const position &, const position &,
56 	   const line_type &);
57   void circle(const position &, double rad, const line_type &, double);
58   void ellipse(const position &, const distance &, const line_type &, double);
59   int supports_filled_polygons();
60 };
61 
supports_filled_polygons()62 int simple_output::supports_filled_polygons()
63 {
64   return driver_extension_flag != 0;
65 }
66 
arc(const position & start,const position & cent,const position & end,const line_type & lt)67 void simple_output::arc(const position &start, const position &cent,
68 			const position &end, const line_type &lt)
69 {
70   switch (lt.type) {
71   case line_type::solid:
72     line_thickness(lt.thickness);
73     simple_arc(start, cent, end);
74     break;
75   case line_type::invisible:
76     break;
77   case line_type::dashed:
78     dashed_arc(start, cent, end, lt);
79     break;
80   case line_type::dotted:
81     dotted_arc(start, cent, end, lt);
82     break;
83   }
84 }
85 
line(const position & start,const position * v,int n,const line_type & lt)86 void simple_output::line(const position &start, const position *v, int n,
87 			 const line_type &lt)
88 {
89   position pos = start;
90   line_thickness(lt.thickness);
91   for (int i = 0; i < n; i++) {
92     switch (lt.type) {
93     case line_type::solid:
94       simple_line(pos, v[i]);
95       break;
96     case line_type::dotted:
97       {
98 	distance vec(v[i] - pos);
99 	double dist = hypot(vec);
100 	int ndots = int(dist/lt.dash_width + .5);
101 	if (ndots == 0)
102 	  dot(pos, lt);
103 	else {
104 	  vec /= double(ndots);
105 	  for (int j = 0; j <= ndots; j++)
106 	    dot(pos + vec*j, lt);
107 	}
108       }
109       break;
110     case line_type::dashed:
111       {
112 	distance vec(v[i] - pos);
113 	double dist = hypot(vec);
114 	if (dist <= lt.dash_width*2.0)
115 	  simple_line(pos, v[i]);
116 	else {
117 	  int ndashes = int((dist - lt.dash_width)/(lt.dash_width*2.0) + .5);
118 	  distance dash_vec = vec*(lt.dash_width/dist);
119 	  double dash_gap = (dist - lt.dash_width)/ndashes;
120 	  distance dash_gap_vec = vec*(dash_gap/dist);
121 	  for (int j = 0; j <= ndashes; j++) {
122 	    position s(pos + dash_gap_vec*j);
123 	    simple_line(s, s + dash_vec);
124 	  }
125 	}
126       }
127       break;
128     case line_type::invisible:
129       break;
130     default:
131       assert(0);
132     }
133     pos = v[i];
134   }
135 }
136 
spline(const position & start,const position * v,int n,const line_type & lt)137 void simple_output::spline(const position &start, const position *v, int n,
138 			   const line_type &lt)
139 {
140   line_thickness(lt.thickness);
141   simple_spline(start, v, n);
142 }
143 
polygon(const position * v,int n,const line_type & lt,double fill)144 void simple_output::polygon(const position *v, int n,
145 			    const line_type &lt, double fill)
146 {
147   if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
148     if (get_last_filled() == 0)
149       set_fill(fill);
150     simple_polygon(1, v, n);
151   }
152   if (lt.type == line_type::solid && driver_extension_flag) {
153     line_thickness(lt.thickness);
154     simple_polygon(0, v, n);
155   }
156   else if (lt.type != line_type::invisible) {
157     line_thickness(lt.thickness);
158     line(v[n - 1], v, n, lt);
159   }
160 }
161 
circle(const position & cent,double rad,const line_type & lt,double fill)162 void simple_output::circle(const position &cent, double rad,
163 			   const line_type &lt, double fill)
164 {
165   if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
166     if (get_last_filled() == 0)
167       set_fill(fill);
168     simple_circle(1, cent, rad);
169   }
170   line_thickness(lt.thickness);
171   switch (lt.type) {
172   case line_type::invisible:
173     break;
174   case line_type::dashed:
175     dashed_circle(cent, rad, lt);
176     break;
177   case line_type::dotted:
178     dotted_circle(cent, rad, lt);
179     break;
180   case line_type::solid:
181     simple_circle(0, cent, rad);
182     break;
183   default:
184     assert(0);
185   }
186 }
187 
ellipse(const position & cent,const distance & dim,const line_type & lt,double fill)188 void simple_output::ellipse(const position &cent, const distance &dim,
189 			    const line_type &lt, double fill)
190 {
191   if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
192     if (get_last_filled() == 0)
193       set_fill(fill);
194     simple_ellipse(1, cent, dim);
195   }
196   if (lt.type != line_type::invisible)
197     line_thickness(lt.thickness);
198   switch (lt.type) {
199   case line_type::invisible:
200     break;
201   case line_type::dotted:
202     dotted_ellipse(cent, dim, lt);
203     break;
204   case line_type::dashed:
205     dashed_ellipse(cent, dim, lt);
206     break;
207   case line_type::solid:
208     simple_ellipse(0, cent, dim);
209     break;
210   default:
211     assert(0);
212   }
213 }
214 
215 class troff_output : public simple_output {
216   const char *last_filename;
217   position upper_left;
218   double height;
219   double scale;
220   double last_line_thickness;
221   double last_fill;
222   char *last_filled;		// color
223   char *last_outlined;		// color
224 public:
225   troff_output();
226   ~troff_output();
227   void start_picture(double, const position &ll, const position &ur);
228   void finish_picture();
229   void text(const position &, text_piece *, int, double);
230   void dot(const position &, const line_type &);
231   void command(const char *, const char *, int);
232   void set_location(const char *, int);
233   void simple_line(const position &, const position &);
234   void simple_spline(const position &, const position *, int n);
235   void simple_arc(const position &, const position &, const position &);
236   void simple_circle(int, const position &, double rad);
237   void simple_ellipse(int, const position &, const distance &);
238   void simple_polygon(int, const position *, int);
239   void line_thickness(double p);
240   void set_fill(double);
241   void set_color(char *, char *);
242   void reset_color();
243   char *get_last_filled();
244   char *get_outline_color();
245   position transform(const position &);
246 };
247 
make_troff_output()248 output *make_troff_output()
249 {
250   return new troff_output;
251 }
252 
troff_output()253 troff_output::troff_output()
254 : last_filename(0), last_line_thickness(BAD_THICKNESS),
255   last_fill(-1.0), last_filled(0), last_outlined(0)
256 {
257 }
258 
~troff_output()259 troff_output::~troff_output()
260 {
261 }
262 
transform(const position & pos)263 inline position troff_output::transform(const position &pos)
264 {
265   return position((pos.x - upper_left.x)/scale,
266 		  (upper_left.y - pos.y)/scale);
267 }
268 
269 #define FILL_REG "00"
270 
271 // If this register > 0, then pic will generate \X'ps: ...' commands
272 // if the aligned attribute is used.
273 #define GROPS_REG "0p"
274 
275 // If this register is defined, geqn won't produce `\x's.
276 #define EQN_NO_EXTRA_SPACE_REG "0x"
277 
start_picture(double sc,const position & ll,const position & ur)278 void troff_output::start_picture(double sc,
279 				 const position &ll, const position &ur)
280 {
281   upper_left.x = ll.x;
282   upper_left.y = ur.y;
283   scale = compute_scale(sc, ll, ur);
284   height = (ur.y - ll.y)/scale;
285   double width = (ur.x - ll.x)/scale;
286   printf(".PS %.3fi %.3fi", height, width);
287   if (args)
288     printf(" %s\n", args);
289   else
290     putchar('\n');
291   printf(".\\\" %g %g %g %g\n", ll.x, ll.y, ur.x, ur.y);
292   printf(".\\\" %.3fi %.3fi %.3fi %.3fi\n", 0.0, height, width, 0.0);
293   printf(".nr " FILL_REG " \\n(.u\n.nf\n");
294   printf(".nr " EQN_NO_EXTRA_SPACE_REG " 1\n");
295   // This guarantees that if the picture is used in a diversion it will
296   // have the right width.
297   printf("\\h'%.3fi'\n.sp -1\n", width);
298 }
299 
finish_picture()300 void troff_output::finish_picture()
301 {
302   line_thickness(BAD_THICKNESS);
303   last_fill = -1.0;		// force it to be reset for each picture
304   reset_color();
305   if (!flyback_flag)
306     printf(".sp %.3fi+1\n", height);
307   printf(".if \\n(" FILL_REG " .fi\n");
308   printf(".br\n");
309   printf(".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
310   // this is a little gross
311   set_location(current_filename, current_lineno);
312   fputs(flyback_flag ? ".PF\n" : ".PE\n", stdout);
313 }
314 
command(const char * s,const char * filename,int lineno)315 void troff_output::command(const char *s,
316 			   const char *filename, int lineno)
317 {
318   if (filename != 0)
319     set_location(filename, lineno);
320   fputs(s, stdout);
321   putchar('\n');
322 }
323 
simple_circle(int filled,const position & cent,double rad)324 void troff_output::simple_circle(int filled, const position &cent, double rad)
325 {
326   position c = transform(cent);
327   printf("\\h'%.3fi'"
328 	 "\\v'%.3fi'"
329 	 "\\D'%c %.3fi'"
330 	 "\n.sp -1\n",
331 	 c.x - rad/scale,
332 	 c.y,
333 	 (filled ? 'C' : 'c'),
334 	 rad*2.0/scale);
335 }
336 
simple_ellipse(int filled,const position & cent,const distance & dim)337 void troff_output::simple_ellipse(int filled, const position &cent,
338 				  const distance &dim)
339 {
340   position c = transform(cent);
341   printf("\\h'%.3fi'"
342 	 "\\v'%.3fi'"
343 	 "\\D'%c %.3fi %.3fi'"
344 	 "\n.sp -1\n",
345 	 c.x - dim.x/(2.0*scale),
346 	 c.y,
347 	 (filled ? 'E' : 'e'),
348 	 dim.x/scale, dim.y/scale);
349 }
350 
simple_arc(const position & start,const distance & cent,const distance & end)351 void troff_output::simple_arc(const position &start, const distance &cent,
352 			      const distance &end)
353 {
354   position s = transform(start);
355   position c = transform(cent);
356   distance cv = c - s;
357   distance ev = transform(end) - c;
358   printf("\\h'%.3fi'"
359 	 "\\v'%.3fi'"
360 	 "\\D'a %.3fi %.3fi %.3fi %.3fi'"
361 	 "\n.sp -1\n",
362 	 s.x, s.y, cv.x, cv.y, ev.x, ev.y);
363 }
364 
simple_line(const position & start,const position & end)365 void troff_output::simple_line(const position &start, const position &end)
366 {
367   position s = transform(start);
368   distance ev = transform(end) - s;
369   printf("\\h'%.3fi'"
370 	 "\\v'%.3fi'"
371 	 "\\D'l %.3fi %.3fi'"
372 	 "\n.sp -1\n",
373 	 s.x, s.y, ev.x, ev.y);
374 }
375 
simple_spline(const position & start,const position * v,int n)376 void troff_output::simple_spline(const position &start,
377 				 const position *v, int n)
378 {
379   position pos = transform(start);
380   printf("\\h'%.3fi'"
381 	 "\\v'%.3fi'",
382 	 pos.x, pos.y);
383   fputs("\\D'~ ", stdout);
384   for (int i = 0; i < n; i++) {
385     position temp = transform(v[i]);
386     distance d = temp - pos;
387     pos = temp;
388     if (i != 0)
389       putchar(' ');
390     printf("%.3fi %.3fi", d.x, d.y);
391   }
392   printf("'\n.sp -1\n");
393 }
394 
395 // a solid polygon
396 
simple_polygon(int filled,const position * v,int n)397 void troff_output::simple_polygon(int filled, const position *v, int n)
398 {
399   position pos = transform(v[0]);
400   printf("\\h'%.3fi'"
401 	 "\\v'%.3fi'",
402 	 pos.x, pos.y);
403   printf("\\D'%c ", (filled ? 'P' : 'p'));
404   for (int i = 1; i < n; i++) {
405     position temp = transform(v[i]);
406     distance d = temp - pos;
407     pos = temp;
408     if (i != 1)
409       putchar(' ');
410     printf("%.3fi %.3fi", d.x, d.y);
411   }
412   printf("'\n.sp -1\n");
413 }
414 
415 const double TEXT_AXIS = 0.22;	// in ems
416 
choose_delimiter(const char * text)417 static const char *choose_delimiter(const char *text)
418 {
419   if (strchr(text, '\'') == 0)
420     return "'";
421   else
422     return "\\(ts";
423 }
424 
text(const position & center,text_piece * v,int n,double ang)425 void troff_output::text(const position &center, text_piece *v, int n,
426 			double ang)
427 {
428   line_thickness(BAD_THICKNESS); // the text might use lines (eg in equations)
429   int rotate_flag = 0;
430   if (driver_extension_flag && ang != 0.0) {
431     rotate_flag = 1;
432     position c = transform(center);
433     printf(".if \\n(" GROPS_REG " \\{\\\n"
434 	   "\\h'%.3fi'"
435 	   "\\v'%.3fi'"
436 	   "\\X'ps: exec gsave currentpoint 2 copy translate %.4f rotate neg exch neg exch translate'"
437 	   "\n.sp -1\n"
438 	   ".\\}\n",
439 	   c.x, c.y, -ang*180.0/M_PI);
440   }
441   for (int i = 0; i < n; i++)
442     if (v[i].text != 0 && *v[i].text != '\0') {
443       position c = transform(center);
444       if (v[i].filename != 0)
445 	set_location(v[i].filename, v[i].lineno);
446       printf("\\h'%.3fi", c.x);
447       const char *delim = choose_delimiter(v[i].text);
448       if (v[i].adj.h == RIGHT_ADJUST)
449 	printf("-\\w%s%s%su", delim, v[i].text, delim);
450       else if (v[i].adj.h != LEFT_ADJUST)
451 	printf("-(\\w%s%s%su/2u)", delim, v[i].text, delim);
452       putchar('\'');
453       printf("\\v'%.3fi-(%dv/2u)+%dv+%.2fm",
454 	     c.y,
455 	     n - 1,
456 	     i,
457 	     TEXT_AXIS);
458       if (v[i].adj.v == ABOVE_ADJUST)
459 	printf("-.5v");
460       else if (v[i].adj.v == BELOW_ADJUST)
461 	printf("+.5v");
462       putchar('\'');
463       fputs(v[i].text, stdout);
464       fputs("\n.sp -1\n", stdout);
465     }
466   if (rotate_flag)
467     printf(".if '\\*(.T'ps' \\{\\\n"
468 	   "\\X'ps: exec grestore'\n.sp -1\n"
469 	   ".\\}\n");
470 }
471 
line_thickness(double p)472 void troff_output::line_thickness(double p)
473 {
474   if (p < 0.0)
475     p = RELATIVE_THICKNESS;
476   if (driver_extension_flag && p != last_line_thickness) {
477     printf("\\D't %.3fp'\\h'%.3fp'\n.sp -1\n", p, -p);
478     last_line_thickness = p;
479   }
480 }
481 
set_fill(double f)482 void troff_output::set_fill(double f)
483 {
484   if (driver_extension_flag && f != last_fill) {
485     // \D'Fg ...' emits a node only in compatibility mode,
486     // thus we add a dummy node
487     printf("\\&\\D'Fg %.3f'\n.sp -1\n", 1.0 - f);
488     last_fill = f;
489   }
490   if (last_filled) {
491     free(last_filled);
492     last_filled = 0;
493     printf(".fcolor\n");
494   }
495 }
496 
set_color(char * color_fill,char * color_outlined)497 void troff_output::set_color(char *color_fill, char *color_outlined)
498 {
499   if (driver_extension_flag) {
500     if (last_filled || last_outlined) {
501       reset_color();
502     }
503     // .gcolor and .fcolor emit a node in compatibility mode only,
504     // but that won't work anyway
505     if (color_fill) {
506       printf(".fcolor %s\n", color_fill);
507       last_filled = strsave(color_fill);
508     }
509     if (color_outlined) {
510       printf(".gcolor %s\n", color_outlined);
511       last_outlined = strsave(color_outlined);
512     }
513   }
514 }
515 
reset_color()516 void troff_output::reset_color()
517 {
518   if (driver_extension_flag) {
519     if (last_filled) {
520       printf(".fcolor\n");
521       a_delete last_filled;
522       last_filled = 0;
523     }
524     if (last_outlined) {
525       printf(".gcolor\n");
526       a_delete last_outlined;
527       last_outlined = 0;
528     }
529   }
530 }
531 
get_last_filled()532 char *troff_output::get_last_filled()
533 {
534   return last_filled;
535 }
536 
get_outline_color()537 char *troff_output::get_outline_color()
538 {
539   return last_outlined;
540 }
541 
542 const double DOT_AXIS = .044;
543 
dot(const position & cent,const line_type & lt)544 void troff_output::dot(const position &cent, const line_type &lt)
545 {
546   if (driver_extension_flag) {
547     line_thickness(lt.thickness);
548     simple_line(cent, cent);
549   }
550   else {
551     position c = transform(cent);
552     printf("\\h'%.3fi-(\\w'.'u/2u)'"
553 	   "\\v'%.3fi+%.2fm'"
554 	   ".\n.sp -1\n",
555 	   c.x,
556 	   c.y,
557 	   DOT_AXIS);
558   }
559 }
560 
set_location(const char * s,int n)561 void troff_output::set_location(const char *s, int n)
562 {
563   if (last_filename != 0 && strcmp(s, last_filename) == 0)
564     printf(".lf %d\n", n);
565   else {
566     printf(".lf %d %s\n", n, s);
567     last_filename = s;
568   }
569 }
570