1114402Sru// -*- C++ -*- 2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2005 3114402Sru Free Software Foundation, Inc. 4114402Sru Written by James Clark (jjc@jclark.com) 5114402Sru 6114402SruThis file is part of groff. 7114402Sru 8114402Srugroff is free software; you can redistribute it and/or modify it under 9114402Sruthe terms of the GNU General Public License as published by the Free 10114402SruSoftware Foundation; either version 2, or (at your option) any later 11114402Sruversion. 12114402Sru 13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY 14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or 15114402SruFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16114402Srufor more details. 17114402Sru 18114402SruYou should have received a copy of the GNU General Public License along 19114402Sruwith groff; see the file COPYING. If not, write to the Free Software 20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21114402Sru 22114402Sru#include "pic.h" 23114402Sru#include "common.h" 24114402Sru 25114402Sru 26114402Sruconst double RELATIVE_THICKNESS = -1.0; 27114402Sruconst double BAD_THICKNESS = -2.0; 28114402Sru 29114402Sruclass simple_output : public common_output { 30114402Sru virtual void simple_line(const position &, const position &) = 0; 31114402Sru virtual void simple_spline(const position &, const position *, int n) = 0; 32114402Sru virtual void simple_arc(const position &, const position &, 33114402Sru const position &) = 0; 34114402Sru virtual void simple_circle(int, const position &, double rad) = 0; 35114402Sru virtual void simple_ellipse(int, const position &, const distance &) = 0; 36114402Sru virtual void simple_polygon(int, const position *, int) = 0; 37114402Sru virtual void line_thickness(double) = 0; 38114402Sru virtual void set_fill(double) = 0; 39114402Sru virtual void set_color(char *, char *) = 0; 40114402Sru virtual void reset_color() = 0; 41114402Sru virtual char *get_last_filled() = 0; 42114402Sru void dot(const position &, const line_type &) = 0; 43114402Srupublic: 44114402Sru void start_picture(double sc, const position &ll, const position &ur) = 0; 45114402Sru void finish_picture() = 0; 46114402Sru void text(const position &, text_piece *, int, double) = 0; 47114402Sru void line(const position &, const position *, int n, 48114402Sru const line_type &); 49114402Sru void polygon(const position *, int n, 50114402Sru const line_type &, double); 51114402Sru void spline(const position &, const position *, int n, 52114402Sru const line_type &); 53114402Sru void arc(const position &, const position &, const position &, 54114402Sru const line_type &); 55114402Sru void circle(const position &, double rad, const line_type &, double); 56114402Sru void ellipse(const position &, const distance &, const line_type &, double); 57114402Sru int supports_filled_polygons(); 58114402Sru}; 59114402Sru 60114402Sruint simple_output::supports_filled_polygons() 61114402Sru{ 62114402Sru return driver_extension_flag != 0; 63114402Sru} 64114402Sru 65114402Sruvoid simple_output::arc(const position &start, const position ¢, 66114402Sru const position &end, const line_type <) 67114402Sru{ 68114402Sru switch (lt.type) { 69114402Sru case line_type::solid: 70114402Sru line_thickness(lt.thickness); 71114402Sru simple_arc(start, cent, end); 72114402Sru break; 73114402Sru case line_type::invisible: 74114402Sru break; 75114402Sru case line_type::dashed: 76114402Sru dashed_arc(start, cent, end, lt); 77114402Sru break; 78114402Sru case line_type::dotted: 79114402Sru dotted_arc(start, cent, end, lt); 80114402Sru break; 81114402Sru } 82114402Sru} 83114402Sru 84114402Sruvoid simple_output::line(const position &start, const position *v, int n, 85114402Sru const line_type <) 86114402Sru{ 87114402Sru position pos = start; 88114402Sru line_thickness(lt.thickness); 89114402Sru for (int i = 0; i < n; i++) { 90114402Sru switch (lt.type) { 91114402Sru case line_type::solid: 92114402Sru simple_line(pos, v[i]); 93114402Sru break; 94114402Sru case line_type::dotted: 95114402Sru { 96114402Sru distance vec(v[i] - pos); 97114402Sru double dist = hypot(vec); 98114402Sru int ndots = int(dist/lt.dash_width + .5); 99114402Sru if (ndots == 0) 100114402Sru dot(pos, lt); 101114402Sru else { 102114402Sru vec /= double(ndots); 103114402Sru for (int j = 0; j <= ndots; j++) 104114402Sru dot(pos + vec*j, lt); 105114402Sru } 106114402Sru } 107114402Sru break; 108114402Sru case line_type::dashed: 109114402Sru { 110114402Sru distance vec(v[i] - pos); 111114402Sru double dist = hypot(vec); 112114402Sru if (dist <= lt.dash_width*2.0) 113114402Sru simple_line(pos, v[i]); 114114402Sru else { 115114402Sru int ndashes = int((dist - lt.dash_width)/(lt.dash_width*2.0) + .5); 116114402Sru distance dash_vec = vec*(lt.dash_width/dist); 117114402Sru double dash_gap = (dist - lt.dash_width)/ndashes; 118114402Sru distance dash_gap_vec = vec*(dash_gap/dist); 119114402Sru for (int j = 0; j <= ndashes; j++) { 120114402Sru position s(pos + dash_gap_vec*j); 121114402Sru simple_line(s, s + dash_vec); 122114402Sru } 123114402Sru } 124114402Sru } 125114402Sru break; 126114402Sru case line_type::invisible: 127114402Sru break; 128114402Sru default: 129114402Sru assert(0); 130114402Sru } 131114402Sru pos = v[i]; 132114402Sru } 133114402Sru} 134114402Sru 135114402Sruvoid simple_output::spline(const position &start, const position *v, int n, 136114402Sru const line_type <) 137114402Sru{ 138114402Sru line_thickness(lt.thickness); 139114402Sru simple_spline(start, v, n); 140114402Sru} 141114402Sru 142114402Sruvoid simple_output::polygon(const position *v, int n, 143114402Sru const line_type <, double fill) 144114402Sru{ 145114402Sru if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) { 146114402Sru if (get_last_filled() == 0) 147114402Sru set_fill(fill); 148114402Sru simple_polygon(1, v, n); 149114402Sru } 150114402Sru if (lt.type == line_type::solid && driver_extension_flag) { 151114402Sru line_thickness(lt.thickness); 152114402Sru simple_polygon(0, v, n); 153114402Sru } 154114402Sru else if (lt.type != line_type::invisible) { 155114402Sru line_thickness(lt.thickness); 156114402Sru line(v[n - 1], v, n, lt); 157114402Sru } 158114402Sru} 159114402Sru 160114402Sruvoid simple_output::circle(const position ¢, double rad, 161114402Sru const line_type <, double fill) 162114402Sru{ 163114402Sru if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) { 164114402Sru if (get_last_filled() == 0) 165114402Sru set_fill(fill); 166114402Sru simple_circle(1, cent, rad); 167114402Sru } 168114402Sru line_thickness(lt.thickness); 169114402Sru switch (lt.type) { 170114402Sru case line_type::invisible: 171114402Sru break; 172114402Sru case line_type::dashed: 173114402Sru dashed_circle(cent, rad, lt); 174114402Sru break; 175114402Sru case line_type::dotted: 176114402Sru dotted_circle(cent, rad, lt); 177114402Sru break; 178114402Sru case line_type::solid: 179114402Sru simple_circle(0, cent, rad); 180114402Sru break; 181114402Sru default: 182114402Sru assert(0); 183114402Sru } 184114402Sru} 185114402Sru 186114402Sruvoid simple_output::ellipse(const position ¢, const distance &dim, 187114402Sru const line_type <, double fill) 188114402Sru{ 189114402Sru if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) { 190114402Sru if (get_last_filled() == 0) 191114402Sru set_fill(fill); 192114402Sru simple_ellipse(1, cent, dim); 193114402Sru } 194114402Sru if (lt.type != line_type::invisible) 195114402Sru line_thickness(lt.thickness); 196114402Sru switch (lt.type) { 197114402Sru case line_type::invisible: 198114402Sru break; 199114402Sru case line_type::dotted: 200151497Sru dotted_ellipse(cent, dim, lt); 201151497Sru break; 202114402Sru case line_type::dashed: 203151497Sru dashed_ellipse(cent, dim, lt); 204151497Sru break; 205114402Sru case line_type::solid: 206114402Sru simple_ellipse(0, cent, dim); 207114402Sru break; 208114402Sru default: 209114402Sru assert(0); 210114402Sru } 211114402Sru} 212114402Sru 213114402Sruclass troff_output : public simple_output { 214114402Sru const char *last_filename; 215114402Sru position upper_left; 216114402Sru double height; 217114402Sru double scale; 218114402Sru double last_line_thickness; 219114402Sru double last_fill; 220114402Sru char *last_filled; // color 221114402Sru char *last_outlined; // color 222114402Srupublic: 223114402Sru troff_output(); 224114402Sru ~troff_output(); 225114402Sru void start_picture(double, const position &ll, const position &ur); 226114402Sru void finish_picture(); 227114402Sru void text(const position &, text_piece *, int, double); 228114402Sru void dot(const position &, const line_type &); 229114402Sru void command(const char *, const char *, int); 230114402Sru void set_location(const char *, int); 231114402Sru void simple_line(const position &, const position &); 232114402Sru void simple_spline(const position &, const position *, int n); 233114402Sru void simple_arc(const position &, const position &, const position &); 234114402Sru void simple_circle(int, const position &, double rad); 235114402Sru void simple_ellipse(int, const position &, const distance &); 236114402Sru void simple_polygon(int, const position *, int); 237114402Sru void line_thickness(double p); 238114402Sru void set_fill(double); 239114402Sru void set_color(char *, char *); 240114402Sru void reset_color(); 241114402Sru char *get_last_filled(); 242114402Sru char *get_outline_color(); 243114402Sru position transform(const position &); 244114402Sru}; 245114402Sru 246114402Sruoutput *make_troff_output() 247114402Sru{ 248114402Sru return new troff_output; 249114402Sru} 250114402Sru 251114402Srutroff_output::troff_output() 252114402Sru: last_filename(0), last_line_thickness(BAD_THICKNESS), 253114402Sru last_fill(-1.0), last_filled(0), last_outlined(0) 254114402Sru{ 255114402Sru} 256114402Sru 257114402Srutroff_output::~troff_output() 258114402Sru{ 259114402Sru} 260114402Sru 261114402Sruinline position troff_output::transform(const position &pos) 262114402Sru{ 263114402Sru return position((pos.x - upper_left.x)/scale, 264114402Sru (upper_left.y - pos.y)/scale); 265114402Sru} 266114402Sru 267114402Sru#define FILL_REG "00" 268114402Sru 269114402Sru// If this register > 0, then pic will generate \X'ps: ...' commands 270114402Sru// if the aligned attribute is used. 271114402Sru#define GROPS_REG "0p" 272114402Sru 273114402Sru// If this register is defined, geqn won't produce `\x's. 274114402Sru#define EQN_NO_EXTRA_SPACE_REG "0x" 275114402Sru 276114402Sruvoid troff_output::start_picture(double sc, 277114402Sru const position &ll, const position &ur) 278114402Sru{ 279114402Sru upper_left.x = ll.x; 280114402Sru upper_left.y = ur.y; 281114402Sru scale = compute_scale(sc, ll, ur); 282114402Sru height = (ur.y - ll.y)/scale; 283114402Sru double width = (ur.x - ll.x)/scale; 284114402Sru printf(".PS %.3fi %.3fi", height, width); 285114402Sru if (args) 286114402Sru printf(" %s\n", args); 287114402Sru else 288114402Sru putchar('\n'); 289114402Sru printf(".\\\" %g %g %g %g\n", ll.x, ll.y, ur.x, ur.y); 290114402Sru printf(".\\\" %.3fi %.3fi %.3fi %.3fi\n", 0.0, height, width, 0.0); 291114402Sru printf(".nr " FILL_REG " \\n(.u\n.nf\n"); 292114402Sru printf(".nr " EQN_NO_EXTRA_SPACE_REG " 1\n"); 293114402Sru // This guarantees that if the picture is used in a diversion it will 294114402Sru // have the right width. 295114402Sru printf("\\h'%.3fi'\n.sp -1\n", width); 296114402Sru} 297114402Sru 298114402Sruvoid troff_output::finish_picture() 299114402Sru{ 300114402Sru line_thickness(BAD_THICKNESS); 301114402Sru last_fill = -1.0; // force it to be reset for each picture 302114402Sru reset_color(); 303114402Sru if (!flyback_flag) 304114402Sru printf(".sp %.3fi+1\n", height); 305114402Sru printf(".if \\n(" FILL_REG " .fi\n"); 306114402Sru printf(".br\n"); 307114402Sru printf(".nr " EQN_NO_EXTRA_SPACE_REG " 0\n"); 308114402Sru // this is a little gross 309114402Sru set_location(current_filename, current_lineno); 310114402Sru fputs(flyback_flag ? ".PF\n" : ".PE\n", stdout); 311114402Sru} 312114402Sru 313114402Sruvoid troff_output::command(const char *s, 314114402Sru const char *filename, int lineno) 315114402Sru{ 316114402Sru if (filename != 0) 317114402Sru set_location(filename, lineno); 318114402Sru fputs(s, stdout); 319114402Sru putchar('\n'); 320114402Sru} 321114402Sru 322114402Sruvoid troff_output::simple_circle(int filled, const position ¢, double rad) 323114402Sru{ 324114402Sru position c = transform(cent); 325114402Sru printf("\\h'%.3fi'" 326114402Sru "\\v'%.3fi'" 327151497Sru "\\D'%c %.3fi'" 328114402Sru "\n.sp -1\n", 329114402Sru c.x - rad/scale, 330114402Sru c.y, 331114402Sru (filled ? 'C' : 'c'), 332114402Sru rad*2.0/scale); 333114402Sru} 334114402Sru 335114402Sruvoid troff_output::simple_ellipse(int filled, const position ¢, 336114402Sru const distance &dim) 337114402Sru{ 338114402Sru position c = transform(cent); 339114402Sru printf("\\h'%.3fi'" 340114402Sru "\\v'%.3fi'" 341151497Sru "\\D'%c %.3fi %.3fi'" 342114402Sru "\n.sp -1\n", 343114402Sru c.x - dim.x/(2.0*scale), 344114402Sru c.y, 345114402Sru (filled ? 'E' : 'e'), 346114402Sru dim.x/scale, dim.y/scale); 347114402Sru} 348114402Sru 349114402Sruvoid troff_output::simple_arc(const position &start, const distance ¢, 350114402Sru const distance &end) 351114402Sru{ 352114402Sru position s = transform(start); 353114402Sru position c = transform(cent); 354114402Sru distance cv = c - s; 355114402Sru distance ev = transform(end) - c; 356114402Sru printf("\\h'%.3fi'" 357114402Sru "\\v'%.3fi'" 358151497Sru "\\D'a %.3fi %.3fi %.3fi %.3fi'" 359114402Sru "\n.sp -1\n", 360114402Sru s.x, s.y, cv.x, cv.y, ev.x, ev.y); 361114402Sru} 362114402Sru 363114402Sruvoid troff_output::simple_line(const position &start, const position &end) 364114402Sru{ 365114402Sru position s = transform(start); 366114402Sru distance ev = transform(end) - s; 367114402Sru printf("\\h'%.3fi'" 368114402Sru "\\v'%.3fi'" 369151497Sru "\\D'l %.3fi %.3fi'" 370114402Sru "\n.sp -1\n", 371114402Sru s.x, s.y, ev.x, ev.y); 372114402Sru} 373114402Sru 374114402Sruvoid troff_output::simple_spline(const position &start, 375114402Sru const position *v, int n) 376114402Sru{ 377114402Sru position pos = transform(start); 378114402Sru printf("\\h'%.3fi'" 379114402Sru "\\v'%.3fi'", 380114402Sru pos.x, pos.y); 381151497Sru fputs("\\D'~ ", stdout); 382114402Sru for (int i = 0; i < n; i++) { 383114402Sru position temp = transform(v[i]); 384114402Sru distance d = temp - pos; 385114402Sru pos = temp; 386114402Sru if (i != 0) 387114402Sru putchar(' '); 388114402Sru printf("%.3fi %.3fi", d.x, d.y); 389114402Sru } 390114402Sru printf("'\n.sp -1\n"); 391114402Sru} 392114402Sru 393114402Sru// a solid polygon 394114402Sru 395114402Sruvoid troff_output::simple_polygon(int filled, const position *v, int n) 396114402Sru{ 397114402Sru position pos = transform(v[0]); 398114402Sru printf("\\h'%.3fi'" 399114402Sru "\\v'%.3fi'", 400114402Sru pos.x, pos.y); 401151497Sru printf("\\D'%c ", (filled ? 'P' : 'p')); 402114402Sru for (int i = 1; i < n; i++) { 403114402Sru position temp = transform(v[i]); 404114402Sru distance d = temp - pos; 405114402Sru pos = temp; 406114402Sru if (i != 1) 407114402Sru putchar(' '); 408114402Sru printf("%.3fi %.3fi", d.x, d.y); 409114402Sru } 410114402Sru printf("'\n.sp -1\n"); 411114402Sru} 412114402Sru 413114402Sruconst double TEXT_AXIS = 0.22; // in ems 414114402Sru 415114402Srustatic const char *choose_delimiter(const char *text) 416114402Sru{ 417114402Sru if (strchr(text, '\'') == 0) 418114402Sru return "'"; 419114402Sru else 420114402Sru return "\\(ts"; 421114402Sru} 422114402Sru 423114402Sruvoid troff_output::text(const position ¢er, text_piece *v, int n, 424114402Sru double ang) 425114402Sru{ 426114402Sru line_thickness(BAD_THICKNESS); // the text might use lines (eg in equations) 427114402Sru int rotate_flag = 0; 428114402Sru if (driver_extension_flag && ang != 0.0) { 429114402Sru rotate_flag = 1; 430114402Sru position c = transform(center); 431114402Sru printf(".if \\n(" GROPS_REG " \\{\\\n" 432114402Sru "\\h'%.3fi'" 433114402Sru "\\v'%.3fi'" 434114402Sru "\\X'ps: exec gsave currentpoint 2 copy translate %.4f rotate neg exch neg exch translate'" 435114402Sru "\n.sp -1\n" 436114402Sru ".\\}\n", 437114402Sru c.x, c.y, -ang*180.0/M_PI); 438114402Sru } 439114402Sru for (int i = 0; i < n; i++) 440114402Sru if (v[i].text != 0 && *v[i].text != '\0') { 441114402Sru position c = transform(center); 442114402Sru if (v[i].filename != 0) 443114402Sru set_location(v[i].filename, v[i].lineno); 444114402Sru printf("\\h'%.3fi", c.x); 445114402Sru const char *delim = choose_delimiter(v[i].text); 446114402Sru if (v[i].adj.h == RIGHT_ADJUST) 447114402Sru printf("-\\w%s%s%su", delim, v[i].text, delim); 448114402Sru else if (v[i].adj.h != LEFT_ADJUST) 449114402Sru printf("-(\\w%s%s%su/2u)", delim, v[i].text, delim); 450114402Sru putchar('\''); 451114402Sru printf("\\v'%.3fi-(%dv/2u)+%dv+%.2fm", 452114402Sru c.y, 453114402Sru n - 1, 454114402Sru i, 455114402Sru TEXT_AXIS); 456114402Sru if (v[i].adj.v == ABOVE_ADJUST) 457114402Sru printf("-.5v"); 458114402Sru else if (v[i].adj.v == BELOW_ADJUST) 459114402Sru printf("+.5v"); 460114402Sru putchar('\''); 461114402Sru fputs(v[i].text, stdout); 462114402Sru fputs("\n.sp -1\n", stdout); 463114402Sru } 464114402Sru if (rotate_flag) 465114402Sru printf(".if '\\*(.T'ps' \\{\\\n" 466114402Sru "\\X'ps: exec grestore'\n.sp -1\n" 467114402Sru ".\\}\n"); 468114402Sru} 469114402Sru 470114402Sruvoid troff_output::line_thickness(double p) 471114402Sru{ 472114402Sru if (p < 0.0) 473114402Sru p = RELATIVE_THICKNESS; 474114402Sru if (driver_extension_flag && p != last_line_thickness) { 475114402Sru printf("\\D't %.3fp'\\h'%.3fp'\n.sp -1\n", p, -p); 476114402Sru last_line_thickness = p; 477114402Sru } 478114402Sru} 479114402Sru 480114402Sruvoid troff_output::set_fill(double f) 481114402Sru{ 482114402Sru if (driver_extension_flag && f != last_fill) { 483151497Sru // \D'Fg ...' emits a node only in compatibility mode, 484151497Sru // thus we add a dummy node 485151497Sru printf("\\&\\D'Fg %.3f'\n.sp -1\n", 1.0 - f); 486114402Sru last_fill = f; 487114402Sru } 488114402Sru if (last_filled) { 489114402Sru free(last_filled); 490114402Sru last_filled = 0; 491151497Sru printf(".fcolor\n"); 492114402Sru } 493114402Sru} 494114402Sru 495114402Sruvoid troff_output::set_color(char *color_fill, char *color_outlined) 496114402Sru{ 497114402Sru if (driver_extension_flag) { 498114402Sru if (last_filled || last_outlined) { 499114402Sru reset_color(); 500114402Sru } 501151497Sru // .gcolor and .fcolor emit a node in compatibility mode only, 502151497Sru // but that won't work anyway 503114402Sru if (color_fill) { 504151497Sru printf(".fcolor %s\n", color_fill); 505114402Sru last_filled = strsave(color_fill); 506114402Sru } 507114402Sru if (color_outlined) { 508151497Sru printf(".gcolor %s\n", color_outlined); 509114402Sru last_outlined = strsave(color_outlined); 510114402Sru } 511114402Sru } 512114402Sru} 513114402Sru 514114402Sruvoid troff_output::reset_color() 515114402Sru{ 516114402Sru if (driver_extension_flag) { 517114402Sru if (last_filled) { 518151497Sru printf(".fcolor\n"); 519114402Sru a_delete last_filled; 520114402Sru last_filled = 0; 521114402Sru } 522114402Sru if (last_outlined) { 523151497Sru printf(".gcolor\n"); 524114402Sru a_delete last_outlined; 525114402Sru last_outlined = 0; 526114402Sru } 527114402Sru } 528114402Sru} 529114402Sru 530114402Sruchar *troff_output::get_last_filled() 531114402Sru{ 532114402Sru return last_filled; 533114402Sru} 534114402Sru 535114402Sruchar *troff_output::get_outline_color() 536114402Sru{ 537114402Sru return last_outlined; 538114402Sru} 539114402Sru 540114402Sruconst double DOT_AXIS = .044; 541114402Sru 542114402Sruvoid troff_output::dot(const position ¢, const line_type <) 543114402Sru{ 544114402Sru if (driver_extension_flag) { 545114402Sru line_thickness(lt.thickness); 546114402Sru simple_line(cent, cent); 547114402Sru } 548114402Sru else { 549114402Sru position c = transform(cent); 550114402Sru printf("\\h'%.3fi-(\\w'.'u/2u)'" 551114402Sru "\\v'%.3fi+%.2fm'" 552114402Sru ".\n.sp -1\n", 553114402Sru c.x, 554114402Sru c.y, 555114402Sru DOT_AXIS); 556114402Sru } 557114402Sru} 558114402Sru 559114402Sruvoid troff_output::set_location(const char *s, int n) 560114402Sru{ 561114402Sru if (last_filename != 0 && strcmp(s, last_filename) == 0) 562114402Sru printf(".lf %d\n", n); 563114402Sru else { 564114402Sru printf(".lf %d %s\n", n, s); 565114402Sru last_filename = s; 566114402Sru } 567114402Sru} 568