Sync usage with man page.
[netbsd-mini2440.git] / gnu / dist / groff / src / preproc / pic / troff.cpp
blob4c28c5e6ee6be6ffe6d3d92feb656b902c7541c4
1 /* $NetBSD$ */
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)
8 This file is part of groff.
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.
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.
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. */
24 #include "pic.h"
25 #include "common.h"
28 const double RELATIVE_THICKNESS = -1.0;
29 const double BAD_THICKNESS = -2.0;
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();
62 int simple_output::supports_filled_polygons()
64 return driver_extension_flag != 0;
67 void simple_output::arc(const position &start, const position &cent,
68 const position &end, const line_type &lt)
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;
86 void simple_output::line(const position &start, const position *v, int n,
87 const line_type &lt)
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:
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);
109 break;
110 case line_type::dashed:
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);
127 break;
128 case line_type::invisible:
129 break;
130 default:
131 assert(0);
133 pos = v[i];
137 void simple_output::spline(const position &start, const position *v, int n,
138 const line_type &lt)
140 line_thickness(lt.thickness);
141 simple_spline(start, v, n);
144 void simple_output::polygon(const position *v, int n,
145 const line_type &lt, double fill)
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);
152 if (lt.type == line_type::solid && driver_extension_flag) {
153 line_thickness(lt.thickness);
154 simple_polygon(0, v, n);
156 else if (lt.type != line_type::invisible) {
157 line_thickness(lt.thickness);
158 line(v[n - 1], v, n, lt);
162 void simple_output::circle(const position &cent, double rad,
163 const line_type &lt, double fill)
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);
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);
188 void simple_output::ellipse(const position &cent, const distance &dim,
189 const line_type &lt, double fill)
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);
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);
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 &);
248 output *make_troff_output()
250 return new 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)
259 troff_output::~troff_output()
263 inline position troff_output::transform(const position &pos)
265 return position((pos.x - upper_left.x)/scale,
266 (upper_left.y - pos.y)/scale);
269 #define FILL_REG "00"
271 // If this register > 0, then pic will generate \X'ps: ...' commands
272 // if the aligned attribute is used.
273 #define GROPS_REG "0p"
275 // If this register is defined, geqn won't produce `\x's.
276 #define EQN_NO_EXTRA_SPACE_REG "0x"
278 void troff_output::start_picture(double sc,
279 const position &ll, const position &ur)
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);
300 void troff_output::finish_picture()
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);
315 void troff_output::command(const char *s,
316 const char *filename, int lineno)
318 if (filename != 0)
319 set_location(filename, lineno);
320 fputs(s, stdout);
321 putchar('\n');
324 void troff_output::simple_circle(int filled, const position &cent, double rad)
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);
337 void troff_output::simple_ellipse(int filled, const position &cent,
338 const distance &dim)
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);
351 void troff_output::simple_arc(const position &start, const distance &cent,
352 const distance &end)
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);
365 void troff_output::simple_line(const position &start, const position &end)
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);
376 void troff_output::simple_spline(const position &start,
377 const position *v, int n)
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);
392 printf("'\n.sp -1\n");
395 // a solid polygon
397 void troff_output::simple_polygon(int filled, const position *v, int n)
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);
412 printf("'\n.sp -1\n");
415 const double TEXT_AXIS = 0.22; // in ems
417 static const char *choose_delimiter(const char *text)
419 if (strchr(text, '\'') == 0)
420 return "'";
421 else
422 return "\\(ts";
425 void troff_output::text(const position &center, text_piece *v, int n,
426 double ang)
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);
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,
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);
466 if (rotate_flag)
467 printf(".if '\\*(.T'ps' \\{\\\n"
468 "\\X'ps: exec grestore'\n.sp -1\n"
469 ".\\}\n");
472 void troff_output::line_thickness(double p)
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;
482 void troff_output::set_fill(double f)
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;
490 if (last_filled) {
491 free(last_filled);
492 last_filled = 0;
493 printf(".fcolor\n");
497 void troff_output::set_color(char *color_fill, char *color_outlined)
499 if (driver_extension_flag) {
500 if (last_filled || last_outlined) {
501 reset_color();
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);
509 if (color_outlined) {
510 printf(".gcolor %s\n", color_outlined);
511 last_outlined = strsave(color_outlined);
516 void troff_output::reset_color()
518 if (driver_extension_flag) {
519 if (last_filled) {
520 printf(".fcolor\n");
521 a_delete last_filled;
522 last_filled = 0;
524 if (last_outlined) {
525 printf(".gcolor\n");
526 a_delete last_outlined;
527 last_outlined = 0;
532 char *troff_output::get_last_filled()
534 return last_filled;
537 char *troff_output::get_outline_color()
539 return last_outlined;
542 const double DOT_AXIS = .044;
544 void troff_output::dot(const position &cent, const line_type &lt)
546 if (driver_extension_flag) {
547 line_thickness(lt.thickness);
548 simple_line(cent, cent);
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);
561 void troff_output::set_location(const char *s, int n)
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;