/contrib/groff/src/preproc/pic/troff.cpp

https://bitbucket.org/freebsd/freebsd-head/ · C++ · 567 lines · 495 code · 41 blank · 31 comment · 95 complexity · f3b5691b0845e44d89c26f2cf454750a MD5 · raw file

  1. // -*- C++ -*-
  2. /* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2002, 2003, 2005
  3. Free Software Foundation, Inc.
  4. Written by James Clark (jjc@jclark.com)
  5. This file is part of groff.
  6. groff is free software; you can redistribute it and/or modify it under
  7. the terms of the GNU General Public License as published by the Free
  8. Software Foundation; either version 2, or (at your option) any later
  9. version.
  10. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12. FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  13. for more details.
  14. You should have received a copy of the GNU General Public License along
  15. with groff; see the file COPYING. If not, write to the Free Software
  16. Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
  17. #include "pic.h"
  18. #include "common.h"
  19. const double RELATIVE_THICKNESS = -1.0;
  20. const double BAD_THICKNESS = -2.0;
  21. class simple_output : public common_output {
  22. virtual void simple_line(const position &, const position &) = 0;
  23. virtual void simple_spline(const position &, const position *, int n) = 0;
  24. virtual void simple_arc(const position &, const position &,
  25. const position &) = 0;
  26. virtual void simple_circle(int, const position &, double rad) = 0;
  27. virtual void simple_ellipse(int, const position &, const distance &) = 0;
  28. virtual void simple_polygon(int, const position *, int) = 0;
  29. virtual void line_thickness(double) = 0;
  30. virtual void set_fill(double) = 0;
  31. virtual void set_color(char *, char *) = 0;
  32. virtual void reset_color() = 0;
  33. virtual char *get_last_filled() = 0;
  34. void dot(const position &, const line_type &) = 0;
  35. public:
  36. void start_picture(double sc, const position &ll, const position &ur) = 0;
  37. void finish_picture() = 0;
  38. void text(const position &, text_piece *, int, double) = 0;
  39. void line(const position &, const position *, int n,
  40. const line_type &);
  41. void polygon(const position *, int n,
  42. const line_type &, double);
  43. void spline(const position &, const position *, int n,
  44. const line_type &);
  45. void arc(const position &, const position &, const position &,
  46. const line_type &);
  47. void circle(const position &, double rad, const line_type &, double);
  48. void ellipse(const position &, const distance &, const line_type &, double);
  49. int supports_filled_polygons();
  50. };
  51. int simple_output::supports_filled_polygons()
  52. {
  53. return driver_extension_flag != 0;
  54. }
  55. void simple_output::arc(const position &start, const position &cent,
  56. const position &end, const line_type &lt)
  57. {
  58. switch (lt.type) {
  59. case line_type::solid:
  60. line_thickness(lt.thickness);
  61. simple_arc(start, cent, end);
  62. break;
  63. case line_type::invisible:
  64. break;
  65. case line_type::dashed:
  66. dashed_arc(start, cent, end, lt);
  67. break;
  68. case line_type::dotted:
  69. dotted_arc(start, cent, end, lt);
  70. break;
  71. }
  72. }
  73. void simple_output::line(const position &start, const position *v, int n,
  74. const line_type &lt)
  75. {
  76. position pos = start;
  77. line_thickness(lt.thickness);
  78. for (int i = 0; i < n; i++) {
  79. switch (lt.type) {
  80. case line_type::solid:
  81. simple_line(pos, v[i]);
  82. break;
  83. case line_type::dotted:
  84. {
  85. distance vec(v[i] - pos);
  86. double dist = hypot(vec);
  87. int ndots = int(dist/lt.dash_width + .5);
  88. if (ndots == 0)
  89. dot(pos, lt);
  90. else {
  91. vec /= double(ndots);
  92. for (int j = 0; j <= ndots; j++)
  93. dot(pos + vec*j, lt);
  94. }
  95. }
  96. break;
  97. case line_type::dashed:
  98. {
  99. distance vec(v[i] - pos);
  100. double dist = hypot(vec);
  101. if (dist <= lt.dash_width*2.0)
  102. simple_line(pos, v[i]);
  103. else {
  104. int ndashes = int((dist - lt.dash_width)/(lt.dash_width*2.0) + .5);
  105. distance dash_vec = vec*(lt.dash_width/dist);
  106. double dash_gap = (dist - lt.dash_width)/ndashes;
  107. distance dash_gap_vec = vec*(dash_gap/dist);
  108. for (int j = 0; j <= ndashes; j++) {
  109. position s(pos + dash_gap_vec*j);
  110. simple_line(s, s + dash_vec);
  111. }
  112. }
  113. }
  114. break;
  115. case line_type::invisible:
  116. break;
  117. default:
  118. assert(0);
  119. }
  120. pos = v[i];
  121. }
  122. }
  123. void simple_output::spline(const position &start, const position *v, int n,
  124. const line_type &lt)
  125. {
  126. line_thickness(lt.thickness);
  127. simple_spline(start, v, n);
  128. }
  129. void simple_output::polygon(const position *v, int n,
  130. const line_type &lt, double fill)
  131. {
  132. if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
  133. if (get_last_filled() == 0)
  134. set_fill(fill);
  135. simple_polygon(1, v, n);
  136. }
  137. if (lt.type == line_type::solid && driver_extension_flag) {
  138. line_thickness(lt.thickness);
  139. simple_polygon(0, v, n);
  140. }
  141. else if (lt.type != line_type::invisible) {
  142. line_thickness(lt.thickness);
  143. line(v[n - 1], v, n, lt);
  144. }
  145. }
  146. void simple_output::circle(const position &cent, double rad,
  147. const line_type &lt, double fill)
  148. {
  149. if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
  150. if (get_last_filled() == 0)
  151. set_fill(fill);
  152. simple_circle(1, cent, rad);
  153. }
  154. line_thickness(lt.thickness);
  155. switch (lt.type) {
  156. case line_type::invisible:
  157. break;
  158. case line_type::dashed:
  159. dashed_circle(cent, rad, lt);
  160. break;
  161. case line_type::dotted:
  162. dotted_circle(cent, rad, lt);
  163. break;
  164. case line_type::solid:
  165. simple_circle(0, cent, rad);
  166. break;
  167. default:
  168. assert(0);
  169. }
  170. }
  171. void simple_output::ellipse(const position &cent, const distance &dim,
  172. const line_type &lt, double fill)
  173. {
  174. if (driver_extension_flag && ((fill >= 0.0) || (get_last_filled() != 0))) {
  175. if (get_last_filled() == 0)
  176. set_fill(fill);
  177. simple_ellipse(1, cent, dim);
  178. }
  179. if (lt.type != line_type::invisible)
  180. line_thickness(lt.thickness);
  181. switch (lt.type) {
  182. case line_type::invisible:
  183. break;
  184. case line_type::dotted:
  185. dotted_ellipse(cent, dim, lt);
  186. break;
  187. case line_type::dashed:
  188. dashed_ellipse(cent, dim, lt);
  189. break;
  190. case line_type::solid:
  191. simple_ellipse(0, cent, dim);
  192. break;
  193. default:
  194. assert(0);
  195. }
  196. }
  197. class troff_output : public simple_output {
  198. const char *last_filename;
  199. position upper_left;
  200. double height;
  201. double scale;
  202. double last_line_thickness;
  203. double last_fill;
  204. char *last_filled; // color
  205. char *last_outlined; // color
  206. public:
  207. troff_output();
  208. ~troff_output();
  209. void start_picture(double, const position &ll, const position &ur);
  210. void finish_picture();
  211. void text(const position &, text_piece *, int, double);
  212. void dot(const position &, const line_type &);
  213. void command(const char *, const char *, int);
  214. void set_location(const char *, int);
  215. void simple_line(const position &, const position &);
  216. void simple_spline(const position &, const position *, int n);
  217. void simple_arc(const position &, const position &, const position &);
  218. void simple_circle(int, const position &, double rad);
  219. void simple_ellipse(int, const position &, const distance &);
  220. void simple_polygon(int, const position *, int);
  221. void line_thickness(double p);
  222. void set_fill(double);
  223. void set_color(char *, char *);
  224. void reset_color();
  225. char *get_last_filled();
  226. char *get_outline_color();
  227. position transform(const position &);
  228. };
  229. output *make_troff_output()
  230. {
  231. return new troff_output;
  232. }
  233. troff_output::troff_output()
  234. : last_filename(0), last_line_thickness(BAD_THICKNESS),
  235. last_fill(-1.0), last_filled(0), last_outlined(0)
  236. {
  237. }
  238. troff_output::~troff_output()
  239. {
  240. }
  241. inline position troff_output::transform(const position &pos)
  242. {
  243. return position((pos.x - upper_left.x)/scale,
  244. (upper_left.y - pos.y)/scale);
  245. }
  246. #define FILL_REG "00"
  247. // If this register > 0, then pic will generate \X'ps: ...' commands
  248. // if the aligned attribute is used.
  249. #define GROPS_REG "0p"
  250. // If this register is defined, geqn won't produce `\x's.
  251. #define EQN_NO_EXTRA_SPACE_REG "0x"
  252. void troff_output::start_picture(double sc,
  253. const position &ll, const position &ur)
  254. {
  255. upper_left.x = ll.x;
  256. upper_left.y = ur.y;
  257. scale = compute_scale(sc, ll, ur);
  258. height = (ur.y - ll.y)/scale;
  259. double width = (ur.x - ll.x)/scale;
  260. printf(".PS %.3fi %.3fi", height, width);
  261. if (args)
  262. printf(" %s\n", args);
  263. else
  264. putchar('\n');
  265. printf(".\\\" %g %g %g %g\n", ll.x, ll.y, ur.x, ur.y);
  266. printf(".\\\" %.3fi %.3fi %.3fi %.3fi\n", 0.0, height, width, 0.0);
  267. printf(".nr " FILL_REG " \\n(.u\n.nf\n");
  268. printf(".nr " EQN_NO_EXTRA_SPACE_REG " 1\n");
  269. // This guarantees that if the picture is used in a diversion it will
  270. // have the right width.
  271. printf("\\h'%.3fi'\n.sp -1\n", width);
  272. }
  273. void troff_output::finish_picture()
  274. {
  275. line_thickness(BAD_THICKNESS);
  276. last_fill = -1.0; // force it to be reset for each picture
  277. reset_color();
  278. if (!flyback_flag)
  279. printf(".sp %.3fi+1\n", height);
  280. printf(".if \\n(" FILL_REG " .fi\n");
  281. printf(".br\n");
  282. printf(".nr " EQN_NO_EXTRA_SPACE_REG " 0\n");
  283. // this is a little gross
  284. set_location(current_filename, current_lineno);
  285. fputs(flyback_flag ? ".PF\n" : ".PE\n", stdout);
  286. }
  287. void troff_output::command(const char *s,
  288. const char *filename, int lineno)
  289. {
  290. if (filename != 0)
  291. set_location(filename, lineno);
  292. fputs(s, stdout);
  293. putchar('\n');
  294. }
  295. void troff_output::simple_circle(int filled, const position &cent, double rad)
  296. {
  297. position c = transform(cent);
  298. printf("\\h'%.3fi'"
  299. "\\v'%.3fi'"
  300. "\\D'%c %.3fi'"
  301. "\n.sp -1\n",
  302. c.x - rad/scale,
  303. c.y,
  304. (filled ? 'C' : 'c'),
  305. rad*2.0/scale);
  306. }
  307. void troff_output::simple_ellipse(int filled, const position &cent,
  308. const distance &dim)
  309. {
  310. position c = transform(cent);
  311. printf("\\h'%.3fi'"
  312. "\\v'%.3fi'"
  313. "\\D'%c %.3fi %.3fi'"
  314. "\n.sp -1\n",
  315. c.x - dim.x/(2.0*scale),
  316. c.y,
  317. (filled ? 'E' : 'e'),
  318. dim.x/scale, dim.y/scale);
  319. }
  320. void troff_output::simple_arc(const position &start, const distance &cent,
  321. const distance &end)
  322. {
  323. position s = transform(start);
  324. position c = transform(cent);
  325. distance cv = c - s;
  326. distance ev = transform(end) - c;
  327. printf("\\h'%.3fi'"
  328. "\\v'%.3fi'"
  329. "\\D'a %.3fi %.3fi %.3fi %.3fi'"
  330. "\n.sp -1\n",
  331. s.x, s.y, cv.x, cv.y, ev.x, ev.y);
  332. }
  333. void troff_output::simple_line(const position &start, const position &end)
  334. {
  335. position s = transform(start);
  336. distance ev = transform(end) - s;
  337. printf("\\h'%.3fi'"
  338. "\\v'%.3fi'"
  339. "\\D'l %.3fi %.3fi'"
  340. "\n.sp -1\n",
  341. s.x, s.y, ev.x, ev.y);
  342. }
  343. void troff_output::simple_spline(const position &start,
  344. const position *v, int n)
  345. {
  346. position pos = transform(start);
  347. printf("\\h'%.3fi'"
  348. "\\v'%.3fi'",
  349. pos.x, pos.y);
  350. fputs("\\D'~ ", stdout);
  351. for (int i = 0; i < n; i++) {
  352. position temp = transform(v[i]);
  353. distance d = temp - pos;
  354. pos = temp;
  355. if (i != 0)
  356. putchar(' ');
  357. printf("%.3fi %.3fi", d.x, d.y);
  358. }
  359. printf("'\n.sp -1\n");
  360. }
  361. // a solid polygon
  362. void troff_output::simple_polygon(int filled, const position *v, int n)
  363. {
  364. position pos = transform(v[0]);
  365. printf("\\h'%.3fi'"
  366. "\\v'%.3fi'",
  367. pos.x, pos.y);
  368. printf("\\D'%c ", (filled ? 'P' : 'p'));
  369. for (int i = 1; i < n; i++) {
  370. position temp = transform(v[i]);
  371. distance d = temp - pos;
  372. pos = temp;
  373. if (i != 1)
  374. putchar(' ');
  375. printf("%.3fi %.3fi", d.x, d.y);
  376. }
  377. printf("'\n.sp -1\n");
  378. }
  379. const double TEXT_AXIS = 0.22; // in ems
  380. static const char *choose_delimiter(const char *text)
  381. {
  382. if (strchr(text, '\'') == 0)
  383. return "'";
  384. else
  385. return "\\(ts";
  386. }
  387. void troff_output::text(const position &center, text_piece *v, int n,
  388. double ang)
  389. {
  390. line_thickness(BAD_THICKNESS); // the text might use lines (eg in equations)
  391. int rotate_flag = 0;
  392. if (driver_extension_flag && ang != 0.0) {
  393. rotate_flag = 1;
  394. position c = transform(center);
  395. printf(".if \\n(" GROPS_REG " \\{\\\n"
  396. "\\h'%.3fi'"
  397. "\\v'%.3fi'"
  398. "\\X'ps: exec gsave currentpoint 2 copy translate %.4f rotate neg exch neg exch translate'"
  399. "\n.sp -1\n"
  400. ".\\}\n",
  401. c.x, c.y, -ang*180.0/M_PI);
  402. }
  403. for (int i = 0; i < n; i++)
  404. if (v[i].text != 0 && *v[i].text != '\0') {
  405. position c = transform(center);
  406. if (v[i].filename != 0)
  407. set_location(v[i].filename, v[i].lineno);
  408. printf("\\h'%.3fi", c.x);
  409. const char *delim = choose_delimiter(v[i].text);
  410. if (v[i].adj.h == RIGHT_ADJUST)
  411. printf("-\\w%s%s%su", delim, v[i].text, delim);
  412. else if (v[i].adj.h != LEFT_ADJUST)
  413. printf("-(\\w%s%s%su/2u)", delim, v[i].text, delim);
  414. putchar('\'');
  415. printf("\\v'%.3fi-(%dv/2u)+%dv+%.2fm",
  416. c.y,
  417. n - 1,
  418. i,
  419. TEXT_AXIS);
  420. if (v[i].adj.v == ABOVE_ADJUST)
  421. printf("-.5v");
  422. else if (v[i].adj.v == BELOW_ADJUST)
  423. printf("+.5v");
  424. putchar('\'');
  425. fputs(v[i].text, stdout);
  426. fputs("\n.sp -1\n", stdout);
  427. }
  428. if (rotate_flag)
  429. printf(".if '\\*(.T'ps' \\{\\\n"
  430. "\\X'ps: exec grestore'\n.sp -1\n"
  431. ".\\}\n");
  432. }
  433. void troff_output::line_thickness(double p)
  434. {
  435. if (p < 0.0)
  436. p = RELATIVE_THICKNESS;
  437. if (driver_extension_flag && p != last_line_thickness) {
  438. printf("\\D't %.3fp'\\h'%.3fp'\n.sp -1\n", p, -p);
  439. last_line_thickness = p;
  440. }
  441. }
  442. void troff_output::set_fill(double f)
  443. {
  444. if (driver_extension_flag && f != last_fill) {
  445. // \D'Fg ...' emits a node only in compatibility mode,
  446. // thus we add a dummy node
  447. printf("\\&\\D'Fg %.3f'\n.sp -1\n", 1.0 - f);
  448. last_fill = f;
  449. }
  450. if (last_filled) {
  451. free(last_filled);
  452. last_filled = 0;
  453. printf(".fcolor\n");
  454. }
  455. }
  456. void troff_output::set_color(char *color_fill, char *color_outlined)
  457. {
  458. if (driver_extension_flag) {
  459. if (last_filled || last_outlined) {
  460. reset_color();
  461. }
  462. // .gcolor and .fcolor emit a node in compatibility mode only,
  463. // but that won't work anyway
  464. if (color_fill) {
  465. printf(".fcolor %s\n", color_fill);
  466. last_filled = strsave(color_fill);
  467. }
  468. if (color_outlined) {
  469. printf(".gcolor %s\n", color_outlined);
  470. last_outlined = strsave(color_outlined);
  471. }
  472. }
  473. }
  474. void troff_output::reset_color()
  475. {
  476. if (driver_extension_flag) {
  477. if (last_filled) {
  478. printf(".fcolor\n");
  479. a_delete last_filled;
  480. last_filled = 0;
  481. }
  482. if (last_outlined) {
  483. printf(".gcolor\n");
  484. a_delete last_outlined;
  485. last_outlined = 0;
  486. }
  487. }
  488. }
  489. char *troff_output::get_last_filled()
  490. {
  491. return last_filled;
  492. }
  493. char *troff_output::get_outline_color()
  494. {
  495. return last_outlined;
  496. }
  497. const double DOT_AXIS = .044;
  498. void troff_output::dot(const position &cent, const line_type &lt)
  499. {
  500. if (driver_extension_flag) {
  501. line_thickness(lt.thickness);
  502. simple_line(cent, cent);
  503. }
  504. else {
  505. position c = transform(cent);
  506. printf("\\h'%.3fi-(\\w'.'u/2u)'"
  507. "\\v'%.3fi+%.2fm'"
  508. ".\n.sp -1\n",
  509. c.x,
  510. c.y,
  511. DOT_AXIS);
  512. }
  513. }
  514. void troff_output::set_location(const char *s, int n)
  515. {
  516. if (last_filename != 0 && strcmp(s, last_filename) == 0)
  517. printf(".lf %d\n", n);
  518. else {
  519. printf(".lf %d %s\n", n, s);
  520. last_filename = s;
  521. }
  522. }