Copyright text: change "groff" to "This"
[s-roff.git] / src / pre-pic / troff.cpp
blob93d66c6ae2e0096d5bb52d2c811de9ffc1fe6ca7
1 /*@
2 * Copyright (c) 2014 Steffen (Daode) Nurpmeso <sdaoden@users.sf.net>.
4 * Copyright (C) 1989 - 1992, 2000 - 2003, 2005 Free Software Foundation, Inc.
5 * Written by James Clark (jjc@jclark.com)
7 * This 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.
12 * This 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.
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, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "config.h"
23 #include "pic-config.h"
25 #include "common.h"
26 #include "pic.h"
28 const double RELATIVE_THICKNESS = -1.0;
29 const double BAD_THICKNESS = -2.0;
31 class simple_output
32 : public common_output
34 virtual void simple_line(const position &, const position &) = 0;
35 virtual void simple_spline(const position &, const position *, int n) = 0;
36 virtual void simple_arc(const position &, const position &,
37 const position &) = 0;
38 virtual void simple_circle(int, const position &, double rad) = 0;
39 virtual void simple_ellipse(int, const position &, const distance &) = 0;
40 virtual void simple_polygon(int, const position *, int) = 0;
41 virtual void line_thickness(double) = 0;
42 virtual void set_fill(double) = 0;
43 virtual void set_color(char *, char *) = 0;
44 virtual void reset_color() = 0;
45 virtual char *get_last_filled() = 0;
46 void dot(const position &, const line_type &) = 0;
48 public:
49 void start_picture(double sc, const position &ll, const position &ur) = 0;
50 void finish_picture() = 0;
51 void text(const position &, text_piece *, int, double) = 0;
52 void line(const position &, const position *, int n,
53 const line_type &);
54 void polygon(const position *, int n,
55 const line_type &, double);
56 void spline(const position &, const position *, int n,
57 const line_type &);
58 void arc(const position &, const position &, const position &,
59 const line_type &);
60 void circle(const position &, double rad, const line_type &, double);
61 void ellipse(const position &, const distance &, const line_type &, double);
62 int supports_filled_polygons();
65 int simple_output::supports_filled_polygons()
67 return driver_extension_flag != 0;
70 void simple_output::arc(const position &start, const position &cent,
71 const position &end, const line_type &lt)
73 switch (lt.type) {
74 case line_type::solid:
75 line_thickness(lt.thickness);
76 simple_arc(start, cent, end);
77 break;
78 case line_type::invisible:
79 break;
80 case line_type::dashed:
81 dashed_arc(start, cent, end, lt);
82 break;
83 case line_type::dotted:
84 dotted_arc(start, cent, end, lt);
85 break;
89 void simple_output::line(const position &start, const position *v, int n,
90 const line_type &lt)
92 position pos = start;
93 line_thickness(lt.thickness);
94 for (int i = 0; i < n; i++) {
95 switch (lt.type) {
96 case line_type::solid:
97 simple_line(pos, v[i]);
98 break;
99 case line_type::dotted:
101 distance vec(v[i] - pos);
102 double dist = hypot(vec);
103 int ndots = int(dist/lt.dash_width + .5);
104 if (ndots == 0)
105 dot(pos, lt);
106 else {
107 vec /= double(ndots);
108 for (int j = 0; j <= ndots; j++)
109 dot(pos + vec*j, lt);
112 break;
113 case line_type::dashed:
115 distance vec(v[i] - pos);
116 double dist = hypot(vec);
117 if (dist <= lt.dash_width*2.0)
118 simple_line(pos, v[i]);
119 else {
120 int ndashes = int((dist - lt.dash_width)/(lt.dash_width*2.0) + .5);
121 distance dash_vec = vec*(lt.dash_width/dist);
122 double dash_gap = (dist - lt.dash_width)/ndashes;
123 distance dash_gap_vec = vec*(dash_gap/dist);
124 for (int j = 0; j <= ndashes; j++) {
125 position s(pos + dash_gap_vec*j);
126 simple_line(s, s + dash_vec);
130 break;
131 case line_type::invisible:
132 break;
133 default:
134 assert(0);
136 pos = v[i];
140 void simple_output::spline(const position &start, const position *v, int n,
141 const line_type &lt)
143 line_thickness(lt.thickness);
144 simple_spline(start, v, n);
147 void simple_output::polygon(const position *v, int n,
148 const line_type &lt, double fill)
150 if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
151 if (get_last_filled() == 0)
152 set_fill(fill);
153 simple_polygon(1, v, n);
155 if (lt.type == line_type::solid && driver_extension_flag) {
156 line_thickness(lt.thickness);
157 simple_polygon(0, v, n);
159 else if (lt.type != line_type::invisible) {
160 line_thickness(lt.thickness);
161 line(v[n - 1], v, n, lt);
165 void simple_output::circle(const position &cent, double rad,
166 const line_type &lt, double fill)
168 if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
169 if (get_last_filled() == 0)
170 set_fill(fill);
171 simple_circle(1, cent, rad);
173 line_thickness(lt.thickness);
174 switch (lt.type) {
175 case line_type::invisible:
176 break;
177 case line_type::dashed:
178 dashed_circle(cent, rad, lt);
179 break;
180 case line_type::dotted:
181 dotted_circle(cent, rad, lt);
182 break;
183 case line_type::solid:
184 simple_circle(0, cent, rad);
185 break;
186 default:
187 assert(0);
191 void simple_output::ellipse(const position &cent, const distance &dim,
192 const line_type &lt, double fill)
194 if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
195 if (get_last_filled() == 0)
196 set_fill(fill);
197 simple_ellipse(1, cent, dim);
199 if (lt.type != line_type::invisible)
200 line_thickness(lt.thickness);
201 switch (lt.type) {
202 case line_type::invisible:
203 break;
204 case line_type::dotted:
205 dotted_ellipse(cent, dim, lt);
206 break;
207 case line_type::dashed:
208 dashed_ellipse(cent, dim, lt);
209 break;
210 case line_type::solid:
211 simple_ellipse(0, cent, dim);
212 break;
213 default:
214 assert(0);
218 class troff_output
219 : public simple_output
221 const char *last_filename;
222 position upper_left;
223 double height;
224 double scale;
225 double last_line_thickness;
226 double last_fill;
227 char *last_filled; // color
228 char *last_outlined; // color
230 public:
231 troff_output();
232 ~troff_output();
233 void start_picture(double, const position &ll, const position &ur);
234 void finish_picture();
235 void text(const position &, text_piece *, int, double);
236 void dot(const position &, const line_type &);
237 void command(const char *, const char *, int);
238 void set_location(const char *, int);
239 void simple_line(const position &, const position &);
240 void simple_spline(const position &, const position *, int n);
241 void simple_arc(const position &, const position &, const position &);
242 void simple_circle(int, const position &, double rad);
243 void simple_ellipse(int, const position &, const distance &);
244 void simple_polygon(int, const position *, int);
245 void line_thickness(double p);
246 void set_fill(double);
247 void set_color(char *, char *);
248 void reset_color();
249 char *get_last_filled();
250 char *get_outline_color();
251 position transform(const position &);
254 output *make_troff_output()
256 return new troff_output;
259 troff_output::troff_output()
260 : last_filename(0), last_line_thickness(BAD_THICKNESS),
261 last_fill(-1.0), last_filled(0), last_outlined(0)
265 troff_output::~troff_output()
269 inline position troff_output::transform(const position &pos)
271 return position((pos.x - upper_left.x)/scale,
272 (upper_left.y - pos.y)/scale);
275 void troff_output::start_picture(double sc,
276 const position &ll, const position &ur)
278 upper_left.x = ll.x;
279 upper_left.y = ur.y;
280 scale = compute_scale(sc, ll, ur);
281 height = (ur.y - ll.y)/scale;
282 double width = (ur.x - ll.x)/scale;
283 printf(".PS %.3fi %.3fi", height, width);
284 if (args)
285 printf(" %s\n", args);
286 else
287 putchar('\n');
288 printf(".\\\" %g %g %g %g\n", ll.x, ll.y, ur.x, ur.y);
289 printf(".\\\" %.3fi %.3fi %.3fi %.3fi\n", 0.0, height, width, 0.0);
290 printf(".nr " FILL_REG " \\n(.u\n.nf\n");
291 printf(".nr " EQN_NO_EXTRA_SPACE_REG " 1\n");
292 // This guarantees that if the picture is used in a diversion it will
293 // have the right width.
294 printf("\\h'%.3fi'\n.sp -1\n", width);
297 void troff_output::finish_picture()
299 line_thickness(BAD_THICKNESS);
300 last_fill = -1.0; // force it to be reset for each picture
301 reset_color();
302 if (!flyback_flag)
303 printf(".sp %.3fi+1\n", height);
304 printf(".if \\n(" FILL_REG " .fi\n");
305 printf(".br\n");
306 printf(".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
307 // this is a little gross
308 set_location(current_filename, current_lineno);
309 fputs(flyback_flag ? ".PF\n" : ".PE\n", stdout);
312 void troff_output::command(const char *s,
313 const char *filename, int lineno)
315 if (filename != 0)
316 set_location(filename, lineno);
317 fputs(s, stdout);
318 putchar('\n');
321 void troff_output::simple_circle(int filled, const position &cent, double rad)
323 position c = transform(cent);
324 printf("\\h'%.3fi'"
325 "\\v'%.3fi'"
326 "\\D'%c %.3fi'"
327 "\n.sp -1\n",
328 c.x - rad/scale,
329 c.y,
330 (filled ? 'C' : 'c'),
331 rad*2.0/scale);
334 void troff_output::simple_ellipse(int filled, const position &cent,
335 const distance &dim)
337 position c = transform(cent);
338 printf("\\h'%.3fi'"
339 "\\v'%.3fi'"
340 "\\D'%c %.3fi %.3fi'"
341 "\n.sp -1\n",
342 c.x - dim.x/(2.0*scale),
343 c.y,
344 (filled ? 'E' : 'e'),
345 dim.x/scale, dim.y/scale);
348 void troff_output::simple_arc(const position &start, const distance &cent,
349 const distance &end)
351 position s = transform(start);
352 position c = transform(cent);
353 distance cv = c - s;
354 distance ev = transform(end) - c;
355 printf("\\h'%.3fi'"
356 "\\v'%.3fi'"
357 "\\D'a %.3fi %.3fi %.3fi %.3fi'"
358 "\n.sp -1\n",
359 s.x, s.y, cv.x, cv.y, ev.x, ev.y);
362 void troff_output::simple_line(const position &start, const position &end)
364 position s = transform(start);
365 distance ev = transform(end) - s;
366 printf("\\h'%.3fi'"
367 "\\v'%.3fi'"
368 "\\D'l %.3fi %.3fi'"
369 "\n.sp -1\n",
370 s.x, s.y, ev.x, ev.y);
373 void troff_output::simple_spline(const position &start,
374 const position *v, int n)
376 position pos = transform(start);
377 printf("\\h'%.3fi'"
378 "\\v'%.3fi'",
379 pos.x, pos.y);
380 fputs("\\D'~ ", stdout);
381 for (int i = 0; i < n; i++) {
382 position temp = transform(v[i]);
383 distance d = temp - pos;
384 pos = temp;
385 if (i != 0)
386 putchar(' ');
387 printf("%.3fi %.3fi", d.x, d.y);
389 printf("'\n.sp -1\n");
392 // a solid polygon
394 void troff_output::simple_polygon(int filled, const position *v, int n)
396 position pos = transform(v[0]);
397 printf("\\h'%.3fi'"
398 "\\v'%.3fi'",
399 pos.x, pos.y);
400 printf("\\D'%c ", (filled ? 'P' : 'p'));
401 for (int i = 1; i < n; i++) {
402 position temp = transform(v[i]);
403 distance d = temp - pos;
404 pos = temp;
405 if (i != 1)
406 putchar(' ');
407 printf("%.3fi %.3fi", d.x, d.y);
409 printf("'\n.sp -1\n");
412 const double TEXT_AXIS = 0.22; // in ems
414 static const char *choose_delimiter(const char *text)
416 if (strchr(text, '\'') == 0)
417 return "'";
418 else
419 return "\\(ts";
422 void troff_output::text(const position &center, text_piece *v, int n,
423 double ang)
425 line_thickness(BAD_THICKNESS); // the text might use lines (eg in equations)
426 int rotate_flag = 0;
427 if (driver_extension_flag && ang != 0.0) {
428 rotate_flag = 1;
429 position c = transform(center);
430 printf(".if \\n(" LDPS_REG " \\{\\\n"
431 "\\h'%.3fi'"
432 "\\v'%.3fi'"
433 "\\X'ps: exec gsave currentpoint 2 copy translate %.4f rotate neg exch neg exch translate'"
434 "\n.sp -1\n"
435 ".\\}\n",
436 c.x, c.y, -ang*180.0/M_PI);
438 for (int i = 0; i < n; i++)
439 if (v[i].text != 0 && *v[i].text != '\0') {
440 position c = transform(center);
441 if (v[i].filename != 0)
442 set_location(v[i].filename, v[i].lineno);
443 printf("\\h'%.3fi", c.x);
444 const char *delim = choose_delimiter(v[i].text);
445 if (v[i].adj.h == RIGHT_ADJUST)
446 printf("-\\w%s%s%su", delim, v[i].text, delim);
447 else if (v[i].adj.h != LEFT_ADJUST)
448 printf("-(\\w%s%s%su/2u)", delim, v[i].text, delim);
449 putchar('\'');
450 printf("\\v'%.3fi-(%dv/2u)+%dv+%.2fm",
451 c.y,
452 n - 1,
454 TEXT_AXIS);
455 if (v[i].adj.v == ABOVE_ADJUST)
456 printf("-.5v");
457 else if (v[i].adj.v == BELOW_ADJUST)
458 printf("+.5v");
459 putchar('\'');
460 fputs(v[i].text, stdout);
461 fputs("\n.sp -1\n", stdout);
463 if (rotate_flag)
464 printf(".if '\\*(.T'ps' \\{\\\n"
465 "\\X'ps: exec grestore'\n.sp -1\n"
466 ".\\}\n");
469 void troff_output::line_thickness(double p)
471 if (p < 0.0)
472 p = RELATIVE_THICKNESS;
473 if (driver_extension_flag && p != last_line_thickness) {
474 printf("\\D't %.3fp'\\h'%.3fp'\n.sp -1\n", p, -p);
475 last_line_thickness = p;
479 void troff_output::set_fill(double f)
481 if (driver_extension_flag && f != last_fill) {
482 // \D'Fg ...' emits a node only in compatibility mode,
483 // thus we add a dummy node
484 printf("\\&\\D'Fg %.3f'\n.sp -1\n", 1.0 - f);
485 last_fill = f;
487 if (last_filled) {
488 free(last_filled);
489 last_filled = 0;
490 printf(".fcolor\n");
494 void troff_output::set_color(char *color_fill, char *color_outlined)
496 if (driver_extension_flag) {
497 if (last_filled || last_outlined) {
498 reset_color();
500 // .gcolor and .fcolor emit a node in compatibility mode only,
501 // but that won't work anyway
502 if (color_fill) {
503 printf(".fcolor %s\n", color_fill);
504 last_filled = strsave(color_fill);
506 if (color_outlined) {
507 printf(".gcolor %s\n", color_outlined);
508 last_outlined = strsave(color_outlined);
513 void troff_output::reset_color()
515 if (driver_extension_flag) {
516 if (last_filled) {
517 printf(".fcolor\n");
518 a_delete last_filled;
519 last_filled = 0;
521 if (last_outlined) {
522 printf(".gcolor\n");
523 a_delete last_outlined;
524 last_outlined = 0;
529 char *troff_output::get_last_filled()
531 return last_filled;
534 char *troff_output::get_outline_color()
536 return last_outlined;
539 const double DOT_AXIS = .044;
541 void troff_output::dot(const position &cent, const line_type &lt)
543 if (driver_extension_flag) {
544 line_thickness(lt.thickness);
545 simple_line(cent, cent);
547 else {
548 position c = transform(cent);
549 printf("\\h'%.3fi-(\\w'.'u/2u)'"
550 "\\v'%.3fi+%.2fm'"
551 ".\n.sp -1\n",
552 c.x,
553 c.y,
554 DOT_AXIS);
558 void troff_output::set_location(const char *s, int n)
560 if (last_filename != 0 && strcmp(s, last_filename) == 0)
561 printf(".lf %d\n", n);
562 else {
563 printf(".lf %d %s\n", n, s);
564 last_filename = s;
568 // s-it2-mode