2 /* Copyright (C) 1989-2018 Free Software Foundation, Inc.
3 Written by James Clark (jjc@jclark.com)
5 This file is part of groff.
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 3 of the License, or
10 (at your option) any later version.
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
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>. */
24 const double RELATIVE_THICKNESS = -1.0;
25 const double BAD_THICKNESS = -2.0;
27 class simple_output : public common_output {
28 virtual void simple_line(const position &, const position &) = 0;
29 virtual void simple_spline(const position &, const position *, int n) = 0;
30 virtual void simple_arc(const position &, const position &,
31 const position &) = 0;
32 virtual void simple_circle(int, const position &, double rad) = 0;
33 virtual void simple_ellipse(int, const position &, const distance &) = 0;
34 virtual void simple_polygon(int, const position *, int) = 0;
35 virtual void line_thickness(double) = 0;
36 virtual void set_fill(double) = 0;
37 virtual void set_color(char *, char *) = 0;
38 virtual void reset_color() = 0;
39 virtual char *get_last_filled() = 0;
40 void dot(const position &, const line_type &) = 0;
42 void start_picture(double sc, const position &ll, const position &ur) = 0;
43 void finish_picture() = 0;
44 void text(const position &, text_piece *, int, double) = 0;
45 void line(const position &, const position *, int n,
47 void polygon(const position *, int n,
48 const line_type &, double);
49 void spline(const position &, const position *, int n,
51 void arc(const position &, const position &, const position &,
53 void circle(const position &, double rad, const line_type &, double);
54 void ellipse(const position &, const distance &, const line_type &, double);
55 int supports_filled_polygons();
58 int simple_output::supports_filled_polygons()
60 return driver_extension_flag != 0;
63 void simple_output::arc(const position &start, const position ¢,
64 const position &end, const line_type <)
67 case line_type::solid:
68 line_thickness(lt.thickness);
69 simple_arc(start, cent, end);
71 case line_type::invisible:
73 case line_type::dashed:
74 dashed_arc(start, cent, end, lt);
76 case line_type::dotted:
77 dotted_arc(start, cent, end, lt);
82 void simple_output::line(const position &start, const position *v, int n,
86 line_thickness(lt.thickness);
87 for (int i = 0; i < n; i++) {
89 case line_type::solid:
90 simple_line(pos, v[i]);
92 case line_type::dotted:
94 distance vec(v[i] - pos);
95 double dist = hypot(vec);
96 int ndots = int(dist/lt.dash_width + .5);
100 vec /= double(ndots);
101 for (int j = 0; j <= ndots; j++)
102 dot(pos + vec*j, lt);
106 case line_type::dashed:
108 distance vec(v[i] - pos);
109 double dist = hypot(vec);
110 if (dist <= lt.dash_width*2.0)
111 simple_line(pos, v[i]);
113 int ndashes = int((dist - lt.dash_width)/(lt.dash_width*2.0) + .5);
114 distance dash_vec = vec*(lt.dash_width/dist);
115 double dash_gap = (dist - lt.dash_width)/ndashes;
116 distance dash_gap_vec = vec*(dash_gap/dist);
117 for (int j = 0; j <= ndashes; j++) {
118 position s(pos + dash_gap_vec*j);
119 simple_line(s, s + dash_vec);
124 case line_type::invisible:
133 void simple_output::spline(const position &start, const position *v, int n,
136 line_thickness(lt.thickness);
137 simple_spline(start, v, n);
140 void simple_output::polygon(const position *v, int n,
141 const line_type <, double fill)
143 if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
144 if (get_last_filled() == 0)
146 simple_polygon(1, v, n);
148 if (lt.type == line_type::solid && driver_extension_flag) {
149 line_thickness(lt.thickness);
150 simple_polygon(0, v, n);
152 else if (lt.type != line_type::invisible) {
153 line_thickness(lt.thickness);
154 line(v[n - 1], v, n, lt);
158 void simple_output::circle(const position ¢, double rad,
159 const line_type <, double fill)
161 if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
162 if (get_last_filled() == 0)
164 simple_circle(1, cent, rad);
166 line_thickness(lt.thickness);
168 case line_type::invisible:
170 case line_type::dashed:
171 dashed_circle(cent, rad, lt);
173 case line_type::dotted:
174 dotted_circle(cent, rad, lt);
176 case line_type::solid:
177 simple_circle(0, cent, rad);
184 void simple_output::ellipse(const position ¢, const distance &dim,
185 const line_type <, double fill)
187 if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
188 if (get_last_filled() == 0)
190 simple_ellipse(1, cent, dim);
192 if (lt.type != line_type::invisible)
193 line_thickness(lt.thickness);
195 case line_type::invisible:
197 case line_type::dotted:
198 dotted_ellipse(cent, dim, lt);
200 case line_type::dashed:
201 dashed_ellipse(cent, dim, lt);
203 case line_type::solid:
204 simple_ellipse(0, cent, dim);
211 class troff_output : public simple_output {
212 const char *last_filename;
216 double last_line_thickness;
218 char *last_filled; // color
219 char *last_outlined; // color
223 void start_picture(double, const position &ll, const position &ur);
224 void finish_picture();
225 void text(const position &, text_piece *, int, double);
226 void dot(const position &, const line_type &);
227 void command(const char *, const char *, int);
228 void set_location(const char *, int);
229 void simple_line(const position &, const position &);
230 void simple_spline(const position &, const position *, int n);
231 void simple_arc(const position &, const position &, const position &);
232 void simple_circle(int, const position &, double rad);
233 void simple_ellipse(int, const position &, const distance &);
234 void simple_polygon(int, const position *, int);
235 void line_thickness(double p);
236 void set_fill(double);
237 void set_color(char *, char *);
239 char *get_last_filled();
240 char *get_outline_color();
241 position transform(const position &);
244 output *make_troff_output()
246 return new troff_output;
249 troff_output::troff_output()
250 : last_filename(0), last_line_thickness(BAD_THICKNESS),
251 last_fill(-1.0), last_filled(0), last_outlined(0)
255 troff_output::~troff_output()
259 inline position troff_output::transform(const position &pos)
261 return position((pos.x - upper_left.x)/scale,
262 (upper_left.y - pos.y)/scale);
265 #define FILL_REG "00"
267 // If this register > 0, then pic will generate \X'ps: ...' commands
268 // if the aligned attribute is used.
269 #define GROPS_REG "0p"
271 // If this register is defined, geqn won't produce '\x's.
272 #define EQN_NO_EXTRA_SPACE_REG "0x"
274 void troff_output::start_picture(double sc,
275 const position &ll, const position &ur)
279 scale = compute_scale(sc, ll, ur);
280 height = (ur.y - ll.y)/scale;
281 double width = (ur.x - ll.x)/scale;
282 printf(".PS %.3fi %.3fi", height, width);
284 printf(" %s\n", args);
287 printf(".\\\" %g %g %g %g\n", ll.x, ll.y, ur.x, ur.y);
288 printf(".\\\" %.3fi %.3fi %.3fi %.3fi\n", 0.0, height, width, 0.0);
289 printf(".nr " FILL_REG " \\n(.u\n.nf\n");
290 printf(".nr " EQN_NO_EXTRA_SPACE_REG " 1\n");
291 // This guarantees that if the picture is used in a diversion it will
292 // have the right width.
293 printf("\\h'%.3fi'\n.sp -1\n", width);
296 void troff_output::finish_picture()
298 line_thickness(BAD_THICKNESS);
299 last_fill = -1.0; // force it to be reset for each picture
302 printf(".sp %.3fi+1\n", height);
303 printf(".if \\n(" FILL_REG " .fi\n");
305 printf(".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
306 // this is a little gross
307 set_location(current_filename, current_lineno);
308 fputs(flyback_flag ? ".PF\n" : ".PE\n", stdout);
311 void troff_output::command(const char *s,
312 const char *filename, int lineno)
315 set_location(filename, lineno);
320 void troff_output::simple_circle(int filled, const position ¢, double rad)
322 position c = transform(cent);
329 (filled ? 'C' : 'c'),
333 void troff_output::simple_ellipse(int filled, const position ¢,
336 position c = transform(cent);
339 "\\D'%c %.3fi %.3fi'"
341 c.x - dim.x/(2.0*scale),
343 (filled ? 'E' : 'e'),
344 dim.x/scale, dim.y/scale);
347 void troff_output::simple_arc(const position &start, const distance ¢,
350 position s = transform(start);
351 position c = transform(cent);
353 distance ev = transform(end) - c;
356 "\\D'a %.3fi %.3fi %.3fi %.3fi'"
358 s.x, s.y, cv.x, cv.y, ev.x, ev.y);
361 void troff_output::simple_line(const position &start, const position &end)
363 position s = transform(start);
364 distance ev = transform(end) - s;
369 s.x, s.y, ev.x, ev.y);
372 void troff_output::simple_spline(const position &start,
373 const position *v, int n)
375 position pos = transform(start);
379 fputs("\\D'~ ", stdout);
380 for (int i = 0; i < n; i++) {
381 position temp = transform(v[i]);
382 distance d = temp - pos;
386 printf("%.3fi %.3fi", d.x, d.y);
388 printf("'\n.sp -1\n");
393 void troff_output::simple_polygon(int filled, const position *v, int n)
395 position pos = transform(v[0]);
399 printf("\\D'%c ", (filled ? 'P' : 'p'));
400 for (int i = 1; i < n; i++) {
401 position temp = transform(v[i]);
402 distance d = temp - pos;
406 printf("%.3fi %.3fi", d.x, d.y);
408 printf("'\n.sp -1\n");
411 const double TEXT_AXIS = 0.22; // in ems
413 static const char *choose_delimiter(const char *text)
415 if (strchr(text, '\'') == 0)
421 void troff_output::text(const position ¢er, text_piece *v, int n,
424 line_thickness(BAD_THICKNESS); // text might use lines (e.g., in equations)
426 if (driver_extension_flag && ang != 0.0) {
428 position c = transform(center);
429 printf(".if \\n(" GROPS_REG " \\{\\\n"
432 "\\X'ps: exec gsave currentpoint 2 copy translate %.4f rotate neg exch neg exch translate'"
435 c.x, c.y, -ang*180.0/M_PI);
437 for (int i = 0; i < n; i++)
438 if (v[i].text != 0 && *v[i].text != '\0') {
439 position c = transform(center);
440 if (v[i].filename != 0)
441 set_location(v[i].filename, v[i].lineno);
442 printf("\\h'%.3fi", c.x);
443 const char *delim = choose_delimiter(v[i].text);
444 if (v[i].adj.h == RIGHT_ADJUST)
445 printf("-\\w%s%s%su", delim, v[i].text, delim);
446 else if (v[i].adj.h != LEFT_ADJUST)
447 printf("-(\\w%s%s%su/2u)", delim, v[i].text, delim);
449 printf("\\v'%.3fi-(%dv/2u)+%dv+%.2fm",
454 if (v[i].adj.v == ABOVE_ADJUST)
456 else if (v[i].adj.v == BELOW_ADJUST)
459 fputs(v[i].text, stdout);
460 fputs("\n.sp -1\n", stdout);
463 printf(".if \\n(" GROPS_REG " \\{\\\n"
464 "\\X'ps: exec grestore'\n.sp -1\n"
468 void troff_output::line_thickness(double p)
471 p = RELATIVE_THICKNESS;
472 if (driver_extension_flag && p != last_line_thickness) {
473 printf("\\D't %.3fp'\\h'%.3fp'\n.sp -1\n", p, -p);
474 last_line_thickness = p;
478 void troff_output::set_fill(double f)
480 if (driver_extension_flag && f != last_fill) {
481 // \D'Fg ...' emits a node only in compatibility mode,
482 // thus we add a dummy node
483 printf("\\&\\D'Fg %.3f'\n.sp -1\n", 1.0 - f);
493 void troff_output::set_color(char *color_fill, char *color_outlined)
495 if (driver_extension_flag) {
496 if (last_filled || last_outlined) {
499 // .gcolor and .fcolor emit a node in compatibility mode only,
500 // but that won't work anyway
502 printf(".fcolor %s\n", color_fill);
503 last_filled = strsave(color_fill);
505 if (color_outlined) {
506 printf(".gcolor %s\n", color_outlined);
507 last_outlined = strsave(color_outlined);
512 void troff_output::reset_color()
514 if (driver_extension_flag) {
528 char *troff_output::get_last_filled()
533 char *troff_output::get_outline_color()
535 return last_outlined;
538 const double DOT_AXIS = .044;
540 void troff_output::dot(const position ¢, const line_type <)
542 if (driver_extension_flag) {
543 line_thickness(lt.thickness);
544 simple_line(cent, cent);
547 position c = transform(cent);
548 printf("\\h'%.3fi-(\\w'.'u/2u)'"
557 void troff_output::set_location(const char *s, int n)
559 if (last_filename != 0 && strcmp(s, last_filename) == 0)
560 printf(".lf %d\n", n);
562 printf(".lf %d %s\n", n, s);