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 8This file is part of groff. 9 10groff is free software; you can redistribute it and/or modify it under 11the terms of the GNU General Public License as published by the Free 12Software Foundation; either version 2, or (at your option) any later 13version. 14 15groff is distributed in the hope that it will be useful, but WITHOUT ANY 16WARRANTY; without even the implied warranty of MERCHANTABILITY or 17FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 18for more details. 19 20You should have received a copy of the GNU General Public License along 21with groff; see the file COPYING. If not, write to the Free Software 22Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 23 24#include "pic.h" 25#include "common.h" 26 27 28const double RELATIVE_THICKNESS = -1.0; 29const double BAD_THICKNESS = -2.0; 30 31class 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; 45public: 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 62int simple_output::supports_filled_polygons() 63{ 64 return driver_extension_flag != 0; 65} 66 67void simple_output::arc(const position &start, const position ¢, 68 const position &end, const line_type <) 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 86void simple_output::line(const position &start, const position *v, int n, 87 const line_type <) 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 137void simple_output::spline(const position &start, const position *v, int n, 138 const line_type <) 139{ 140 line_thickness(lt.thickness); 141 simple_spline(start, v, n); 142} 143 144void simple_output::polygon(const position *v, int n, 145 const line_type <, 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 162void simple_output::circle(const position ¢, double rad, 163 const line_type <, 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 188void simple_output::ellipse(const position ¢, const distance &dim, 189 const line_type <, 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 215class 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 224public: 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 248output *make_troff_output() 249{ 250 return new troff_output; 251} 252 253troff_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 259troff_output::~troff_output() 260{ 261} 262 263inline 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 278void 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 300void 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 315void 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 324void troff_output::simple_circle(int filled, const position ¢, 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 337void troff_output::simple_ellipse(int filled, const position ¢, 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 351void troff_output::simple_arc(const position &start, const distance ¢, 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 365void 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 376void 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 397void 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 415const double TEXT_AXIS = 0.22; // in ems 416 417static const char *choose_delimiter(const char *text) 418{ 419 if (strchr(text, '\'') == 0) 420 return "'"; 421 else 422 return "\\(ts"; 423} 424 425void troff_output::text(const position ¢er, 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 472void 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 482void 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 497void 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 516void 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 532char *troff_output::get_last_filled() 533{ 534 return last_filled; 535} 536 537char *troff_output::get_outline_color() 538{ 539 return last_outlined; 540} 541 542const double DOT_AXIS = .044; 543 544void troff_output::dot(const position ¢, const line_type <) 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 561void 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