Update to groff 1.19.2.
[dragonfly.git] / contrib / groff-1.19 / src / preproc / pic / troff.cpp
blob688ca47b9c4760d4cdab1e02af8c8ef390f7176d
1 // -*- C++ -*-
2 /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2005
3 Free Software Foundation, Inc.
4 Written by James Clark (jjc@jclark.com)
6 This file is part of groff.
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 2, or (at your option) any later
11 version.
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.
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
22 #include "pic.h"
23 #include "common.h"
26 const double RELATIVE_THICKNESS = -1.0;
27 const double BAD_THICKNESS = -2.0;
29 class simple_output : public common_output {
30 virtual void simple_line(const position &, const position &) = 0;
31 virtual void simple_spline(const position &, const position *, int n) = 0;
32 virtual void simple_arc(const position &, const position &,
33 const position &) = 0;
34 virtual void simple_circle(int, const position &, double rad) = 0;
35 virtual void simple_ellipse(int, const position &, const distance &) = 0;
36 virtual void simple_polygon(int, const position *, int) = 0;
37 virtual void line_thickness(double) = 0;
38 virtual void set_fill(double) = 0;
39 virtual void set_color(char *, char *) = 0;
40 virtual void reset_color() = 0;
41 virtual char *get_last_filled() = 0;
42 void dot(const position &, const line_type &) = 0;
43 public:
44 void start_picture(double sc, const position &ll, const position &ur) = 0;
45 void finish_picture() = 0;
46 void text(const position &, text_piece *, int, double) = 0;
47 void line(const position &, const position *, int n,
48 const line_type &);
49 void polygon(const position *, int n,
50 const line_type &, double);
51 void spline(const position &, const position *, int n,
52 const line_type &);
53 void arc(const position &, const position &, const position &,
54 const line_type &);
55 void circle(const position &, double rad, const line_type &, double);
56 void ellipse(const position &, const distance &, const line_type &, double);
57 int supports_filled_polygons();
60 int simple_output::supports_filled_polygons()
62 return driver_extension_flag != 0;
65 void simple_output::arc(const position &start, const position &cent,
66 const position &end, const line_type &lt)
68 switch (lt.type) {
69 case line_type::solid:
70 line_thickness(lt.thickness);
71 simple_arc(start, cent, end);
72 break;
73 case line_type::invisible:
74 break;
75 case line_type::dashed:
76 dashed_arc(start, cent, end, lt);
77 break;
78 case line_type::dotted:
79 dotted_arc(start, cent, end, lt);
80 break;
84 void simple_output::line(const position &start, const position *v, int n,
85 const line_type &lt)
87 position pos = start;
88 line_thickness(lt.thickness);
89 for (int i = 0; i < n; i++) {
90 switch (lt.type) {
91 case line_type::solid:
92 simple_line(pos, v[i]);
93 break;
94 case line_type::dotted:
96 distance vec(v[i] - pos);
97 double dist = hypot(vec);
98 int ndots = int(dist/lt.dash_width + .5);
99 if (ndots == 0)
100 dot(pos, lt);
101 else {
102 vec /= double(ndots);
103 for (int j = 0; j <= ndots; j++)
104 dot(pos + vec*j, lt);
107 break;
108 case line_type::dashed:
110 distance vec(v[i] - pos);
111 double dist = hypot(vec);
112 if (dist <= lt.dash_width*2.0)
113 simple_line(pos, v[i]);
114 else {
115 int ndashes = int((dist - lt.dash_width)/(lt.dash_width*2.0) + .5);
116 distance dash_vec = vec*(lt.dash_width/dist);
117 double dash_gap = (dist - lt.dash_width)/ndashes;
118 distance dash_gap_vec = vec*(dash_gap/dist);
119 for (int j = 0; j <= ndashes; j++) {
120 position s(pos + dash_gap_vec*j);
121 simple_line(s, s + dash_vec);
125 break;
126 case line_type::invisible:
127 break;
128 default:
129 assert(0);
131 pos = v[i];
135 void simple_output::spline(const position &start, const position *v, int n,
136 const line_type &lt)
138 line_thickness(lt.thickness);
139 simple_spline(start, v, n);
142 void simple_output::polygon(const position *v, int n,
143 const line_type &lt, double fill)
145 if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
146 if (get_last_filled() == 0)
147 set_fill(fill);
148 simple_polygon(1, v, n);
150 if (lt.type == line_type::solid && driver_extension_flag) {
151 line_thickness(lt.thickness);
152 simple_polygon(0, v, n);
154 else if (lt.type != line_type::invisible) {
155 line_thickness(lt.thickness);
156 line(v[n - 1], v, n, lt);
160 void simple_output::circle(const position &cent, double rad,
161 const line_type &lt, double fill)
163 if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
164 if (get_last_filled() == 0)
165 set_fill(fill);
166 simple_circle(1, cent, rad);
168 line_thickness(lt.thickness);
169 switch (lt.type) {
170 case line_type::invisible:
171 break;
172 case line_type::dashed:
173 dashed_circle(cent, rad, lt);
174 break;
175 case line_type::dotted:
176 dotted_circle(cent, rad, lt);
177 break;
178 case line_type::solid:
179 simple_circle(0, cent, rad);
180 break;
181 default:
182 assert(0);
186 void simple_output::ellipse(const position &cent, const distance &dim,
187 const line_type &lt, double fill)
189 if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
190 if (get_last_filled() == 0)
191 set_fill(fill);
192 simple_ellipse(1, cent, dim);
194 if (lt.type != line_type::invisible)
195 line_thickness(lt.thickness);
196 switch (lt.type) {
197 case line_type::invisible:
198 break;
199 case line_type::dotted:
200 dotted_ellipse(cent, dim, lt);
201 break;
202 case line_type::dashed:
203 dashed_ellipse(cent, dim, lt);
204 break;
205 case line_type::solid:
206 simple_ellipse(0, cent, dim);
207 break;
208 default:
209 assert(0);
213 class troff_output : public simple_output {
214 const char *last_filename;
215 position upper_left;
216 double height;
217 double scale;
218 double last_line_thickness;
219 double last_fill;
220 char *last_filled; // color
221 char *last_outlined; // color
222 public:
223 troff_output();
224 ~troff_output();
225 void start_picture(double, const position &ll, const position &ur);
226 void finish_picture();
227 void text(const position &, text_piece *, int, double);
228 void dot(const position &, const line_type &);
229 void command(const char *, const char *, int);
230 void set_location(const char *, int);
231 void simple_line(const position &, const position &);
232 void simple_spline(const position &, const position *, int n);
233 void simple_arc(const position &, const position &, const position &);
234 void simple_circle(int, const position &, double rad);
235 void simple_ellipse(int, const position &, const distance &);
236 void simple_polygon(int, const position *, int);
237 void line_thickness(double p);
238 void set_fill(double);
239 void set_color(char *, char *);
240 void reset_color();
241 char *get_last_filled();
242 char *get_outline_color();
243 position transform(const position &);
246 output *make_troff_output()
248 return new troff_output;
251 troff_output::troff_output()
252 : last_filename(0), last_line_thickness(BAD_THICKNESS),
253 last_fill(-1.0), last_filled(0), last_outlined(0)
257 troff_output::~troff_output()
261 inline position troff_output::transform(const position &pos)
263 return position((pos.x - upper_left.x)/scale,
264 (upper_left.y - pos.y)/scale);
267 #define FILL_REG "00"
269 // If this register > 0, then pic will generate \X'ps: ...' commands
270 // if the aligned attribute is used.
271 #define GROPS_REG "0p"
273 // If this register is defined, geqn won't produce `\x's.
274 #define EQN_NO_EXTRA_SPACE_REG "0x"
276 void troff_output::start_picture(double sc,
277 const position &ll, const position &ur)
279 upper_left.x = ll.x;
280 upper_left.y = ur.y;
281 scale = compute_scale(sc, ll, ur);
282 height = (ur.y - ll.y)/scale;
283 double width = (ur.x - ll.x)/scale;
284 printf(".PS %.3fi %.3fi", height, width);
285 if (args)
286 printf(" %s\n", args);
287 else
288 putchar('\n');
289 printf(".\\\" %g %g %g %g\n", ll.x, ll.y, ur.x, ur.y);
290 printf(".\\\" %.3fi %.3fi %.3fi %.3fi\n", 0.0, height, width, 0.0);
291 printf(".nr " FILL_REG " \\n(.u\n.nf\n");
292 printf(".nr " EQN_NO_EXTRA_SPACE_REG " 1\n");
293 // This guarantees that if the picture is used in a diversion it will
294 // have the right width.
295 printf("\\h'%.3fi'\n.sp -1\n", width);
298 void troff_output::finish_picture()
300 line_thickness(BAD_THICKNESS);
301 last_fill = -1.0; // force it to be reset for each picture
302 reset_color();
303 if (!flyback_flag)
304 printf(".sp %.3fi+1\n", height);
305 printf(".if \\n(" FILL_REG " .fi\n");
306 printf(".br\n");
307 printf(".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
308 // this is a little gross
309 set_location(current_filename, current_lineno);
310 fputs(flyback_flag ? ".PF\n" : ".PE\n", stdout);
313 void troff_output::command(const char *s,
314 const char *filename, int lineno)
316 if (filename != 0)
317 set_location(filename, lineno);
318 fputs(s, stdout);
319 putchar('\n');
322 void troff_output::simple_circle(int filled, const position &cent, double rad)
324 position c = transform(cent);
325 printf("\\h'%.3fi'"
326 "\\v'%.3fi'"
327 "\\D'%c %.3fi'"
328 "\n.sp -1\n",
329 c.x - rad/scale,
330 c.y,
331 (filled ? 'C' : 'c'),
332 rad*2.0/scale);
335 void troff_output::simple_ellipse(int filled, const position &cent,
336 const distance &dim)
338 position c = transform(cent);
339 printf("\\h'%.3fi'"
340 "\\v'%.3fi'"
341 "\\D'%c %.3fi %.3fi'"
342 "\n.sp -1\n",
343 c.x - dim.x/(2.0*scale),
344 c.y,
345 (filled ? 'E' : 'e'),
346 dim.x/scale, dim.y/scale);
349 void troff_output::simple_arc(const position &start, const distance &cent,
350 const distance &end)
352 position s = transform(start);
353 position c = transform(cent);
354 distance cv = c - s;
355 distance ev = transform(end) - c;
356 printf("\\h'%.3fi'"
357 "\\v'%.3fi'"
358 "\\D'a %.3fi %.3fi %.3fi %.3fi'"
359 "\n.sp -1\n",
360 s.x, s.y, cv.x, cv.y, ev.x, ev.y);
363 void troff_output::simple_line(const position &start, const position &end)
365 position s = transform(start);
366 distance ev = transform(end) - s;
367 printf("\\h'%.3fi'"
368 "\\v'%.3fi'"
369 "\\D'l %.3fi %.3fi'"
370 "\n.sp -1\n",
371 s.x, s.y, ev.x, ev.y);
374 void troff_output::simple_spline(const position &start,
375 const position *v, int n)
377 position pos = transform(start);
378 printf("\\h'%.3fi'"
379 "\\v'%.3fi'",
380 pos.x, pos.y);
381 fputs("\\D'~ ", stdout);
382 for (int i = 0; i < n; i++) {
383 position temp = transform(v[i]);
384 distance d = temp - pos;
385 pos = temp;
386 if (i != 0)
387 putchar(' ');
388 printf("%.3fi %.3fi", d.x, d.y);
390 printf("'\n.sp -1\n");
393 // a solid polygon
395 void troff_output::simple_polygon(int filled, const position *v, int n)
397 position pos = transform(v[0]);
398 printf("\\h'%.3fi'"
399 "\\v'%.3fi'",
400 pos.x, pos.y);
401 printf("\\D'%c ", (filled ? 'P' : 'p'));
402 for (int i = 1; i < n; i++) {
403 position temp = transform(v[i]);
404 distance d = temp - pos;
405 pos = temp;
406 if (i != 1)
407 putchar(' ');
408 printf("%.3fi %.3fi", d.x, d.y);
410 printf("'\n.sp -1\n");
413 const double TEXT_AXIS = 0.22; // in ems
415 static const char *choose_delimiter(const char *text)
417 if (strchr(text, '\'') == 0)
418 return "'";
419 else
420 return "\\(ts";
423 void troff_output::text(const position &center, text_piece *v, int n,
424 double ang)
426 line_thickness(BAD_THICKNESS); // the text might use lines (eg in equations)
427 int rotate_flag = 0;
428 if (driver_extension_flag && ang != 0.0) {
429 rotate_flag = 1;
430 position c = transform(center);
431 printf(".if \\n(" GROPS_REG " \\{\\\n"
432 "\\h'%.3fi'"
433 "\\v'%.3fi'"
434 "\\X'ps: exec gsave currentpoint 2 copy translate %.4f rotate neg exch neg exch translate'"
435 "\n.sp -1\n"
436 ".\\}\n",
437 c.x, c.y, -ang*180.0/M_PI);
439 for (int i = 0; i < n; i++)
440 if (v[i].text != 0 && *v[i].text != '\0') {
441 position c = transform(center);
442 if (v[i].filename != 0)
443 set_location(v[i].filename, v[i].lineno);
444 printf("\\h'%.3fi", c.x);
445 const char *delim = choose_delimiter(v[i].text);
446 if (v[i].adj.h == RIGHT_ADJUST)
447 printf("-\\w%s%s%su", delim, v[i].text, delim);
448 else if (v[i].adj.h != LEFT_ADJUST)
449 printf("-(\\w%s%s%su/2u)", delim, v[i].text, delim);
450 putchar('\'');
451 printf("\\v'%.3fi-(%dv/2u)+%dv+%.2fm",
452 c.y,
453 n - 1,
455 TEXT_AXIS);
456 if (v[i].adj.v == ABOVE_ADJUST)
457 printf("-.5v");
458 else if (v[i].adj.v == BELOW_ADJUST)
459 printf("+.5v");
460 putchar('\'');
461 fputs(v[i].text, stdout);
462 fputs("\n.sp -1\n", stdout);
464 if (rotate_flag)
465 printf(".if '\\*(.T'ps' \\{\\\n"
466 "\\X'ps: exec grestore'\n.sp -1\n"
467 ".\\}\n");
470 void troff_output::line_thickness(double p)
472 if (p < 0.0)
473 p = RELATIVE_THICKNESS;
474 if (driver_extension_flag && p != last_line_thickness) {
475 printf("\\D't %.3fp'\\h'%.3fp'\n.sp -1\n", p, -p);
476 last_line_thickness = p;
480 void troff_output::set_fill(double f)
482 if (driver_extension_flag && f != last_fill) {
483 // \D'Fg ...' emits a node only in compatibility mode,
484 // thus we add a dummy node
485 printf("\\&\\D'Fg %.3f'\n.sp -1\n", 1.0 - f);
486 last_fill = f;
488 if (last_filled) {
489 free(last_filled);
490 last_filled = 0;
491 printf(".fcolor\n");
495 void troff_output::set_color(char *color_fill, char *color_outlined)
497 if (driver_extension_flag) {
498 if (last_filled || last_outlined) {
499 reset_color();
501 // .gcolor and .fcolor emit a node in compatibility mode only,
502 // but that won't work anyway
503 if (color_fill) {
504 printf(".fcolor %s\n", color_fill);
505 last_filled = strsave(color_fill);
507 if (color_outlined) {
508 printf(".gcolor %s\n", color_outlined);
509 last_outlined = strsave(color_outlined);
514 void troff_output::reset_color()
516 if (driver_extension_flag) {
517 if (last_filled) {
518 printf(".fcolor\n");
519 a_delete last_filled;
520 last_filled = 0;
522 if (last_outlined) {
523 printf(".gcolor\n");
524 a_delete last_outlined;
525 last_outlined = 0;
530 char *troff_output::get_last_filled()
532 return last_filled;
535 char *troff_output::get_outline_color()
537 return last_outlined;
540 const double DOT_AXIS = .044;
542 void troff_output::dot(const position &cent, const line_type &lt)
544 if (driver_extension_flag) {
545 line_thickness(lt.thickness);
546 simple_line(cent, cent);
548 else {
549 position c = transform(cent);
550 printf("\\h'%.3fi-(\\w'.'u/2u)'"
551 "\\v'%.3fi+%.2fm'"
552 ".\n.sp -1\n",
553 c.x,
554 c.y,
555 DOT_AXIS);
559 void troff_output::set_location(const char *s, int n)
561 if (last_filename != 0 && strcmp(s, last_filename) == 0)
562 printf(".lf %d\n", n);
563 else {
564 printf(".lf %d %s\n", n, s);
565 last_filename = s;