/contrib/groff/src/devices/grolj4/lj4.cpp

https://bitbucket.org/freebsd/freebsd-head/ · C++ · 708 lines · 623 code · 40 blank · 45 comment · 154 complexity · 48f5239cd3a6b132d74ef98db2dc8d33 MD5 · raw file

  1. // -*- C++ -*-
  2. /* Copyright (C) 1994, 2000, 2001, 2002, 2003, 2004
  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. /*
  18. TODO
  19. option to use beziers for circle/ellipse/arc
  20. option to use lines for spline (for LJ3)
  21. left/top offset registration
  22. output bin selection option
  23. paper source option
  24. output non-integer parameters using fixed point numbers
  25. X command to insert contents of file
  26. X command to specify inline escape sequence (how to specify unprintable chars?)
  27. X command to include bitmap graphics
  28. */
  29. #include "driver.h"
  30. #include "nonposix.h"
  31. extern "C" const char *Version_string;
  32. static struct {
  33. const char *name;
  34. int code;
  35. // at 300dpi
  36. int x_offset_portrait;
  37. int x_offset_landscape;
  38. } paper_table[] = {
  39. { "letter", 2, 75, 60 },
  40. { "legal", 3, 75, 60 },
  41. { "executive", 1, 75, 60 },
  42. { "a4", 26, 71, 59 },
  43. { "com10", 81, 75, 60 },
  44. { "monarch", 80, 75, 60 },
  45. { "c5", 91, 71, 59 },
  46. { "b5", 100, 71, 59 },
  47. { "dl", 90, 71, 59 },
  48. };
  49. static int user_paper_size = -1;
  50. static int landscape_flag = 0;
  51. static int duplex_flag = 0;
  52. // An upper limit on the paper size in centipoints,
  53. // used for setting HPGL picture frame.
  54. #define MAX_PAPER_WIDTH (12*720)
  55. #define MAX_PAPER_HEIGHT (17*720)
  56. // Dotted lines that are thinner than this don't work right.
  57. #define MIN_DOT_PEN_WIDTH .351
  58. #ifndef DEFAULT_LINE_WIDTH_FACTOR
  59. // in ems/1000
  60. #define DEFAULT_LINE_WIDTH_FACTOR 40
  61. #endif
  62. const int DEFAULT_HPGL_UNITS = 1016;
  63. int line_width_factor = DEFAULT_LINE_WIDTH_FACTOR;
  64. unsigned ncopies = 0; // 0 means don't send ncopies command
  65. static int lookup_paper_size(const char *);
  66. class lj4_font : public font {
  67. public:
  68. ~lj4_font();
  69. void handle_unknown_font_command(const char *command, const char *arg,
  70. const char *filename, int lineno);
  71. static lj4_font *load_lj4_font(const char *);
  72. int weight;
  73. int style;
  74. int proportional;
  75. int typeface;
  76. private:
  77. lj4_font(const char *);
  78. };
  79. lj4_font::lj4_font(const char *nm)
  80. : font(nm), weight(0), style(0), proportional(0), typeface(0)
  81. {
  82. }
  83. lj4_font::~lj4_font()
  84. {
  85. }
  86. lj4_font *lj4_font::load_lj4_font(const char *s)
  87. {
  88. lj4_font *f = new lj4_font(s);
  89. if (!f->load()) {
  90. delete f;
  91. return 0;
  92. }
  93. return f;
  94. }
  95. static struct {
  96. const char *s;
  97. int lj4_font::*ptr;
  98. int min;
  99. int max;
  100. } command_table[] = {
  101. { "pclweight", &lj4_font::weight, -7, 7 },
  102. { "pclstyle", &lj4_font::style, 0, 32767 },
  103. { "pclproportional", &lj4_font::proportional, 0, 1 },
  104. { "pcltypeface", &lj4_font::typeface, 0, 65535 },
  105. };
  106. void lj4_font::handle_unknown_font_command(const char *command,
  107. const char *arg,
  108. const char *filename, int lineno)
  109. {
  110. for (unsigned int i = 0;
  111. i < sizeof(command_table)/sizeof(command_table[0]); i++) {
  112. if (strcmp(command, command_table[i].s) == 0) {
  113. if (arg == 0)
  114. fatal_with_file_and_line(filename, lineno,
  115. "`%1' command requires an argument",
  116. command);
  117. char *ptr;
  118. long n = strtol(arg, &ptr, 10);
  119. if (n == 0 && ptr == arg)
  120. fatal_with_file_and_line(filename, lineno,
  121. "`%1' command requires numeric argument",
  122. command);
  123. if (n < command_table[i].min) {
  124. error_with_file_and_line(filename, lineno,
  125. "argument for `%1' command must not be less than %2",
  126. command, command_table[i].min);
  127. n = command_table[i].min;
  128. }
  129. else if (n > command_table[i].max) {
  130. error_with_file_and_line(filename, lineno,
  131. "argument for `%1' command must not be greater than %2",
  132. command, command_table[i].max);
  133. n = command_table[i].max;
  134. }
  135. this->*command_table[i].ptr = int(n);
  136. break;
  137. }
  138. }
  139. }
  140. class lj4_printer : public printer {
  141. public:
  142. lj4_printer(int);
  143. ~lj4_printer();
  144. void set_char(int, font *, const environment *, int, const char *name);
  145. void draw(int code, int *p, int np, const environment *env);
  146. void begin_page(int);
  147. void end_page(int page_length);
  148. font *make_font(const char *);
  149. void end_of_line();
  150. private:
  151. void set_line_thickness(int size, int dot = 0);
  152. void hpgl_init();
  153. void hpgl_start();
  154. void hpgl_end();
  155. int moveto(int hpos, int vpos);
  156. int moveto1(int hpos, int vpos);
  157. int cur_hpos;
  158. int cur_vpos;
  159. lj4_font *cur_font;
  160. int cur_size;
  161. unsigned short cur_symbol_set;
  162. int x_offset;
  163. int line_thickness;
  164. double pen_width;
  165. double hpgl_scale;
  166. int hpgl_inited;
  167. int paper_size;
  168. };
  169. inline
  170. int lj4_printer::moveto(int hpos, int vpos)
  171. {
  172. if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0)
  173. return moveto1(hpos, vpos);
  174. else
  175. return 1;
  176. }
  177. inline
  178. void lj4_printer::hpgl_start()
  179. {
  180. fputs("\033%1B", stdout);
  181. }
  182. inline
  183. void lj4_printer::hpgl_end()
  184. {
  185. fputs(";\033%0A", stdout);
  186. }
  187. lj4_printer::lj4_printer(int ps)
  188. : cur_hpos(-1),
  189. cur_font(0),
  190. cur_size(0),
  191. cur_symbol_set(0),
  192. line_thickness(-1),
  193. pen_width(-1.0),
  194. hpgl_inited(0)
  195. {
  196. if (7200 % font::res != 0)
  197. fatal("invalid resolution %1: resolution must be a factor of 7200",
  198. font::res);
  199. fputs("\033E", stdout); // reset
  200. if (font::res != 300)
  201. printf("\033&u%dD", font::res); // unit of measure
  202. if (ncopies > 0)
  203. printf("\033&l%uX", ncopies);
  204. paper_size = 0; // default to letter
  205. if (font::papersize) {
  206. int n = lookup_paper_size(font::papersize);
  207. if (n < 0)
  208. error("unknown paper size `%1'", font::papersize);
  209. else
  210. paper_size = n;
  211. }
  212. if (ps >= 0)
  213. paper_size = ps;
  214. printf("\033&l%dA" // paper size
  215. "\033&l%dO" // orientation
  216. "\033&l0E", // no top margin
  217. paper_table[paper_size].code,
  218. landscape_flag != 0);
  219. if (landscape_flag)
  220. x_offset = paper_table[paper_size].x_offset_landscape;
  221. else
  222. x_offset = paper_table[paper_size].x_offset_portrait;
  223. x_offset = (x_offset * font::res) / 300;
  224. if (duplex_flag)
  225. printf("\033&l%dS", duplex_flag);
  226. }
  227. lj4_printer::~lj4_printer()
  228. {
  229. fputs("\033E", stdout);
  230. }
  231. void lj4_printer::begin_page(int)
  232. {
  233. }
  234. void lj4_printer::end_page(int)
  235. {
  236. putchar('\f');
  237. cur_hpos = -1;
  238. }
  239. void lj4_printer::end_of_line()
  240. {
  241. cur_hpos = -1; // force absolute motion
  242. }
  243. inline
  244. int is_unprintable(unsigned char c)
  245. {
  246. return c < 32 && (c == 0 || (7 <= c && c <= 15) || c == 27);
  247. }
  248. void lj4_printer::set_char(int idx, font *f, const environment *env,
  249. int w, const char *)
  250. {
  251. int code = f->get_code(idx);
  252. unsigned char ch = code & 0xff;
  253. unsigned short symbol_set = code >> 8;
  254. if (symbol_set != cur_symbol_set) {
  255. printf("\033(%d%c", symbol_set/32, (symbol_set & 31) + 64);
  256. cur_symbol_set = symbol_set;
  257. }
  258. if (f != cur_font) {
  259. lj4_font *psf = (lj4_font *)f;
  260. // FIXME only output those that are needed
  261. printf("\033(s%dp%ds%db%dT",
  262. psf->proportional,
  263. psf->style,
  264. psf->weight,
  265. psf->typeface);
  266. if (!psf->proportional || !cur_font || !cur_font->proportional)
  267. cur_size = 0;
  268. cur_font = psf;
  269. }
  270. if (env->size != cur_size) {
  271. if (cur_font->proportional) {
  272. static const char *quarters[] = { "", ".25", ".5", ".75" };
  273. printf("\033(s%d%sV", env->size/4, quarters[env->size & 3]);
  274. }
  275. else {
  276. double pitch = double(font::res)/w;
  277. // PCL uses the next largest pitch, so round it down.
  278. pitch = floor(pitch*100.0)/100.0;
  279. printf("\033(s%.2fH", pitch);
  280. }
  281. cur_size = env->size;
  282. }
  283. if (!moveto(env->hpos, env->vpos))
  284. return;
  285. if (is_unprintable(ch))
  286. fputs("\033&p1X", stdout);
  287. putchar(ch);
  288. cur_hpos += w;
  289. }
  290. int lj4_printer::moveto1(int hpos, int vpos)
  291. {
  292. if (hpos < x_offset || vpos < 0)
  293. return 0;
  294. fputs("\033*p", stdout);
  295. if (cur_hpos < 0)
  296. printf("%dx%dY", hpos - x_offset, vpos);
  297. else {
  298. if (cur_hpos != hpos)
  299. printf("%s%d%c", hpos > cur_hpos ? "+" : "",
  300. hpos - cur_hpos, vpos == cur_vpos ? 'X' : 'x');
  301. if (cur_vpos != vpos)
  302. printf("%s%dY", vpos > cur_vpos ? "+" : "", vpos - cur_vpos);
  303. }
  304. cur_hpos = hpos;
  305. cur_vpos = vpos;
  306. return 1;
  307. }
  308. void lj4_printer::draw(int code, int *p, int np, const environment *env)
  309. {
  310. switch (code) {
  311. case 'R':
  312. {
  313. if (np != 2) {
  314. error("2 arguments required for rule");
  315. break;
  316. }
  317. int hpos = env->hpos;
  318. int vpos = env->vpos;
  319. int hsize = p[0];
  320. int vsize = p[1];
  321. if (hsize < 0) {
  322. hpos += hsize;
  323. hsize = -hsize;
  324. }
  325. if (vsize < 0) {
  326. vpos += vsize;
  327. vsize = -vsize;
  328. }
  329. if (!moveto(hpos, vpos))
  330. return;
  331. printf("\033*c%da%db0P", hsize, vsize);
  332. break;
  333. }
  334. case 'l':
  335. if (np != 2) {
  336. error("2 arguments required for line");
  337. break;
  338. }
  339. hpgl_init();
  340. if (!moveto(env->hpos, env->vpos))
  341. return;
  342. hpgl_start();
  343. set_line_thickness(env->size, p[0] == 0 && p[1] == 0);
  344. printf("PD%d,%d", p[0], p[1]);
  345. hpgl_end();
  346. break;
  347. case 'p':
  348. case 'P':
  349. {
  350. if (np & 1) {
  351. error("even number of arguments required for polygon");
  352. break;
  353. }
  354. if (np == 0) {
  355. error("no arguments for polygon");
  356. break;
  357. }
  358. hpgl_init();
  359. if (!moveto(env->hpos, env->vpos))
  360. return;
  361. hpgl_start();
  362. if (code == 'p')
  363. set_line_thickness(env->size);
  364. printf("PMPD%d", p[0]);
  365. for (int i = 1; i < np; i++)
  366. printf(",%d", p[i]);
  367. printf("PM2%cP", code == 'p' ? 'E' : 'F');
  368. hpgl_end();
  369. break;
  370. }
  371. case '~':
  372. {
  373. if (np & 1) {
  374. error("even number of arguments required for spline");
  375. break;
  376. }
  377. if (np == 0) {
  378. error("no arguments for spline");
  379. break;
  380. }
  381. hpgl_init();
  382. if (!moveto(env->hpos, env->vpos))
  383. return;
  384. hpgl_start();
  385. set_line_thickness(env->size);
  386. printf("PD%d,%d", p[0]/2, p[1]/2);
  387. const int tnum = 2;
  388. const int tden = 3;
  389. if (np > 2) {
  390. fputs("BR", stdout);
  391. for (int i = 0; i < np - 2; i += 2) {
  392. if (i != 0)
  393. putchar(',');
  394. printf("%d,%d,%d,%d,%d,%d",
  395. (p[i]*tnum)/(2*tden),
  396. (p[i + 1]*tnum)/(2*tden),
  397. p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden),
  398. p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden),
  399. (p[i] - p[i]/2) + p[i + 2]/2,
  400. (p[i + 1] - p[i + 1]/2) + p[i + 3]/2);
  401. }
  402. }
  403. printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2);
  404. hpgl_end();
  405. break;
  406. }
  407. case 'c':
  408. case 'C':
  409. // troff adds an extra argument to C
  410. if (np != 1 && !(code == 'C' && np == 2)) {
  411. error("1 argument required for circle");
  412. break;
  413. }
  414. hpgl_init();
  415. if (!moveto(env->hpos + p[0]/2, env->vpos))
  416. return;
  417. hpgl_start();
  418. if (code == 'c') {
  419. set_line_thickness(env->size);
  420. printf("CI%d", p[0]/2);
  421. }
  422. else
  423. printf("WG%d,0,360", p[0]/2);
  424. hpgl_end();
  425. break;
  426. case 'e':
  427. case 'E':
  428. if (np != 2) {
  429. error("2 arguments required for ellipse");
  430. break;
  431. }
  432. hpgl_init();
  433. if (!moveto(env->hpos + p[0]/2, env->vpos))
  434. return;
  435. hpgl_start();
  436. printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale);
  437. if (code == 'e') {
  438. set_line_thickness(env->size);
  439. printf("CI%d", p[1]/2);
  440. }
  441. else
  442. printf("WG%d,0,360", p[1]/2);
  443. printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale);
  444. hpgl_end();
  445. break;
  446. case 'a':
  447. {
  448. if (np != 4) {
  449. error("4 arguments required for arc");
  450. break;
  451. }
  452. hpgl_init();
  453. if (!moveto(env->hpos, env->vpos))
  454. return;
  455. hpgl_start();
  456. set_line_thickness(env->size);
  457. double c[2];
  458. if (adjust_arc_center(p, c)) {
  459. double sweep = ((atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0])
  460. - atan2(-c[1], -c[0]))
  461. * 180.0/PI);
  462. if (sweep > 0.0)
  463. sweep -= 360.0;
  464. printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep);
  465. }
  466. else
  467. printf("PD%d,%d", p[0] + p[2], p[1] + p[3]);
  468. hpgl_end();
  469. }
  470. break;
  471. case 'f':
  472. if (np != 1 && np != 2) {
  473. error("1 argument required for fill");
  474. break;
  475. }
  476. hpgl_init();
  477. hpgl_start();
  478. if (p[0] >= 0 && p[0] <= 1000)
  479. printf("FT10,%d", p[0]/10);
  480. hpgl_end();
  481. break;
  482. case 'F':
  483. // not implemented yet
  484. break;
  485. case 't':
  486. {
  487. if (np == 0) {
  488. line_thickness = -1;
  489. }
  490. else {
  491. // troff gratuitously adds an extra 0
  492. if (np != 1 && np != 2) {
  493. error("0 or 1 argument required for thickness");
  494. break;
  495. }
  496. line_thickness = p[0];
  497. }
  498. break;
  499. }
  500. default:
  501. error("unrecognised drawing command `%1'", char(code));
  502. break;
  503. }
  504. }
  505. void lj4_printer::hpgl_init()
  506. {
  507. if (hpgl_inited)
  508. return;
  509. hpgl_inited = 1;
  510. hpgl_scale = double(DEFAULT_HPGL_UNITS)/font::res;
  511. printf("\033&f0S" // push position
  512. "\033*p0x0Y" // move to 0,0
  513. "\033*c%dx%dy0T" // establish picture frame
  514. "\033%%1B" // switch to HPGL
  515. "SP1SC0,%.4f,0,-%.4f,2IR0,100,0,100" // set up scaling
  516. "LA1,4,2,4" // round line ends and joins
  517. "PR" // relative plotting
  518. "TR0" // opaque
  519. ";\033%%1A" // back to PCL
  520. "\033&f1S", // pop position
  521. MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT,
  522. hpgl_scale, hpgl_scale);
  523. }
  524. void lj4_printer::set_line_thickness(int size, int dot)
  525. {
  526. double pw;
  527. if (line_thickness < 0)
  528. pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0);
  529. else
  530. pw = line_thickness*25.4/font::res;
  531. if (dot && pw < MIN_DOT_PEN_WIDTH)
  532. pw = MIN_DOT_PEN_WIDTH;
  533. if (pw != pen_width) {
  534. printf("PW%f", pw);
  535. pen_width = pw;
  536. }
  537. }
  538. font *lj4_printer::make_font(const char *nm)
  539. {
  540. return lj4_font::load_lj4_font(nm);
  541. }
  542. printer *make_printer()
  543. {
  544. return new lj4_printer(user_paper_size);
  545. }
  546. static
  547. int lookup_paper_size(const char *s)
  548. {
  549. for (unsigned int i = 0;
  550. i < sizeof(paper_table)/sizeof(paper_table[0]); i++) {
  551. // FIXME Perhaps allow unique prefix.
  552. if (strcasecmp(s, paper_table[i].name) == 0)
  553. return i;
  554. }
  555. return -1;
  556. }
  557. static void usage(FILE *stream);
  558. extern "C" int optopt, optind;
  559. int main(int argc, char **argv)
  560. {
  561. setlocale(LC_NUMERIC, "C");
  562. program_name = argv[0];
  563. static char stderr_buf[BUFSIZ];
  564. setbuf(stderr, stderr_buf);
  565. int c;
  566. static const struct option long_options[] = {
  567. { "help", no_argument, 0, CHAR_MAX + 1 },
  568. { "version", no_argument, 0, 'v' },
  569. { NULL, 0, 0, 0 }
  570. };
  571. while ((c = getopt_long(argc, argv, "c:d:F:I:lp:vw:", long_options, NULL))
  572. != EOF)
  573. switch(c) {
  574. case 'l':
  575. landscape_flag = 1;
  576. break;
  577. case 'I':
  578. // ignore include search path
  579. break;
  580. case ':':
  581. if (optopt == 'd') {
  582. fprintf(stderr, "duplex assumed to be long-side\n");
  583. duplex_flag = 1;
  584. } else
  585. fprintf(stderr, "option -%c requires an argument\n", optopt);
  586. fflush(stderr);
  587. break;
  588. case 'd':
  589. if (!isdigit(*optarg)) // this ugly hack prevents -d without
  590. optind--; // args from messing up the arg list
  591. duplex_flag = atoi(optarg);
  592. if (duplex_flag != 1 && duplex_flag != 2) {
  593. fprintf(stderr, "odd value for duplex; assumed to be long-side\n");
  594. duplex_flag = 1;
  595. }
  596. break;
  597. case 'p':
  598. {
  599. int n = lookup_paper_size(optarg);
  600. if (n < 0)
  601. error("unknown paper size `%1'", optarg);
  602. else
  603. user_paper_size = n;
  604. break;
  605. }
  606. case 'v':
  607. printf("GNU grolj4 (groff) version %s\n", Version_string);
  608. exit(0);
  609. break;
  610. case 'F':
  611. font::command_line_font_dir(optarg);
  612. break;
  613. case 'c':
  614. {
  615. char *ptr;
  616. long n = strtol(optarg, &ptr, 10);
  617. if (n == 0 && ptr == optarg)
  618. error("argument for -c must be a positive integer");
  619. else if (n <= 0 || n > 32767)
  620. error("out of range argument for -c");
  621. else
  622. ncopies = unsigned(n);
  623. break;
  624. }
  625. case 'w':
  626. {
  627. char *ptr;
  628. long n = strtol(optarg, &ptr, 10);
  629. if (n == 0 && ptr == optarg)
  630. error("argument for -w must be a non-negative integer");
  631. else if (n < 0 || n > INT_MAX)
  632. error("out of range argument for -w");
  633. else
  634. line_width_factor = int(n);
  635. break;
  636. }
  637. case CHAR_MAX + 1: // --help
  638. usage(stdout);
  639. exit(0);
  640. break;
  641. case '?':
  642. usage(stderr);
  643. exit(1);
  644. break;
  645. default:
  646. assert(0);
  647. }
  648. SET_BINARY(fileno(stdout));
  649. if (optind >= argc)
  650. do_file("-");
  651. else {
  652. for (int i = optind; i < argc; i++)
  653. do_file(argv[i]);
  654. }
  655. return 0;
  656. }
  657. static void usage(FILE *stream)
  658. {
  659. fprintf(stream,
  660. "usage: %s [-lv] [-d [n]] [-c n] [-p paper_size]\n"
  661. " [-w n] [-F dir] [files ...]\n",
  662. program_name);
  663. }