troff.cpp revision 114402
1230479Snetchild// -*- C++ -*- 2230479Snetchild/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003 3230479Snetchild Free Software Foundation, Inc. 4230479Snetchild Written by James Clark (jjc@jclark.com) 5230479Snetchild 6230479SnetchildThis file is part of groff. 7230479Snetchild 8230479Snetchildgroff is free software; you can redistribute it and/or modify it under 9230479Snetchildthe terms of the GNU General Public License as published by the Free 10230479SnetchildSoftware Foundation; either version 2, or (at your option) any later 11230479Snetchildversion. 12230479Snetchild 13230479Snetchildgroff is distributed in the hope that it will be useful, but WITHOUT ANY 14230479SnetchildWARRANTY; without even the implied warranty of MERCHANTABILITY or 15230479SnetchildFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16230479Snetchildfor more details. 17230479Snetchild 18230479SnetchildYou should have received a copy of the GNU General Public License along 19230479Snetchildwith groff; see the file COPYING. If not, write to the Free Software 20230479SnetchildFoundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ 21230479Snetchild 22#include "pic.h" 23#include "common.h" 24 25 26const double RELATIVE_THICKNESS = -1.0; 27const double BAD_THICKNESS = -2.0; 28 29class 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; 43public: 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(); 58}; 59 60int simple_output::supports_filled_polygons() 61{ 62 return driver_extension_flag != 0; 63} 64 65void simple_output::arc(const position &start, const position ¢, 66 const position &end, const line_type <) 67{ 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; 81 } 82} 83 84void simple_output::line(const position &start, const position *v, int n, 85 const line_type <) 86{ 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: 95 { 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); 105 } 106 } 107 break; 108 case line_type::dashed: 109 { 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); 122 } 123 } 124 } 125 break; 126 case line_type::invisible: 127 break; 128 default: 129 assert(0); 130 } 131 pos = v[i]; 132 } 133} 134 135void simple_output::spline(const position &start, const position *v, int n, 136 const line_type <) 137{ 138 line_thickness(lt.thickness); 139 simple_spline(start, v, n); 140} 141 142void simple_output::polygon(const position *v, int n, 143 const line_type <, double fill) 144{ 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); 149 } 150 if (lt.type == line_type::solid && driver_extension_flag) { 151 line_thickness(lt.thickness); 152 simple_polygon(0, v, n); 153 } 154 else if (lt.type != line_type::invisible) { 155 line_thickness(lt.thickness); 156 line(v[n - 1], v, n, lt); 157 } 158} 159 160void simple_output::circle(const position ¢, double rad, 161 const line_type <, double fill) 162{ 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); 167 } 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); 183 } 184} 185 186void simple_output::ellipse(const position ¢, const distance &dim, 187 const line_type <, double fill) 188{ 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); 193 } 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 case line_type::dashed: 201 case line_type::solid: 202 simple_ellipse(0, cent, dim); 203 break; 204 default: 205 assert(0); 206 } 207} 208 209class troff_output : public simple_output { 210 const char *last_filename; 211 position upper_left; 212 double height; 213 double scale; 214 double last_line_thickness; 215 double last_fill; 216 char *last_filled; // color 217 char *last_outlined; // color 218public: 219 troff_output(); 220 ~troff_output(); 221 void start_picture(double, const position &ll, const position &ur); 222 void finish_picture(); 223 void text(const position &, text_piece *, int, double); 224 void dot(const position &, const line_type &); 225 void command(const char *, const char *, int); 226 void set_location(const char *, int); 227 void simple_line(const position &, const position &); 228 void simple_spline(const position &, const position *, int n); 229 void simple_arc(const position &, const position &, const position &); 230 void simple_circle(int, const position &, double rad); 231 void simple_ellipse(int, const position &, const distance &); 232 void simple_polygon(int, const position *, int); 233 void line_thickness(double p); 234 void set_fill(double); 235 void set_color(char *, char *); 236 void reset_color(); 237 char *get_last_filled(); 238 char *get_outline_color(); 239 position transform(const position &); 240}; 241 242output *make_troff_output() 243{ 244 return new troff_output; 245} 246 247troff_output::troff_output() 248: last_filename(0), last_line_thickness(BAD_THICKNESS), 249 last_fill(-1.0), last_filled(0), last_outlined(0) 250{ 251} 252 253troff_output::~troff_output() 254{ 255} 256 257inline position troff_output::transform(const position &pos) 258{ 259 return position((pos.x - upper_left.x)/scale, 260 (upper_left.y - pos.y)/scale); 261} 262 263#define FILL_REG "00" 264 265// If this register > 0, then pic will generate \X'ps: ...' commands 266// if the aligned attribute is used. 267#define GROPS_REG "0p" 268 269// If this register is defined, geqn won't produce `\x's. 270#define EQN_NO_EXTRA_SPACE_REG "0x" 271 272void troff_output::start_picture(double sc, 273 const position &ll, const position &ur) 274{ 275 upper_left.x = ll.x; 276 upper_left.y = ur.y; 277 scale = compute_scale(sc, ll, ur); 278 height = (ur.y - ll.y)/scale; 279 double width = (ur.x - ll.x)/scale; 280 printf(".PS %.3fi %.3fi", height, width); 281 if (args) 282 printf(" %s\n", args); 283 else 284 putchar('\n'); 285 printf(".\\\" %g %g %g %g\n", ll.x, ll.y, ur.x, ur.y); 286 printf(".\\\" %.3fi %.3fi %.3fi %.3fi\n", 0.0, height, width, 0.0); 287 printf(".nr " FILL_REG " \\n(.u\n.nf\n"); 288 printf(".nr " EQN_NO_EXTRA_SPACE_REG " 1\n"); 289 // This guarantees that if the picture is used in a diversion it will 290 // have the right width. 291 printf("\\h'%.3fi'\n.sp -1\n", width); 292} 293 294void troff_output::finish_picture() 295{ 296 line_thickness(BAD_THICKNESS); 297 last_fill = -1.0; // force it to be reset for each picture 298 reset_color(); 299 if (!flyback_flag) 300 printf(".sp %.3fi+1\n", height); 301 printf(".if \\n(" FILL_REG " .fi\n"); 302 printf(".br\n"); 303 printf(".nr " EQN_NO_EXTRA_SPACE_REG " 0\n"); 304 // this is a little gross 305 set_location(current_filename, current_lineno); 306 fputs(flyback_flag ? ".PF\n" : ".PE\n", stdout); 307} 308 309void troff_output::command(const char *s, 310 const char *filename, int lineno) 311{ 312 if (filename != 0) 313 set_location(filename, lineno); 314 fputs(s, stdout); 315 putchar('\n'); 316} 317 318void troff_output::simple_circle(int filled, const position ¢, double rad) 319{ 320 position c = transform(cent); 321 printf("\\h'%.3fi'" 322 "\\v'%.3fi'" 323 "\\D'%c%.3fi'" 324 "\n.sp -1\n", 325 c.x - rad/scale, 326 c.y, 327 (filled ? 'C' : 'c'), 328 rad*2.0/scale); 329} 330 331void troff_output::simple_ellipse(int filled, const position ¢, 332 const distance &dim) 333{ 334 position c = transform(cent); 335 printf("\\h'%.3fi'" 336 "\\v'%.3fi'" 337 "\\D'%c%.3fi %.3fi'" 338 "\n.sp -1\n", 339 c.x - dim.x/(2.0*scale), 340 c.y, 341 (filled ? 'E' : 'e'), 342 dim.x/scale, dim.y/scale); 343} 344 345void troff_output::simple_arc(const position &start, const distance ¢, 346 const distance &end) 347{ 348 position s = transform(start); 349 position c = transform(cent); 350 distance cv = c - s; 351 distance ev = transform(end) - c; 352 printf("\\h'%.3fi'" 353 "\\v'%.3fi'" 354 "\\D'a%.3fi %.3fi %.3fi %.3fi'" 355 "\n.sp -1\n", 356 s.x, s.y, cv.x, cv.y, ev.x, ev.y); 357} 358 359void troff_output::simple_line(const position &start, const position &end) 360{ 361 position s = transform(start); 362 distance ev = transform(end) - s; 363 printf("\\h'%.3fi'" 364 "\\v'%.3fi'" 365 "\\D'l%.3fi %.3fi'" 366 "\n.sp -1\n", 367 s.x, s.y, ev.x, ev.y); 368} 369 370void troff_output::simple_spline(const position &start, 371 const position *v, int n) 372{ 373 position pos = transform(start); 374 printf("\\h'%.3fi'" 375 "\\v'%.3fi'", 376 pos.x, pos.y); 377 fputs("\\D'~", stdout); 378 for (int i = 0; i < n; i++) { 379 position temp = transform(v[i]); 380 distance d = temp - pos; 381 pos = temp; 382 if (i != 0) 383 putchar(' '); 384 printf("%.3fi %.3fi", d.x, d.y); 385 } 386 printf("'\n.sp -1\n"); 387} 388 389// a solid polygon 390 391void troff_output::simple_polygon(int filled, const position *v, int n) 392{ 393 position pos = transform(v[0]); 394 printf("\\h'%.3fi'" 395 "\\v'%.3fi'", 396 pos.x, pos.y); 397 printf("\\D'%c", (filled ? 'P' : 'p')); 398 for (int i = 1; i < n; i++) { 399 position temp = transform(v[i]); 400 distance d = temp - pos; 401 pos = temp; 402 if (i != 1) 403 putchar(' '); 404 printf("%.3fi %.3fi", d.x, d.y); 405 } 406 printf("'\n.sp -1\n"); 407} 408 409const double TEXT_AXIS = 0.22; // in ems 410 411static const char *choose_delimiter(const char *text) 412{ 413 if (strchr(text, '\'') == 0) 414 return "'"; 415 else 416 return "\\(ts"; 417} 418 419void troff_output::text(const position ¢er, text_piece *v, int n, 420 double ang) 421{ 422 line_thickness(BAD_THICKNESS); // the text might use lines (eg in equations) 423 int rotate_flag = 0; 424 if (driver_extension_flag && ang != 0.0) { 425 rotate_flag = 1; 426 position c = transform(center); 427 printf(".if \\n(" GROPS_REG " \\{\\\n" 428 "\\h'%.3fi'" 429 "\\v'%.3fi'" 430 "\\X'ps: exec gsave currentpoint 2 copy translate %.4f rotate neg exch neg exch translate'" 431 "\n.sp -1\n" 432 ".\\}\n", 433 c.x, c.y, -ang*180.0/M_PI); 434 } 435 for (int i = 0; i < n; i++) 436 if (v[i].text != 0 && *v[i].text != '\0') { 437 position c = transform(center); 438 if (v[i].filename != 0) 439 set_location(v[i].filename, v[i].lineno); 440 printf("\\h'%.3fi", c.x); 441 const char *delim = choose_delimiter(v[i].text); 442 if (v[i].adj.h == RIGHT_ADJUST) 443 printf("-\\w%s%s%su", delim, v[i].text, delim); 444 else if (v[i].adj.h != LEFT_ADJUST) 445 printf("-(\\w%s%s%su/2u)", delim, v[i].text, delim); 446 putchar('\''); 447 printf("\\v'%.3fi-(%dv/2u)+%dv+%.2fm", 448 c.y, 449 n - 1, 450 i, 451 TEXT_AXIS); 452 if (v[i].adj.v == ABOVE_ADJUST) 453 printf("-.5v"); 454 else if (v[i].adj.v == BELOW_ADJUST) 455 printf("+.5v"); 456 putchar('\''); 457 fputs(v[i].text, stdout); 458 fputs("\n.sp -1\n", stdout); 459 } 460 if (rotate_flag) 461 printf(".if '\\*(.T'ps' \\{\\\n" 462 "\\X'ps: exec grestore'\n.sp -1\n" 463 ".\\}\n"); 464} 465 466void troff_output::line_thickness(double p) 467{ 468 if (p < 0.0) 469 p = RELATIVE_THICKNESS; 470 if (driver_extension_flag && p != last_line_thickness) { 471 printf("\\D't %.3fp'\\h'%.3fp'\n.sp -1\n", p, -p); 472 last_line_thickness = p; 473 } 474} 475 476void troff_output::set_fill(double f) 477{ 478 if (driver_extension_flag && f != last_fill) { 479 printf("\\D'Fg %.3f'\n.sp -1\n", 1.0 - f); 480 last_fill = f; 481 } 482 if (last_filled) { 483 free(last_filled); 484 last_filled = 0; 485 printf("\\M[]\n.sp -1\n"); 486 } 487} 488 489void troff_output::set_color(char *color_fill, char *color_outlined) 490{ 491 if (driver_extension_flag) { 492 if (last_filled || last_outlined) { 493 reset_color(); 494 } 495 if (color_fill) { 496 printf("\\M[%s]\n.sp -1\n", color_fill); 497 last_filled = strsave(color_fill); 498 } 499 if (color_outlined) { 500 printf("\\m[%s]\n.sp -1\n", color_outlined); 501 last_outlined = strsave(color_outlined); 502 } 503 } 504} 505 506void troff_output::reset_color() 507{ 508 if (driver_extension_flag) { 509 if (last_filled) { 510 printf("\\M[]\n.sp -1\n"); 511 a_delete last_filled; 512 last_filled = 0; 513 } 514 if (last_outlined) { 515 printf("\\m[]\n.sp -1\n"); 516 a_delete last_outlined; 517 last_outlined = 0; 518 } 519 } 520} 521 522char *troff_output::get_last_filled() 523{ 524 return last_filled; 525} 526 527char *troff_output::get_outline_color() 528{ 529 return last_outlined; 530} 531 532const double DOT_AXIS = .044; 533 534void troff_output::dot(const position ¢, const line_type <) 535{ 536 if (driver_extension_flag) { 537 line_thickness(lt.thickness); 538 simple_line(cent, cent); 539 } 540 else { 541 position c = transform(cent); 542 printf("\\h'%.3fi-(\\w'.'u/2u)'" 543 "\\v'%.3fi+%.2fm'" 544 ".\n.sp -1\n", 545 c.x, 546 c.y, 547 DOT_AXIS); 548 } 549} 550 551void troff_output::set_location(const char *s, int n) 552{ 553 if (last_filename != 0 && strcmp(s, last_filename) == 0) 554 printf(".lf %d\n", n); 555 else { 556 printf(".lf %d %s\n", n, s); 557 last_filename = s; 558 } 559} 560