PageRenderTime 71ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/contrib/groff/src/devices/grohtml/post-html.cpp

https://bitbucket.org/freebsd/freebsd-head/
C++ | 5053 lines | 3641 code | 623 blank | 789 comment | 1039 complexity | d2253a29ccdb5e77327c9607fe5b9b93 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, BSD-3-Clause, LGPL-2.0, LGPL-2.1, BSD-2-Clause, 0BSD, JSON, AGPL-1.0, GPL-2.0
  1. // -*- C++ -*-
  2. /* Copyright (C) 2000, 2001, 2002, 2003, 2004, 2005
  3. * Free Software Foundation, Inc.
  4. *
  5. * Gaius Mulley (gaius@glam.ac.uk) wrote post-html.cpp
  6. * but it owes a huge amount of ideas and raw code from
  7. * James Clark (jjc@jclark.com) grops/ps.cpp.
  8. */
  9. /*
  10. This file is part of groff.
  11. groff is free software; you can redistribute it and/or modify it under
  12. the terms of the GNU General Public License as published by the Free
  13. Software Foundation; either version 2, or (at your option) any later
  14. version.
  15. groff is distributed in the hope that it will be useful, but WITHOUT ANY
  16. WARRANTY; without even the implied warranty of MERCHANTABILITY or
  17. FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
  18. for more details.
  19. You should have received a copy of the GNU General Public License along
  20. with groff; see the file COPYING. If not, write to the Free Software
  21. Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
  22. #include "driver.h"
  23. #include "stringclass.h"
  24. #include "cset.h"
  25. #include "html.h"
  26. #include "html-text.h"
  27. #include "html-table.h"
  28. #include <time.h>
  29. #ifdef HAVE_UNISTD_H
  30. #include <unistd.h>
  31. #endif
  32. #include <stdio.h>
  33. #include <fcntl.h>
  34. #include <string.h>
  35. extern "C" const char *Version_string;
  36. #if !defined(TRUE)
  37. # define TRUE (1==1)
  38. #endif
  39. #if !defined(FALSE)
  40. # define FALSE (1==0)
  41. #endif
  42. #define MAX_LINE_LENGTH 60 /* maximum characters we want in a line */
  43. #define SIZE_INCREMENT 2 /* font size increment <big> = +2 */
  44. #define CENTER_TOLERANCE 2 /* how many pixels off center do we allow */
  45. #define ANCHOR_TEMPLATE "heading" /* if simple anchor is set we use this */
  46. #define UNICODE_DESC_START 0x80 /* all character entities above this are */
  47. /* either encoded by their glyph names or if */
  48. /* there is no name then we use &#nnn; */
  49. typedef enum {CENTERED, LEFT, RIGHT, INLINE} TAG_ALIGNMENT;
  50. typedef enum {col_tag, tab_tag, tab0_tag, none} colType;
  51. #undef DEBUG_TABLES
  52. // #define DEBUG_TABLES
  53. /*
  54. * prototypes
  55. */
  56. char *get_html_translation (font *f, const string &name);
  57. int char_translate_to_html (font *f, char *buf, int buflen, unsigned char ch, int b, int and_single);
  58. static int auto_links = TRUE; /* by default we enable automatic links at */
  59. /* top of the document. */
  60. static int auto_rule = TRUE; /* by default we enable an automatic rule */
  61. /* at the top and bottom of the document */
  62. static int simple_anchors = FALSE; /* default to anchors with heading text */
  63. static int manufacture_headings = FALSE; /* default is to use the Hn html headings, */
  64. /* rather than manufacture our own. */
  65. static color *default_background = NULL; /* has user requested initial bg color? */
  66. static string job_name; /* if set then the output is split into */
  67. /* multiple files with `job_name'-%d.html */
  68. static int multiple_files = FALSE; /* must we the output be divided into */
  69. /* multiple html files, one for each */
  70. /* heading? */
  71. static int base_point_size = 0; /* which troff font size maps onto html */
  72. /* size 3? */
  73. static int split_level = 2; /* what heading level to split at? */
  74. static string head_info; /* user supplied information to be placed */
  75. /* into <head> </head> */
  76. /*
  77. * start with a few favorites
  78. */
  79. void stop () {}
  80. static int min (int a, int b)
  81. {
  82. if (a < b)
  83. return a;
  84. else
  85. return b;
  86. }
  87. static int max (int a, int b)
  88. {
  89. if (a > b)
  90. return a;
  91. else
  92. return b;
  93. }
  94. /*
  95. * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
  96. */
  97. static int is_intersection (int a1, int a2, int b1, int b2)
  98. {
  99. // easier to prove NOT outside limits
  100. return ! ((a1 > b2) || (a2 < b1));
  101. }
  102. /*
  103. * is_digit - returns TRUE if character, ch, is a digit.
  104. */
  105. static int is_digit (char ch)
  106. {
  107. return (ch >= '0') && (ch <= '9');
  108. }
  109. /*
  110. * the classes and methods for maintaining a list of files.
  111. */
  112. struct file {
  113. FILE *fp;
  114. file *next;
  115. int new_output_file;
  116. int require_links;
  117. string output_file_name;
  118. file (FILE *f);
  119. };
  120. /*
  121. * file - initialize all fields to NULL
  122. */
  123. file::file (FILE *f)
  124. : fp(f), next(NULL), new_output_file(FALSE),
  125. require_links(FALSE), output_file_name("")
  126. {
  127. }
  128. class files {
  129. public:
  130. files ();
  131. FILE *get_file (void);
  132. void start_of_list (void);
  133. void move_next (void);
  134. void add_new_file (FILE *f);
  135. void set_file_name (string name);
  136. void set_links_required (void);
  137. int are_links_required (void);
  138. int is_new_output_file (void);
  139. string file_name (void);
  140. string next_file_name (void);
  141. private:
  142. file *head;
  143. file *tail;
  144. file *ptr;
  145. };
  146. /*
  147. * files - create an empty list of files.
  148. */
  149. files::files ()
  150. : head(NULL), tail(NULL), ptr(NULL)
  151. {
  152. }
  153. /*
  154. * get_file - returns the FILE associated with ptr.
  155. */
  156. FILE *files::get_file (void)
  157. {
  158. if (ptr)
  159. return ptr->fp;
  160. else
  161. return NULL;
  162. }
  163. /*
  164. * start_of_list - reset the ptr to the start of the list.
  165. */
  166. void files::start_of_list (void)
  167. {
  168. ptr = head;
  169. }
  170. /*
  171. * move_next - moves the ptr to the next element on the list.
  172. */
  173. void files::move_next (void)
  174. {
  175. if (ptr != NULL)
  176. ptr = ptr->next;
  177. }
  178. /*
  179. * add_new_file - adds a new file, f, to the list.
  180. */
  181. void files::add_new_file (FILE *f)
  182. {
  183. if (head == NULL) {
  184. head = new file(f);
  185. tail = head;
  186. } else {
  187. tail->next = new file(f);
  188. tail = tail->next;
  189. }
  190. ptr = tail;
  191. }
  192. /*
  193. * set_file_name - sets the final file name to contain the html
  194. * data to name.
  195. */
  196. void files::set_file_name (string name)
  197. {
  198. if (ptr != NULL) {
  199. ptr->output_file_name = name;
  200. ptr->new_output_file = TRUE;
  201. }
  202. }
  203. /*
  204. * set_links_required - issue links when processing this component
  205. * of the file.
  206. */
  207. void files::set_links_required (void)
  208. {
  209. if (ptr != NULL)
  210. ptr->require_links = TRUE;
  211. }
  212. /*
  213. * are_links_required - returns TRUE if this section of the file
  214. * requires that links should be issued.
  215. */
  216. int files::are_links_required (void)
  217. {
  218. if (ptr != NULL)
  219. return ptr->require_links;
  220. return FALSE;
  221. }
  222. /*
  223. * is_new_output_file - returns TRUE if this component of the file
  224. * is the start of a new output file.
  225. */
  226. int files::is_new_output_file (void)
  227. {
  228. if (ptr != NULL)
  229. return ptr->new_output_file;
  230. return FALSE;
  231. }
  232. /*
  233. * file_name - returns the name of the file.
  234. */
  235. string files::file_name (void)
  236. {
  237. if (ptr != NULL)
  238. return ptr->output_file_name;
  239. return string("");
  240. }
  241. /*
  242. * next_file_name - returns the name of the next file.
  243. */
  244. string files::next_file_name (void)
  245. {
  246. if (ptr != NULL && ptr->next != NULL)
  247. return ptr->next->output_file_name;
  248. return string("");
  249. }
  250. /*
  251. * the class and methods for styles
  252. */
  253. struct style {
  254. font *f;
  255. int point_size;
  256. int font_no;
  257. int height;
  258. int slant;
  259. color col;
  260. style ();
  261. style (font *, int, int, int, int, color);
  262. int operator == (const style &) const;
  263. int operator != (const style &) const;
  264. };
  265. style::style()
  266. : f(NULL)
  267. {
  268. }
  269. style::style(font *p, int sz, int h, int sl, int no, color c)
  270. : f(p), point_size(sz), font_no(no), height(h), slant(sl), col(c)
  271. {
  272. }
  273. int style::operator==(const style &s) const
  274. {
  275. return (f == s.f && point_size == s.point_size
  276. && height == s.height && slant == s.slant && col == s.col);
  277. }
  278. int style::operator!=(const style &s) const
  279. {
  280. return !(*this == s);
  281. }
  282. /*
  283. * the class and methods for retaining ascii text
  284. */
  285. struct char_block {
  286. enum { SIZE = 256 };
  287. char *buffer;
  288. int used;
  289. char_block *next;
  290. char_block();
  291. char_block(int length);
  292. ~char_block();
  293. };
  294. char_block::char_block()
  295. : buffer(NULL), used(0), next(NULL)
  296. {
  297. }
  298. char_block::char_block(int length)
  299. : used(0), next(NULL)
  300. {
  301. buffer = new char[max(length, char_block::SIZE)];
  302. if (buffer == NULL)
  303. fatal("out of memory error");
  304. }
  305. char_block::~char_block()
  306. {
  307. if (buffer != NULL)
  308. a_delete buffer;
  309. }
  310. class char_buffer {
  311. public:
  312. char_buffer();
  313. ~char_buffer();
  314. char *add_string(const char *, unsigned int);
  315. char *add_string(const string &);
  316. private:
  317. char_block *head;
  318. char_block *tail;
  319. };
  320. char_buffer::char_buffer()
  321. : head(NULL), tail(NULL)
  322. {
  323. }
  324. char_buffer::~char_buffer()
  325. {
  326. while (head != NULL) {
  327. char_block *temp = head;
  328. head = head->next;
  329. delete temp;
  330. }
  331. }
  332. char *char_buffer::add_string (const char *s, unsigned int length)
  333. {
  334. int i=0;
  335. unsigned int old_used;
  336. if (s == NULL || length == 0)
  337. return NULL;
  338. if (tail == NULL) {
  339. tail = new char_block(length+1);
  340. head = tail;
  341. } else {
  342. if (tail->used + length+1 > char_block::SIZE) {
  343. tail->next = new char_block(length+1);
  344. tail = tail->next;
  345. }
  346. }
  347. old_used = tail->used;
  348. do {
  349. tail->buffer[tail->used] = s[i];
  350. tail->used++;
  351. i++;
  352. length--;
  353. } while (length>0);
  354. // add terminating nul character
  355. tail->buffer[tail->used] = '\0';
  356. tail->used++;
  357. // and return start of new string
  358. return &tail->buffer[old_used];
  359. }
  360. char *char_buffer::add_string (const string &s)
  361. {
  362. return add_string(s.contents(), s.length());
  363. }
  364. /*
  365. * the classes and methods for maintaining glyph positions.
  366. */
  367. class text_glob {
  368. public:
  369. void text_glob_html (style *s, char *str, int length,
  370. int min_vertical, int min_horizontal,
  371. int max_vertical, int max_horizontal);
  372. void text_glob_special (style *s, char *str, int length,
  373. int min_vertical, int min_horizontal,
  374. int max_vertical, int max_horizontal);
  375. void text_glob_line (style *s,
  376. int min_vertical, int min_horizontal,
  377. int max_vertical, int max_horizontal,
  378. int thickness);
  379. void text_glob_auto_image(style *s, char *str, int length,
  380. int min_vertical, int min_horizontal,
  381. int max_vertical, int max_horizontal);
  382. void text_glob_tag (style *s, char *str, int length,
  383. int min_vertical, int min_horizontal,
  384. int max_vertical, int max_horizontal);
  385. text_glob (void);
  386. ~text_glob (void);
  387. int is_a_line (void);
  388. int is_a_tag (void);
  389. int is_eol (void);
  390. int is_auto_img (void);
  391. int is_br (void);
  392. int is_in (void);
  393. int is_po (void);
  394. int is_ti (void);
  395. int is_ll (void);
  396. int is_ce (void);
  397. int is_tl (void);
  398. int is_eo_tl (void);
  399. int is_eol_ce (void);
  400. int is_col (void);
  401. int is_tab (void);
  402. int is_tab0 (void);
  403. int is_ta (void);
  404. int is_tab_ts (void);
  405. int is_tab_te (void);
  406. int is_nf (void);
  407. int is_fi (void);
  408. int is_eo_h (void);
  409. int get_arg (void);
  410. int get_tab_args (char *align);
  411. void remember_table (html_table *t);
  412. html_table *get_table (void);
  413. style text_style;
  414. const char *text_string;
  415. unsigned int text_length;
  416. int minv, minh, maxv, maxh;
  417. int is_tag; // is this a .br, .sp, .tl etc
  418. int is_img_auto; // image created by eqn delim
  419. int is_special; // text has come via 'x X html:'
  420. int is_line; // is the command a <line>?
  421. int thickness; // the thickness of a line
  422. html_table *tab; // table description
  423. private:
  424. text_glob (style *s, const char *str, int length,
  425. int min_vertical , int min_horizontal,
  426. int max_vertical , int max_horizontal,
  427. bool is_troff_command,
  428. bool is_auto_image, bool is_special_command,
  429. bool is_a_line , int thickness);
  430. };
  431. text_glob::text_glob (style *s, const char *str, int length,
  432. int min_vertical, int min_horizontal,
  433. int max_vertical, int max_horizontal,
  434. bool is_troff_command,
  435. bool is_auto_image, bool is_special_command,
  436. bool is_a_line_flag, int line_thickness)
  437. : text_style(*s), text_string(str), text_length(length),
  438. minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal),
  439. is_tag(is_troff_command), is_img_auto(is_auto_image), is_special(is_special_command),
  440. is_line(is_a_line_flag), thickness(line_thickness), tab(NULL)
  441. {
  442. }
  443. text_glob::text_glob ()
  444. : text_string(NULL), text_length(0), minv(-1), minh(-1), maxv(-1), maxh(-1),
  445. is_tag(FALSE), is_special(FALSE), is_line(FALSE), thickness(0), tab(NULL)
  446. {
  447. }
  448. text_glob::~text_glob ()
  449. {
  450. if (tab != NULL)
  451. delete tab;
  452. }
  453. /*
  454. * text_glob_html - used to place html text into the glob buffer.
  455. */
  456. void text_glob::text_glob_html (style *s, char *str, int length,
  457. int min_vertical , int min_horizontal,
  458. int max_vertical , int max_horizontal)
  459. {
  460. text_glob *g = new text_glob(s, str, length,
  461. min_vertical, min_horizontal, max_vertical, max_horizontal,
  462. FALSE, FALSE, FALSE, FALSE, 0);
  463. *this = *g;
  464. delete g;
  465. }
  466. /*
  467. * text_glob_html - used to place html specials into the glob buffer.
  468. * This text is essentially html commands coming through
  469. * from the macro sets, with special designated sequences of
  470. * characters translated into html. See add_and_encode.
  471. */
  472. void text_glob::text_glob_special (style *s, char *str, int length,
  473. int min_vertical , int min_horizontal,
  474. int max_vertical , int max_horizontal)
  475. {
  476. text_glob *g = new text_glob(s, str, length,
  477. min_vertical, min_horizontal, max_vertical, max_horizontal,
  478. FALSE, FALSE, TRUE, FALSE, 0);
  479. *this = *g;
  480. delete g;
  481. }
  482. /*
  483. * text_glob_line - record horizontal draw line commands.
  484. */
  485. void text_glob::text_glob_line (style *s,
  486. int min_vertical , int min_horizontal,
  487. int max_vertical , int max_horizontal,
  488. int thickness_value)
  489. {
  490. text_glob *g = new text_glob(s, "", 0,
  491. min_vertical, min_horizontal, max_vertical, max_horizontal,
  492. FALSE, FALSE, FALSE, TRUE, thickness_value);
  493. *this = *g;
  494. delete g;
  495. }
  496. /*
  497. * text_glob_auto_image - record the presence of a .auto-image tag command.
  498. * Used to mark that an image has been created automatically
  499. * by a preprocessor and (pre-grohtml/troff) combination.
  500. * Under some circumstances images may not be created.
  501. * (consider .EQ
  502. * delim $$
  503. * .EN
  504. * .TS
  505. * tab(!), center;
  506. * l!l.
  507. * $1 over x$!recripical of x
  508. * .TE
  509. *
  510. * the first auto-image marker is created via .EQ/.EN pair
  511. * and no image is created.
  512. * The second auto-image marker occurs at $1 over x$
  513. * Currently this image will not be created
  514. * as the whole of the table is created as an image.
  515. * (Once html tables are handled by grohtml this will change.
  516. * Shortly this will be the case).
  517. */
  518. void text_glob::text_glob_auto_image(style *s, char *str, int length,
  519. int min_vertical, int min_horizontal,
  520. int max_vertical, int max_horizontal)
  521. {
  522. text_glob *g = new text_glob(s, str, length,
  523. min_vertical, min_horizontal, max_vertical, max_horizontal,
  524. TRUE, TRUE, FALSE, FALSE, 0);
  525. *this = *g;
  526. delete g;
  527. }
  528. /*
  529. * text_glob_tag - records a troff tag.
  530. */
  531. void text_glob::text_glob_tag (style *s, char *str, int length,
  532. int min_vertical, int min_horizontal,
  533. int max_vertical, int max_horizontal)
  534. {
  535. text_glob *g = new text_glob(s, str, length,
  536. min_vertical, min_horizontal, max_vertical, max_horizontal,
  537. TRUE, FALSE, FALSE, FALSE, 0);
  538. *this = *g;
  539. delete g;
  540. }
  541. /*
  542. * is_a_line - returns TRUE if glob should be converted into an <hr>
  543. */
  544. int text_glob::is_a_line (void)
  545. {
  546. return is_line;
  547. }
  548. /*
  549. * is_a_tag - returns TRUE if glob contains a troff directive.
  550. */
  551. int text_glob::is_a_tag (void)
  552. {
  553. return is_tag;
  554. }
  555. /*
  556. * is_eol - returns TRUE if glob contains the tag eol
  557. */
  558. int text_glob::is_eol (void)
  559. {
  560. return is_tag && (strcmp(text_string, "devtag:.eol") == 0);
  561. }
  562. /*
  563. * is_eol_ce - returns TRUE if glob contains the tag eol.ce
  564. */
  565. int text_glob::is_eol_ce (void)
  566. {
  567. return is_tag && (strcmp(text_string, "devtag:eol.ce") == 0);
  568. }
  569. /*
  570. * is_tl - returns TRUE if glob contains the tag .tl
  571. */
  572. int text_glob::is_tl (void)
  573. {
  574. return is_tag && (strcmp(text_string, "devtag:.tl") == 0);
  575. }
  576. /*
  577. * is_eo_tl - returns TRUE if glob contains the tag eo.tl
  578. */
  579. int text_glob::is_eo_tl (void)
  580. {
  581. return is_tag && (strcmp(text_string, "devtag:.eo.tl") == 0);
  582. }
  583. /*
  584. * is_nf - returns TRUE if glob contains the tag .fi 0
  585. */
  586. int text_glob::is_nf (void)
  587. {
  588. return is_tag && (strncmp(text_string, "devtag:.fi",
  589. strlen("devtag:.fi")) == 0) &&
  590. (get_arg() == 0);
  591. }
  592. /*
  593. * is_fi - returns TRUE if glob contains the tag .fi 1
  594. */
  595. int text_glob::is_fi (void)
  596. {
  597. return( is_tag && (strncmp(text_string, "devtag:.fi",
  598. strlen("devtag:.fi")) == 0) &&
  599. (get_arg() == 1) );
  600. }
  601. /*
  602. * is_eo_h - returns TRUE if glob contains the tag .eo.h
  603. */
  604. int text_glob::is_eo_h (void)
  605. {
  606. return is_tag && (strcmp(text_string, "devtag:.eo.h") == 0);
  607. }
  608. /*
  609. * is_ce - returns TRUE if glob contains the tag .ce
  610. */
  611. int text_glob::is_ce (void)
  612. {
  613. return is_tag && (strncmp(text_string, "devtag:.ce",
  614. strlen("devtag:.ce")) == 0);
  615. }
  616. /*
  617. * is_in - returns TRUE if glob contains the tag .in
  618. */
  619. int text_glob::is_in (void)
  620. {
  621. return is_tag && (strncmp(text_string, "devtag:.in ",
  622. strlen("devtag:.in ")) == 0);
  623. }
  624. /*
  625. * is_po - returns TRUE if glob contains the tag .po
  626. */
  627. int text_glob::is_po (void)
  628. {
  629. return is_tag && (strncmp(text_string, "devtag:.po ",
  630. strlen("devtag:.po ")) == 0);
  631. }
  632. /*
  633. * is_ti - returns TRUE if glob contains the tag .ti
  634. */
  635. int text_glob::is_ti (void)
  636. {
  637. return is_tag && (strncmp(text_string, "devtag:.ti ",
  638. strlen("devtag:.ti ")) == 0);
  639. }
  640. /*
  641. * is_ll - returns TRUE if glob contains the tag .ll
  642. */
  643. int text_glob::is_ll (void)
  644. {
  645. return is_tag && (strncmp(text_string, "devtag:.ll ",
  646. strlen("devtag:.ll ")) == 0);
  647. }
  648. /*
  649. * is_col - returns TRUE if glob contains the tag .col
  650. */
  651. int text_glob::is_col (void)
  652. {
  653. return is_tag && (strncmp(text_string, "devtag:.col",
  654. strlen("devtag:.col")) == 0);
  655. }
  656. /*
  657. * is_tab_ts - returns TRUE if glob contains the tag .tab_ts
  658. */
  659. int text_glob::is_tab_ts (void)
  660. {
  661. return is_tag && (strcmp(text_string, "devtag:.tab-ts") == 0);
  662. }
  663. /*
  664. * is_tab_te - returns TRUE if glob contains the tag .tab_te
  665. */
  666. int text_glob::is_tab_te (void)
  667. {
  668. return is_tag && (strcmp(text_string, "devtag:.tab-te") == 0);
  669. }
  670. /*
  671. * is_ta - returns TRUE if glob contains the tag .ta
  672. */
  673. int text_glob::is_ta (void)
  674. {
  675. return is_tag && (strncmp(text_string, "devtag:.ta ",
  676. strlen("devtag:.ta ")) == 0);
  677. }
  678. /*
  679. * is_tab - returns TRUE if glob contains the tag tab
  680. */
  681. int text_glob::is_tab (void)
  682. {
  683. return is_tag && (strncmp(text_string, "devtag:tab ",
  684. strlen("devtag:tab ")) == 0);
  685. }
  686. /*
  687. * is_tab0 - returns TRUE if glob contains the tag tab0
  688. */
  689. int text_glob::is_tab0 (void)
  690. {
  691. return is_tag && (strncmp(text_string, "devtag:tab0",
  692. strlen("devtag:tab0")) == 0);
  693. }
  694. /*
  695. * is_auto_img - returns TRUE if the glob contains an automatically
  696. * generated image.
  697. */
  698. int text_glob::is_auto_img (void)
  699. {
  700. return is_img_auto;
  701. }
  702. /*
  703. * is_br - returns TRUE if the glob is a tag containing a .br
  704. * or an implied .br. Note that we do not include .nf or .fi
  705. * as grohtml will place a .br after these commands if they
  706. * should break the line.
  707. */
  708. int text_glob::is_br (void)
  709. {
  710. return is_a_tag() && ((strcmp ("devtag:.br", text_string) == 0) ||
  711. (strncmp("devtag:.sp", text_string,
  712. strlen("devtag:.sp")) == 0));
  713. }
  714. int text_glob::get_arg (void)
  715. {
  716. if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
  717. const char *p = text_string;
  718. while ((*p != (char)0) && (!isspace(*p)))
  719. p++;
  720. while ((*p != (char)0) && (isspace(*p)))
  721. p++;
  722. if (*p == (char)0)
  723. return -1;
  724. return atoi(p);
  725. }
  726. return -1;
  727. }
  728. /*
  729. * get_tab_args - returns the tab position and alignment of the tab tag
  730. */
  731. int text_glob::get_tab_args (char *align)
  732. {
  733. if (strncmp("devtag:", text_string, strlen("devtag:")) == 0) {
  734. const char *p = text_string;
  735. // firstly the alignment C|R|L
  736. while ((*p != (char)0) && (!isspace(*p)))
  737. p++;
  738. while ((*p != (char)0) && (isspace(*p)))
  739. p++;
  740. *align = *p;
  741. // now the int value
  742. while ((*p != (char)0) && (!isspace(*p)))
  743. p++;
  744. while ((*p != (char)0) && (isspace(*p)))
  745. p++;
  746. if (*p == (char)0)
  747. return -1;
  748. return atoi(p);
  749. }
  750. return -1;
  751. }
  752. /*
  753. * remember_table - saves table, t, in the text_glob.
  754. */
  755. void text_glob::remember_table (html_table *t)
  756. {
  757. if (tab != NULL)
  758. delete tab;
  759. tab = t;
  760. }
  761. /*
  762. * get_table - returns the stored table description.
  763. */
  764. html_table *text_glob::get_table (void)
  765. {
  766. return tab;
  767. }
  768. /*
  769. * the class and methods used to construct ordered double linked
  770. * lists. In a previous implementation we used templates via
  771. * #include "ordered-list.h", but this does assume that all C++
  772. * compilers can handle this feature. Pragmatically it is safer to
  773. * assume this is not the case.
  774. */
  775. struct element_list {
  776. element_list *right;
  777. element_list *left;
  778. text_glob *datum;
  779. int lineno;
  780. int minv, minh, maxv, maxh;
  781. element_list (text_glob *d,
  782. int line_number,
  783. int min_vertical, int min_horizontal,
  784. int max_vertical, int max_horizontal);
  785. element_list ();
  786. ~element_list ();
  787. };
  788. element_list::element_list ()
  789. : right(0), left(0), datum(0), lineno(0), minv(-1), minh(-1), maxv(-1), maxh(-1)
  790. {
  791. }
  792. /*
  793. * element_list - create a list element assigning the datum and region parameters.
  794. */
  795. element_list::element_list (text_glob *in,
  796. int line_number,
  797. int min_vertical, int min_horizontal,
  798. int max_vertical, int max_horizontal)
  799. : right(0), left(0), datum(in), lineno(line_number),
  800. minv(min_vertical), minh(min_horizontal), maxv(max_vertical), maxh(max_horizontal)
  801. {
  802. }
  803. element_list::~element_list ()
  804. {
  805. if (datum != NULL)
  806. delete datum;
  807. }
  808. class list {
  809. public:
  810. list ();
  811. ~list ();
  812. int is_less (element_list *a, element_list *b);
  813. void add (text_glob *in,
  814. int line_number,
  815. int min_vertical, int min_horizontal,
  816. int max_vertical, int max_horizontal);
  817. void sub_move_right (void);
  818. void move_right (void);
  819. void move_left (void);
  820. int is_empty (void);
  821. int is_equal_to_tail (void);
  822. int is_equal_to_head (void);
  823. void start_from_head (void);
  824. void start_from_tail (void);
  825. void insert (text_glob *in);
  826. void move_to (text_glob *in);
  827. text_glob *move_right_get_data (void);
  828. text_glob *move_left_get_data (void);
  829. text_glob *get_data (void);
  830. private:
  831. element_list *head;
  832. element_list *tail;
  833. element_list *ptr;
  834. };
  835. /*
  836. * list - construct an empty list.
  837. */
  838. list::list ()
  839. : head(NULL), tail(NULL), ptr(NULL)
  840. {
  841. }
  842. /*
  843. * ~list - destroy a complete list.
  844. */
  845. list::~list()
  846. {
  847. element_list *temp=head;
  848. do {
  849. temp = head;
  850. if (temp != NULL) {
  851. head = head->right;
  852. delete temp;
  853. }
  854. } while ((head != NULL) && (head != tail));
  855. }
  856. /*
  857. * is_less - returns TRUE if a is left of b if on the same line or
  858. * if a is higher up the page than b.
  859. */
  860. int list::is_less (element_list *a, element_list *b)
  861. {
  862. // was if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
  863. if (a->lineno < b->lineno) {
  864. return( TRUE );
  865. } else if (a->lineno > b->lineno) {
  866. return( FALSE );
  867. } else if (is_intersection(a->minv, a->maxv, b->minv, b->maxv)) {
  868. return( a->minh < b->minh );
  869. } else {
  870. return( a->maxv < b->maxv );
  871. }
  872. }
  873. /*
  874. * add - adds a datum to the list in the order specified by the
  875. * region position.
  876. */
  877. void list::add (text_glob *in, int line_number, int min_vertical, int min_horizontal, int max_vertical, int max_horizontal)
  878. {
  879. // create a new list element with datum and position fields initialized
  880. element_list *t = new element_list(in, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
  881. element_list *last;
  882. #if 0
  883. fprintf(stderr, "[%s %d,%d,%d,%d] ",
  884. in->text_string, min_vertical, min_horizontal, max_vertical, max_horizontal);
  885. fflush(stderr);
  886. #endif
  887. if (head == NULL) {
  888. head = t;
  889. tail = t;
  890. ptr = t;
  891. t->left = t;
  892. t->right = t;
  893. } else {
  894. last = tail;
  895. while ((last != head) && (is_less(t, last)))
  896. last = last->left;
  897. if (is_less(t, last)) {
  898. t->right = last;
  899. last->left->right = t;
  900. t->left = last->left;
  901. last->left = t;
  902. // now check for a new head
  903. if (last == head)
  904. head = t;
  905. } else {
  906. // add t beyond last
  907. t->right = last->right;
  908. t->left = last;
  909. last->right->left = t;
  910. last->right = t;
  911. // now check for a new tail
  912. if (last == tail)
  913. tail = t;
  914. }
  915. }
  916. }
  917. /*
  918. * sub_move_right - removes the element which is currently pointed to by ptr
  919. * from the list and moves ptr to the right.
  920. */
  921. void list::sub_move_right (void)
  922. {
  923. element_list *t=ptr->right;
  924. if (head == tail) {
  925. head = NULL;
  926. if (tail != NULL)
  927. delete tail;
  928. tail = NULL;
  929. ptr = NULL;
  930. } else {
  931. if (head == ptr)
  932. head = head->right;
  933. if (tail == ptr)
  934. tail = tail->left;
  935. ptr->left->right = ptr->right;
  936. ptr->right->left = ptr->left;
  937. ptr = t;
  938. }
  939. }
  940. /*
  941. * start_from_head - assigns ptr to the head.
  942. */
  943. void list::start_from_head (void)
  944. {
  945. ptr = head;
  946. }
  947. /*
  948. * start_from_tail - assigns ptr to the tail.
  949. */
  950. void list::start_from_tail (void)
  951. {
  952. ptr = tail;
  953. }
  954. /*
  955. * is_empty - returns TRUE if the list has no elements.
  956. */
  957. int list::is_empty (void)
  958. {
  959. return head == NULL;
  960. }
  961. /*
  962. * is_equal_to_tail - returns TRUE if the ptr equals the tail.
  963. */
  964. int list::is_equal_to_tail (void)
  965. {
  966. return ptr == tail;
  967. }
  968. /*
  969. * is_equal_to_head - returns TRUE if the ptr equals the head.
  970. */
  971. int list::is_equal_to_head (void)
  972. {
  973. return ptr == head;
  974. }
  975. /*
  976. * move_left - moves the ptr left.
  977. */
  978. void list::move_left (void)
  979. {
  980. ptr = ptr->left;
  981. }
  982. /*
  983. * move_right - moves the ptr right.
  984. */
  985. void list::move_right (void)
  986. {
  987. ptr = ptr->right;
  988. }
  989. /*
  990. * get_datum - returns the datum referenced via ptr.
  991. */
  992. text_glob* list::get_data (void)
  993. {
  994. return ptr->datum;
  995. }
  996. /*
  997. * move_right_get_data - returns the datum referenced via ptr and moves
  998. * ptr right.
  999. */
  1000. text_glob* list::move_right_get_data (void)
  1001. {
  1002. ptr = ptr->right;
  1003. if (ptr == head)
  1004. return NULL;
  1005. else
  1006. return ptr->datum;
  1007. }
  1008. /*
  1009. * move_left_get_data - returns the datum referenced via ptr and moves
  1010. * ptr right.
  1011. */
  1012. text_glob* list::move_left_get_data (void)
  1013. {
  1014. ptr = ptr->left;
  1015. if (ptr == tail)
  1016. return NULL;
  1017. else
  1018. return ptr->datum;
  1019. }
  1020. /*
  1021. * insert - inserts data after the current position.
  1022. */
  1023. void list::insert (text_glob *in)
  1024. {
  1025. if (is_empty())
  1026. fatal("list must not be empty if we are inserting data");
  1027. else {
  1028. if (ptr == NULL)
  1029. ptr = head;
  1030. element_list *t = new element_list(in, ptr->lineno, ptr->minv, ptr->minh, ptr->maxv, ptr->maxh);
  1031. if (ptr == tail)
  1032. tail = t;
  1033. ptr->right->left = t;
  1034. t->right = ptr->right;
  1035. ptr->right = t;
  1036. t->left = ptr;
  1037. }
  1038. }
  1039. /*
  1040. * move_to - moves the current position to the point where data, in, exists.
  1041. * This is an expensive method and should be used sparingly.
  1042. */
  1043. void list::move_to (text_glob *in)
  1044. {
  1045. ptr = head;
  1046. while (ptr != tail && ptr->datum != in)
  1047. ptr = ptr->right;
  1048. }
  1049. /*
  1050. * page class and methods
  1051. */
  1052. class page {
  1053. public:
  1054. page (void);
  1055. void add (style *s, const string &str,
  1056. int line_number,
  1057. int min_vertical, int min_horizontal,
  1058. int max_vertical, int max_horizontal);
  1059. void add_tag (style *s, const string &str,
  1060. int line_number,
  1061. int min_vertical, int min_horizontal,
  1062. int max_vertical, int max_horizontal);
  1063. void add_and_encode (style *s, const string &str,
  1064. int line_number,
  1065. int min_vertical, int min_horizontal,
  1066. int max_vertical, int max_horizontal,
  1067. int is_tag);
  1068. void add_line (style *s,
  1069. int line_number,
  1070. int x1, int y1, int x2, int y2,
  1071. int thickness);
  1072. void insert_tag (const string &str);
  1073. void dump_page (void); // debugging method
  1074. // and the data
  1075. list glyphs; // position of glyphs and specials on page
  1076. char_buffer buffer; // all characters for this page
  1077. };
  1078. page::page()
  1079. {
  1080. }
  1081. /*
  1082. * insert_tag - inserts a tag after the current position.
  1083. */
  1084. void page::insert_tag (const string &str)
  1085. {
  1086. if (str.length() > 0) {
  1087. text_glob *g=new text_glob();
  1088. text_glob *f=glyphs.get_data();
  1089. g->text_glob_tag(&f->text_style, buffer.add_string(str), str.length(),
  1090. f->minv, f->minh, f->maxv, f->maxh);
  1091. glyphs.insert(g);
  1092. }
  1093. }
  1094. /*
  1095. * add - add html text to the list of glyphs.
  1096. */
  1097. void page::add (style *s, const string &str,
  1098. int line_number,
  1099. int min_vertical, int min_horizontal,
  1100. int max_vertical, int max_horizontal)
  1101. {
  1102. if (str.length() > 0) {
  1103. text_glob *g=new text_glob();
  1104. g->text_glob_html(s, buffer.add_string(str), str.length(),
  1105. min_vertical, min_horizontal, max_vertical, max_horizontal);
  1106. glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
  1107. }
  1108. }
  1109. /*
  1110. * add_tag - adds a troff tag, for example: .tl .sp .br
  1111. */
  1112. void page::add_tag (style *s, const string &str,
  1113. int line_number,
  1114. int min_vertical, int min_horizontal,
  1115. int max_vertical, int max_horizontal)
  1116. {
  1117. if (str.length() > 0) {
  1118. text_glob *g;
  1119. if (strncmp((str+'\0').contents(), "devtag:.auto-image",
  1120. strlen("devtag:.auto-image")) == 0) {
  1121. g = new text_glob();
  1122. g->text_glob_auto_image(s, buffer.add_string(str), str.length(),
  1123. min_vertical, min_horizontal, max_vertical, max_horizontal);
  1124. } else {
  1125. g = new text_glob();
  1126. g->text_glob_tag(s, buffer.add_string(str), str.length(),
  1127. min_vertical, min_horizontal, max_vertical, max_horizontal);
  1128. }
  1129. glyphs.add(g, line_number, min_vertical, min_horizontal, max_vertical, max_horizontal);
  1130. }
  1131. }
  1132. /*
  1133. * add_line - adds the <line> primitive providing that y1==y2
  1134. */
  1135. void page::add_line (style *s,
  1136. int line_number,
  1137. int x_1, int y_1, int x_2, int y_2,
  1138. int thickness)
  1139. {
  1140. if (y_1 == y_2) {
  1141. text_glob *g = new text_glob();
  1142. g->text_glob_line(s,
  1143. min(y_1, y_2), min(x_1, x_2),
  1144. max(y_1, y_2), max(x_1, x_2),
  1145. thickness);
  1146. glyphs.add(g, line_number,
  1147. min(y_1, y_2), min(x_1, x_2),
  1148. max(y_1, y_2), max(x_1, x_2));
  1149. }
  1150. }
  1151. /*
  1152. * to_unicode - returns a unicode translation of int, ch.
  1153. */
  1154. static char *to_unicode (unsigned int ch)
  1155. {
  1156. static char buf[30];
  1157. sprintf(buf, "&#%u;", ch);
  1158. return buf;
  1159. }
  1160. /*
  1161. * add_and_encode - adds a special string to the page, it translates the string
  1162. * into html glyphs. The special string will have come from x X html:
  1163. * and can contain troff character encodings which appear as
  1164. * \(char\). A sequence of \\ represents \.
  1165. * So for example we can write:
  1166. * "cost = \(Po\)3.00 file = \\foo\\bar"
  1167. * which is translated into:
  1168. * "cost = &pound;3.00 file = \foo\bar"
  1169. */
  1170. void page::add_and_encode (style *s, const string &str,
  1171. int line_number,
  1172. int min_vertical, int min_horizontal,
  1173. int max_vertical, int max_horizontal,
  1174. int is_tag)
  1175. {
  1176. string html_string;
  1177. char *html_glyph;
  1178. int i=0;
  1179. if (s->f == NULL)
  1180. return;
  1181. while (i < str.length()) {
  1182. if ((i+1<str.length()) && (str.substring(i, 2) == string("\\("))) {
  1183. // start of escape
  1184. i += 2; // move over \(
  1185. int a = i;
  1186. while ((i+1<str.length()) && (str.substring(i, 2) != string("\\)"))) {
  1187. i++;
  1188. }
  1189. int n = i;
  1190. if ((i+1<str.length()) && (str.substring(i, 2) == string("\\)")))
  1191. i++;
  1192. else
  1193. n = -1;
  1194. if (n > 0) {
  1195. string troff_charname = str.substring(a, n-a);
  1196. html_glyph = get_html_translation(s->f, troff_charname);
  1197. if (html_glyph)
  1198. html_string += html_glyph;
  1199. else {
  1200. int idx=s->f->name_to_index((troff_charname + '\0').contents());
  1201. if (s->f->contains(idx) && (idx != 0))
  1202. html_string += s->f->get_code(idx);
  1203. }
  1204. }
  1205. } else
  1206. html_string += str[i];
  1207. i++;
  1208. }
  1209. if (html_string.length() > 0) {
  1210. text_glob *g=new text_glob();
  1211. if (is_tag)
  1212. g->text_glob_tag(s, buffer.add_string(html_string),
  1213. html_string.length(),
  1214. min_vertical, min_horizontal,
  1215. max_vertical, max_horizontal);
  1216. else
  1217. g->text_glob_special(s, buffer.add_string(html_string),
  1218. html_string.length(),
  1219. min_vertical, min_horizontal,
  1220. max_vertical, max_horizontal);
  1221. glyphs.add(g, line_number, min_vertical,
  1222. min_horizontal, max_vertical, max_horizontal);
  1223. }
  1224. }
  1225. /*
  1226. * dump_page - dump the page contents for debugging purposes.
  1227. */
  1228. void page::dump_page(void)
  1229. {
  1230. #if defined(DEBUG_TABLES)
  1231. text_glob *old_pos = glyphs.get_data();
  1232. text_glob *g;
  1233. printf("\n<!--\n");
  1234. printf("\n\ndebugging start\n");
  1235. glyphs.start_from_head();
  1236. do {
  1237. g = glyphs.get_data();
  1238. if (g->is_tab_ts()) {
  1239. printf("\n\n");
  1240. if (g->get_table() != NULL)
  1241. g->get_table()->dump_table();
  1242. }
  1243. printf("%s ", g->text_string);
  1244. if (g->is_tab_te())
  1245. printf("\n\n");
  1246. glyphs.move_right();
  1247. } while (! glyphs.is_equal_to_head());
  1248. glyphs.move_to(old_pos);
  1249. printf("\ndebugging end\n\n");
  1250. printf("\n-->\n");
  1251. fflush(stdout);
  1252. #endif
  1253. }
  1254. /*
  1255. * font classes and methods
  1256. */
  1257. class html_font : public font {
  1258. html_font(const char *);
  1259. public:
  1260. int encoding_index;
  1261. char *encoding;
  1262. char *reencoded_name;
  1263. ~html_font();
  1264. static html_font *load_html_font(const char *);
  1265. };
  1266. html_font *html_font::load_html_font(const char *s)
  1267. {
  1268. html_font *f = new html_font(s);
  1269. if (!f->load()) {
  1270. delete f;
  1271. return 0;
  1272. }
  1273. return f;
  1274. }
  1275. html_font::html_font(const char *nm)
  1276. : font(nm)
  1277. {
  1278. }
  1279. html_font::~html_font()
  1280. {
  1281. }
  1282. /*
  1283. * a simple class to contain the header to this document
  1284. */
  1285. class title_desc {
  1286. public:
  1287. title_desc ();
  1288. ~title_desc ();
  1289. int has_been_written;
  1290. int has_been_found;
  1291. int with_h1;
  1292. string text;
  1293. };
  1294. title_desc::title_desc ()
  1295. : has_been_written(FALSE), has_been_found(FALSE), with_h1(FALSE)
  1296. {
  1297. }
  1298. title_desc::~title_desc ()
  1299. {
  1300. }
  1301. class header_desc {
  1302. public:
  1303. header_desc ();
  1304. ~header_desc ();
  1305. int no_of_level_one_headings; // how many .SH or .NH 1 have we found?
  1306. int no_of_headings; // how many headings have we found?
  1307. char_buffer headings; // all the headings used in the document
  1308. list headers; // list of headers built from .NH and .SH
  1309. list header_filename; // in which file is this header?
  1310. int header_level; // current header level
  1311. int written_header; // have we written the header yet?
  1312. string header_buffer; // current header text
  1313. void write_headings (FILE *f, int force);
  1314. };
  1315. header_desc::header_desc ()
  1316. : no_of_level_one_headings(0), no_of_headings(0),
  1317. header_level(2), written_header(0)
  1318. {
  1319. }
  1320. header_desc::~header_desc ()
  1321. {
  1322. }
  1323. /*
  1324. * write_headings - emits a list of links for the headings in this document
  1325. */
  1326. void header_desc::write_headings (FILE *f, int force)
  1327. {
  1328. text_glob *g;
  1329. if (auto_links || force) {
  1330. if (! headers.is_empty()) {
  1331. int h=1;
  1332. headers.start_from_head();
  1333. header_filename.start_from_head();
  1334. do {
  1335. g = headers.get_data();
  1336. fputs("<a href=\"", f);
  1337. if (multiple_files && (! header_filename.is_empty())) {
  1338. text_glob *fn = header_filename.get_data();
  1339. fputs(fn->text_string, f);
  1340. }
  1341. fputs("#", f);
  1342. if (simple_anchors) {
  1343. string buffer(ANCHOR_TEMPLATE);
  1344. buffer += as_string(h);
  1345. buffer += '\0';
  1346. fputs(buffer.contents(), f);
  1347. } else
  1348. fputs(g->text_string, f);
  1349. h++;
  1350. fputs("\">", f);
  1351. fputs(g->text_string, f);
  1352. fputs("</a><br>\n", f);
  1353. headers.move_right();
  1354. if (multiple_files && (! header_filename.is_empty()))
  1355. header_filename.move_right();
  1356. } while (! headers.is_equal_to_head());
  1357. fputs("\n", f);
  1358. }
  1359. }
  1360. }
  1361. struct assert_pos {
  1362. assert_pos *next;
  1363. const char *val;
  1364. const char *id;
  1365. };
  1366. class assert_state {
  1367. public:
  1368. assert_state ();
  1369. ~assert_state ();
  1370. void addx (const char *c, const char *i, const char *v,
  1371. const char *f, const char *l);
  1372. void addy (const char *c, const char *i, const char *v,
  1373. const char *f, const char *l);
  1374. void build(const char *c, const char *v,
  1375. const char *f, const char *l);
  1376. void check_br (int br);
  1377. void check_ce (int ce);
  1378. void check_fi (int fi);
  1379. void check_sp (int sp);
  1380. void reset (void);
  1381. private:
  1382. int check_br_flag;
  1383. int check_ce_flag;
  1384. int check_fi_flag;
  1385. int check_sp_flag;
  1386. const char *val_br;
  1387. const char *val_ce;
  1388. const char *val_fi;
  1389. const char *val_sp;
  1390. const char *file_br;
  1391. const char *file_ce;
  1392. const char *file_fi;
  1393. const char *file_sp;
  1394. const char *line_br;
  1395. const char *line_ce;
  1396. const char *line_fi;
  1397. const char *line_sp;
  1398. assert_pos *xhead;
  1399. assert_pos *yhead;
  1400. void add (assert_pos **h,
  1401. const char *c, const char *i, const char *v,
  1402. const char *f, const char *l);
  1403. void compare(assert_pos *t,
  1404. const char *v, const char *f, const char *l);
  1405. void close (const char *c);
  1406. void set (const char *c, const char *v,
  1407. const char *f, const char *l);
  1408. void check_value (const char *s, int v, const char *name,
  1409. const char *f, const char *l, int *flag);
  1410. int check_value_error (int c, int v, const char *s,
  1411. const char *name,
  1412. const char *f, const char *l, int flag);
  1413. };
  1414. assert_state::assert_state ()
  1415. {
  1416. reset();
  1417. val_br = NULL;
  1418. val_ce = NULL;
  1419. val_fi = NULL;
  1420. val_sp = NULL;
  1421. file_br = NULL;
  1422. file_ce = NULL;
  1423. file_fi = NULL;
  1424. file_sp = NULL;
  1425. line_br = NULL;
  1426. line_ce = NULL;
  1427. line_fi = NULL;
  1428. line_sp = NULL;
  1429. xhead = NULL;
  1430. yhead = NULL;
  1431. }
  1432. assert_state::~assert_state ()
  1433. {
  1434. assert_pos *t;
  1435. while (xhead != NULL) {
  1436. t = xhead;
  1437. xhead = xhead->next;
  1438. a_delete (char *)t->val;
  1439. a_delete (char *)t->id;
  1440. delete t;
  1441. }
  1442. while (yhead != NULL) {
  1443. t = yhead;
  1444. yhead = yhead->next;
  1445. a_delete (char *)t->val;
  1446. a_delete (char *)t->id;
  1447. delete t;
  1448. }
  1449. }
  1450. void assert_state::reset (void)
  1451. {
  1452. check_br_flag = 0;
  1453. check_ce_flag = 0;
  1454. check_fi_flag = 0;
  1455. check_sp_flag = 0;
  1456. }
  1457. void assert_state::add (assert_pos **h,
  1458. const char *c, const char *i, const char *v,
  1459. const char *f, const char *l)
  1460. {
  1461. assert_pos *t = *h;
  1462. while (t != NULL) {
  1463. if (strcmp(t->id, i) == 0)
  1464. break;
  1465. t = t->next;
  1466. }
  1467. if (t != NULL && v != NULL && (v[0] != '='))
  1468. compare(t, v, f, l);
  1469. else {
  1470. if (t == NULL) {
  1471. t = new assert_pos;
  1472. t->next = *h;
  1473. (*h) = t;
  1474. }
  1475. if (v == NULL || v[0] != '=') {
  1476. if (f == NULL)
  1477. f = "stdin";
  1478. if (l == NULL)
  1479. l = "<none>";
  1480. if (v == NULL)
  1481. v = "no value at all";
  1482. fprintf(stderr, "%s:%s:error in assert format of id=%s expecting value to be prefixed with an `=' got %s\n",
  1483. f, l, i, v);
  1484. }
  1485. t->id = i;
  1486. t->val = v;
  1487. a_delete (char *)c;
  1488. a_delete (char *)f;
  1489. a_delete (char *)l;
  1490. }
  1491. }
  1492. void assert_state::addx (const char *c, const char *i, const char *v,
  1493. const char *f, const char *l)
  1494. {
  1495. add(&xhead, c, i, v, f, l);
  1496. }
  1497. void assert_state::addy (const char *c, const char *i, const char *v,
  1498. const char *f, const char *l)
  1499. {
  1500. add(&yhead, c, i, v, f, l);
  1501. }
  1502. void assert_state::compare(assert_pos *t,
  1503. const char *v, const char *f, const char *l)
  1504. {
  1505. const char *s=t->val;
  1506. while ((*v) == '=')
  1507. v++;
  1508. while ((*s) == '=')
  1509. s++;
  1510. if (strcmp(v, s) != 0) {
  1511. if (f == NULL)
  1512. f = "stdin";
  1513. if (l == NULL)
  1514. l = "<none>";
  1515. fprintf(stderr, "%s:%s: grohtml assertion failed at id%s expecting %s and was given %s\n",
  1516. f, l, t->id, s, v);
  1517. }
  1518. }
  1519. void assert_state::close (const char *c)
  1520. {
  1521. if (strcmp(c, "sp") == 0)
  1522. check_sp_flag = 0;
  1523. else if (strcmp(c, "br") == 0)
  1524. check_br_flag = 0;
  1525. else if (strcmp(c, "fi") == 0)
  1526. check_fi_flag = 0;
  1527. else if (strcmp(c, "nf") == 0)
  1528. check_fi_flag = 0;
  1529. else if (strcmp(c, "ce") == 0)
  1530. check_ce_flag = 0;
  1531. else
  1532. fprintf(stderr, "internal error: unrecognised tag in grohtml (%s)\n", c);
  1533. }
  1534. const char *replace_negate_str (const char *before, char *after)
  1535. {
  1536. if (before != NULL)
  1537. a_delete (char *)before;
  1538. if (strlen(after) > 0) {
  1539. int d = atoi(after);
  1540. if (d < 0 || d > 1) {
  1541. fprintf(stderr, "expecting nf/fi value to be 0 or 1 not %d\n", d);
  1542. d = 0;
  1543. }
  1544. if (d == 0)
  1545. after[0] = '1';
  1546. else
  1547. after[0] = '0';
  1548. after[1] = (char)0;
  1549. }
  1550. return after;
  1551. }
  1552. const char *replace_str (const char *before, const char *after)
  1553. {
  1554. if (before != NULL)
  1555. a_delete (char *)before;
  1556. return after;
  1557. }
  1558. void assert_state::set (const char *c, const char *v,
  1559. const char *f, const char *l)
  1560. {
  1561. if (l == NULL)
  1562. l = "<none>";
  1563. if (f == NULL)
  1564. f = "stdin";
  1565. // fprintf(stderr, "%s:%s:setting %s to %s\n", f, l, c, v);
  1566. if (strcmp(c, "sp") == 0) {
  1567. check_sp_flag = 1;
  1568. val_sp = replace_str(val_sp, strsave(v));
  1569. file_sp = replace_str(file_sp, strsave(f));
  1570. line_sp = replace_str(line_sp, strsave(l));
  1571. } else if (strcmp(c, "br") == 0) {
  1572. check_br_flag = 1;
  1573. val_br = replace_str(val_br, strsave(v));
  1574. file_br = replace_str(file_br, strsave(f));
  1575. line_br = replace_str(line_br, strsave(l));
  1576. } else if (strcmp(c, "fi") == 0) {
  1577. check_fi_flag = 1;
  1578. val_fi = replace_str(val_fi, strsave(v));
  1579. file_fi = replace_str(file_fi, strsave(f));
  1580. line_fi = replace_str(line_fi, strsave(l));
  1581. } else if (strcmp(c, "nf") == 0) {
  1582. check_fi_flag = 1;
  1583. val_fi = replace_negate_str(val_fi, strsave(v));
  1584. file_fi = replace_str(file_fi, strsave(f));
  1585. line_fi = replace_str(line_fi, strsave(l));
  1586. } else if (strcmp(c, "ce") == 0) {
  1587. check_ce_flag = 1;
  1588. val_ce = replace_str(val_ce, strsave(v));
  1589. file_ce = replace_str(file_ce, strsave(f));
  1590. line_ce = replace_str(line_ce, strsave(l));
  1591. }
  1592. }
  1593. /*
  1594. * build - builds the troff state assertion.
  1595. * see tmac/www.tmac for cmd examples.
  1596. */
  1597. void assert_state::build (const char *c, const char *v,
  1598. const char *f, const char *l)
  1599. {
  1600. if (c[0] == '{')
  1601. set(&c[1], v, f, l);
  1602. if (c[0] == '}')
  1603. close(&c[1]);
  1604. }
  1605. int assert_state::check_value_error (int c, int v, const char *s,
  1606. const char *name,
  1607. const char *f, const char *l, int flag)
  1608. {
  1609. if (! c) {
  1610. if (f == NULL)
  1611. f = "stdin";
  1612. if (l == NULL)
  1613. l = "<none>";
  1614. fprintf(stderr, "%s:%s:grohtml (troff state) assertion failed, expected %s to be %s but found it to contain %d\n",
  1615. f, l, name, s, v);
  1616. return 0;
  1617. }
  1618. return flag;
  1619. }
  1620. void assert_state::check_value (const char *s, int v, const char *name,
  1621. const char *f, const char *l, int *flag)
  1622. {
  1623. if (strncmp(s, "<=", 2) == 0)
  1624. *flag = check_value_error(v <= atoi(&s[2]), v, s, name, f, l, *flag);
  1625. else if (strncmp(s, ">=", 2) == 0)
  1626. *flag = check_value_error(v >= atoi(&s[2]), v, s, name, f, l, *flag);
  1627. else if (strncmp(s, "==", 2) == 0)
  1628. *flag = check_value_error(v == atoi(&s[2]), v, s, name, f, l, *flag);
  1629. else if (strncmp(s, "!=", 2) == 0)
  1630. *flag = check_value_error(v != atoi(&s[2]), v, s, name, f, l, *flag);
  1631. else if (strncmp(s, "<", 1) == 0)
  1632. *flag = check_value_error(v < atoi(&s[2]), v, s, name, f, l, *flag);
  1633. else if (strncmp(s, ">", 1) == 0)
  1634. *flag = check_value_error(v > atoi(&s[2]), v, s, name, f, l, *flag);
  1635. else if (strncmp(s, "=", 1) == 0)
  1636. *flag = check_value_error(v == atoi(&s[1]), v, s, name, f, l, *flag);
  1637. else
  1638. *flag = check_value_error(v == atoi(s), v, s, name, f, l, *flag);
  1639. }
  1640. void assert_state::check_sp (int sp)
  1641. {
  1642. if (check_sp_flag)
  1643. check_value(val_sp, sp, "sp", file_sp, line_sp, &check_sp_flag);
  1644. }
  1645. void assert_state::check_fi (int fi)
  1646. {
  1647. if (check_fi_flag)
  1648. check_value(val_fi, fi, "fi", file_fi, line_fi, &check_fi_flag);
  1649. }
  1650. void assert_state::check_br (int br)
  1651. {
  1652. if (check_br_flag)
  1653. check_value(val_br, br, "br", file_br, line_br, &check_br_flag);
  1654. }
  1655. void assert_state::check_ce (int ce)
  1656. {
  1657. if (check_ce_flag)
  1658. check_value(val_ce, ce, "ce", file_ce, line_ce, &check_ce_flag);
  1659. }
  1660. class html_printer : public printer {
  1661. files file_list;
  1662. simple_output html;
  1663. int res;
  1664. int space_char_index;
  1665. int space_width;
  1666. int no_of_printed_pages;
  1667. int paper_length;
  1668. string sbuf;
  1669. int sbuf_start_hpos;
  1670. int sbuf_vpos;
  1671. int sbuf_end_hpos;
  1672. int sbuf_prev_hpos;
  1673. int sbuf_kern;
  1674. style sbuf_style;
  1675. int last_sbuf_length;
  1676. int overstrike_detected;
  1677. style output_style;
  1678. int output_hpos;
  1679. int output_vpos;
  1680. int output_vpos_max;
  1681. int output_draw_point_size;
  1682. int line_thickness;
  1683. int output_line_thickness;
  1684. unsigned char output_space_code;
  1685. char *inside_font_style;
  1686. int page_number;
  1687. title_desc title;
  1688. header_desc header;
  1689. int header_indent;
  1690. int supress_sub_sup;
  1691. int cutoff_heading;
  1692. page *page_contents;
  1693. html_text *current_paragraph;
  1694. html_indent *indent;
  1695. html_table *table;
  1696. int end_center;
  1697. int end_tempindent;
  1698. TAG_ALIGNMENT next_tag;
  1699. int fill_on;
  1700. int max_linelength;
  1701. int linelength;
  1702. int pageoffset;
  1703. int troff_indent;
  1704. int device_indent;
  1705. int temp_indent;
  1706. int pointsize;
  1707. int vertical_spacing;
  1708. int line_number;
  1709. color *background;
  1710. int seen_indent;
  1711. int next_indent;
  1712. int seen_pageoffset;
  1713. int next_pageoffset;
  1714. int seen_linelength;
  1715. int next_linelength;
  1716. int seen_center;
  1717. int next_center;
  1718. int seen_space;
  1719. int seen_break;
  1720. int current_column;
  1721. int row_space;
  1722. assert_state as;
  1723. void flush_sbuf ();
  1724. void set_style (const style &);
  1725. void set_space_code (unsigned char c);
  1726. void do_exec (char *, const environment *);
  1727. void do_import (char *, const environment *);
  1728. void do_def (char *, const environment *);
  1729. void do_mdef (char *, const environment *);
  1730. void do_file (char *, const environment *);
  1731. void set_line_thickness (const environment *);
  1732. void terminate_current_font (void);
  1733. void flush_font (void);
  1734. void add_to_sbuf (int index, const string &s);
  1735. void write_title (int in_head);
  1736. int sbuf_continuation (int index, const char *name, const environment *env, int w);
  1737. void flush_page (void);
  1738. void troff_tag (text_glob *g);
  1739. void flush_globs (void);
  1740. void emit_line (text_glob *g);
  1741. void emit_raw (text_glob *g);
  1742. void emit_html (text_glob *g);
  1743. void determine_space (text_glob *g);
  1744. void start_font (const char *name);
  1745. void end_font (const char *name);
  1746. int is_font_courier (font *f);
  1747. int is_line_start (int nf);
  1748. int is_courier_until_eol (void);
  1749. void start_size (int from, int to);
  1750. void do_font (text_glob *g);
  1751. void do_center (char *arg);
  1752. void do_check_center (void);
  1753. void do_break (void);
  1754. void do_space (char *arg);
  1755. void do_eol (void);
  1756. void do_eol_ce (void);
  1757. void do_title (void);
  1758. void do_fill (char *arg);
  1759. void do_heading (char *arg);
  1760. void write_header (void);
  1761. void determine_header_level (int level);
  1762. void do_linelength (char *arg);
  1763. void do_pageoffset (char *arg);
  1764. void do_indentation (char *arg);
  1765. void do_tempindent (char *arg);
  1766. void do_indentedparagraph (void);
  1767. void do_verticalspacing (char *arg);
  1768. void do_pointsize (char *arg);
  1769. void do_centered_image (void);
  1770. void do_left_image (void);
  1771. void do_right_image (void);
  1772. void do_auto_image (text_glob *g, const char *filename);
  1773. void do_links (void);
  1774. void do_flush (void);
  1775. void do_job_name (char *name);
  1776. void do_head (char *name);
  1777. void insert_split_file (void);
  1778. int is_in_middle (int left, int right);
  1779. void do_sup_or_sub (text_glob *g);
  1780. int start_subscript (text_glob *g);
  1781. int end_subscript (text_glob *g);
  1782. int start_superscript (text_glob *g);
  1783. int end_superscript (text_glob *g);
  1784. void outstanding_eol (int n);
  1785. int is_bold (font *f);
  1786. font *make_bold (font *f);
  1787. int overstrike (int index, const char *name, const environment *env, int w);
  1788. void do_body (void);
  1789. int next_horiz_pos (text_glob *g, int nf);
  1790. void lookahead_for_tables (void);
  1791. void insert_tab_te (void);
  1792. text_glob *insert_tab_ts (text_glob *where);
  1793. void insert_tab0_foreach_tab (void);
  1794. void insert_tab_0 (text_glob *where);
  1795. void do_indent (int in, int pageoff, int linelen);
  1796. void shutdown_table (void);
  1797. void do_tab_ts (text_glob *g);
  1798. void do_tab_te (void);
  1799. void do_col (char *s);
  1800. void do_tab (char *s);
  1801. void do_tab0 (void);
  1802. int calc_nf (text_glob *g, int nf);
  1803. void calc_po_in (text_glob *g, int nf);
  1804. void remove_tabs (void);
  1805. void remove_courier_tabs (void);
  1806. void update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g);
  1807. void add_table_end (const char *);
  1808. void do_file_components (void);
  1809. void write_navigation (const string &top, const string &prev,
  1810. const string &next, const string &current);
  1811. void emit_link (const string &to, const char *name);
  1812. int get_troff_indent (void);
  1813. void restore_troff_indent (void);
  1814. void handle_assertion (int minv, int minh, int maxv, int maxh, const char *s);
  1815. void handle_state_assertion (text_glob *g);
  1816. void do_end_para (text_glob *g);
  1817. int round_width (int x);
  1818. void handle_tag_within_title (text_glob *g);
  1819. void writeHeadMetaStyle (void);
  1820. // ADD HERE
  1821. public:
  1822. html_printer ();
  1823. ~html_printer ();
  1824. void set_char (int i, font *f, const environment *env, int w, const char *name);
  1825. void set_numbered_char(int num, const environment *env, int *widthp);
  1826. int set_char_and_width(const char *nm, const environment *env,
  1827. int *widthp, font **f);
  1828. void draw (int code, int *p, int np, const environment *env);
  1829. void begin_page (int);
  1830. void end_page (int);
  1831. void special (char *arg, const environment *env, char type);
  1832. void devtag (char *arg, const environment *env, char type);
  1833. font *make_font (const char *);
  1834. void end_of_line ();
  1835. };
  1836. printer *make_printer()
  1837. {
  1838. return new html_printer;
  1839. }
  1840. static void usage(FILE *stream);
  1841. void html_printer::set_style(const style &sty)
  1842. {
  1843. const char *fontname = sty.f->get_name();
  1844. if (fontname == NULL)
  1845. fatal("no internalname specified for font");
  1846. #if 0
  1847. change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
  1848. #endif
  1849. }
  1850. /*
  1851. * is_bold - returns TRUE if font, f, is bold.
  1852. */
  1853. int html_printer::is_bold (font *f)
  1854. {
  1855. const char *fontname = f->get_name();
  1856. return (strcmp(fontname, "B") == 0) || (strcmp(fontname, "BI") == 0);
  1857. }
  1858. /*
  1859. * make_bold - if a bold font of, f, exists then return it.
  1860. */
  1861. font *html_printer::make_bold (font *f)
  1862. {
  1863. const char *fontname = f->get_name();
  1864. if (strcmp(fontname, "B") == 0)
  1865. return f;
  1866. if (strcmp(fontname, "I") == 0)
  1867. return font::load_font("BI");
  1868. if (strcmp(fontname, "BI") == 0)
  1869. return f;
  1870. return NULL;
  1871. }
  1872. void html_printer::end_of_line()
  1873. {
  1874. flush_sbuf();
  1875. line_number++;
  1876. }
  1877. /*
  1878. * emit_line - writes out a horizontal rule.
  1879. */
  1880. void html_printer::emit_line (text_glob *)
  1881. {
  1882. // --fixme-- needs to know the length in percentage
  1883. html.put_string("<hr>");
  1884. }
  1885. /*
  1886. * restore_troff_indent - is called when we have temporarily shutdown
  1887. * indentation (typically done when we have
  1888. * centered an image).
  1889. */
  1890. void html_printer::restore_troff_indent (void)
  1891. {
  1892. troff_indent = next_indent;
  1893. if (troff_indent > 0) {
  1894. /*
  1895. * force device indentation
  1896. */
  1897. device_indent = 0;
  1898. do_indent(get_troff_indent(), pageoffset, linelength);
  1899. }
  1900. }
  1901. /*
  1902. * emit_raw - writes the raw html information directly to the device.
  1903. */
  1904. void html_printer::emit_raw (text_glob *g)
  1905. {
  1906. do_font(g);
  1907. if (next_tag == INLINE) {
  1908. determine_space(g);
  1909. current_paragraph->do_emittext(g->text_string, g->text_length);
  1910. } else {
  1911. int space = current_paragraph->retrieve_para_space() || seen_space;
  1912. current_paragraph->done_para();
  1913. shutdown_table();
  1914. switch (next_tag) {
  1915. case CENTERED:
  1916. current_paragraph->do_para("align=center", space);
  1917. break;
  1918. case LEFT:
  1919. current_paragraph->do_para(&html, "align=left", get_troff_indent(), pageoffset, linelength, space);
  1920. break;
  1921. case RIGHT:
  1922. current_paragraph->do_para(&html, "align=right", get_troff_indent(), pageoffset, linelength, space);
  1923. break;
  1924. default:
  1925. fatal("unknown enumeration");
  1926. }
  1927. current_paragraph->do_emittext(g->text_string, g->text_length);
  1928. current_paragraph->done_para();
  1929. next_tag = INLINE;
  1930. supress_sub_sup = TRUE;
  1931. seen_space = FALSE;
  1932. restore_troff_indent();
  1933. }
  1934. }
  1935. /*
  1936. * handle_tag_within_title - handle a limited number of tags within
  1937. * the context of a table. Those tags which
  1938. * set values rather than generate spaces
  1939. * and paragraphs.
  1940. */
  1941. void html_printer::handle_tag_within_title (text_glob *g)
  1942. {
  1943. if (g->is_in() || g->is_ti() || g->is_po() || g->is_ce() || g->is_ll()
  1944. || g->is_fi() || g->is_nf())
  1945. troff_tag(g);
  1946. }
  1947. /*
  1948. * do_center - handle the .ce commands from troff.
  1949. */
  1950. void html_printer::do_center (char *arg)
  1951. {
  1952. next_center = atoi(arg);
  1953. seen_center = TRUE;
  1954. }
  1955. /*
  1956. * do_centered_image - set a flag such that the next devtag is
  1957. * placed inside a centered paragraph.
  1958. */
  1959. void html_printer::do_centered_image (void)
  1960. {
  1961. next_tag = CENTERED;
  1962. }
  1963. /*
  1964. * do_right_image - set a flag such that the next devtag is
  1965. * placed inside a right aligned paragraph.
  1966. */
  1967. void html_printer::do_right_image (void)
  1968. {
  1969. next_tag = RIGHT;
  1970. }
  1971. /*
  1972. * do_left_image - set a flag such that the next devtag is
  1973. * placed inside a left aligned paragraph.
  1974. */
  1975. void html_printer::do_left_image (void)
  1976. {
  1977. next_tag = LEFT;
  1978. }
  1979. /*
  1980. * exists - returns TRUE if filename exists.
  1981. */
  1982. static int exists (const char *filename)
  1983. {
  1984. FILE *fp = fopen(filename, "r");
  1985. if (fp == 0) {
  1986. return( FALSE );
  1987. } else {
  1988. fclose(fp);
  1989. return( TRUE );
  1990. }
  1991. }
  1992. /*
  1993. * generate_img_src - returns a html image tag for the filename
  1994. * providing that the image exists.
  1995. */
  1996. static string &generate_img_src (const char *filename)
  1997. {
  1998. string *s = new string("");
  1999. while (filename && (filename[0] == ' ')) {
  2000. filename++;
  2001. }
  2002. if (exists(filename))
  2003. *s += string("<img src=\"") + filename + "\" "
  2004. + "alt=\"Image " + filename + "\">";
  2005. return *s;
  2006. }
  2007. /*
  2008. * do_auto_image - tests whether the image, indicated by filename,
  2009. * is present, if so then it emits an html image tag.
  2010. * An image tag may be passed through from pic, eqn
  2011. * but the corresponding image might not be created.
  2012. * Consider .EQ delim $$ .EN or an empty .PS .PE.
  2013. */
  2014. void html_printer::do_auto_image (text_glob *g, const char *filename)
  2015. {
  2016. string buffer = generate_img_src(filename);
  2017. if (! buffer.empty()) {
  2018. /*
  2019. * utilize emit_raw by creating a new text_glob.
  2020. */
  2021. text_glob h = *g;
  2022. h.text_string = buffer.contents();
  2023. h.text_length = buffer.length();
  2024. emit_raw(&h);
  2025. } else
  2026. next_tag = INLINE;
  2027. }
  2028. /*
  2029. * outstanding_eol - call do_eol, n, times.
  2030. */
  2031. void html_printer::outstanding_eol (int n)
  2032. {
  2033. while (n > 0) {
  2034. do_eol();
  2035. n--;
  2036. }
  2037. }
  2038. /*
  2039. * do_title - handle the .tl commands from troff.
  2040. */
  2041. void html_printer::do_title (void)
  2042. {
  2043. text_glob *t;
  2044. int removed_from_head;
  2045. if (page_number == 1) {
  2046. int found_title_start = FALSE;
  2047. if (! page_contents->glyphs.is_empty()) {
  2048. page_contents->glyphs.sub_move_right(); /* move onto next word */
  2049. do {
  2050. t = page_contents->glyphs.get_data();
  2051. removed_from_head = FALSE;
  2052. if (t->is_auto_img()) {
  2053. string img = generate_img_src((char *)(t->text_string + 20));
  2054. if (! img.empty()) {
  2055. if (found_title_start)
  2056. title.text += " ";
  2057. found_title_start = TRUE;
  2058. title.has_been_found = TRUE;
  2059. title.text += img;
  2060. }
  2061. page_contents->glyphs.sub_move_right(); /* move onto next word */
  2062. removed_from_head = ((!page_contents->glyphs.is_empty()) &&
  2063. (page_contents->glyphs.is_equal_to_head()));
  2064. } else if (t->is_eo_tl()) {
  2065. /* end of title found
  2066. */
  2067. title.has_been_found = TRUE;
  2068. return;
  2069. } else if (t->is_a_tag()) {
  2070. handle_tag_within_title(t);
  2071. page_contents->glyphs.sub_move_right(); /* move onto next word */
  2072. removed_from_head = ((!page_contents->glyphs.is_empty()) &&
  2073. (page_contents->glyphs.is_equal_to_head()));
  2074. } else if (found_title_start) {
  2075. title.text += " " + string(t->text_string, t->text_length);
  2076. page_contents->glyphs.sub_move_right(); /* move onto next word */
  2077. removed_from_head = ((!page_contents->glyphs.is_empty()) &&
  2078. (page_contents->glyphs.is_equal_to_head()));
  2079. } else {
  2080. title.text += string(t->text_string, t->text_length);
  2081. found_title_start = TRUE;
  2082. title.has_been_found = TRUE;
  2083. page_contents->glyphs.sub_move_right(); /* move onto next word */
  2084. removed_from_head = ((!page_contents->glyphs.is_empty()) &&
  2085. (page_contents->glyphs.is_equal_to_head()));
  2086. }
  2087. } while ((! page_contents->glyphs.is_equal_to_head()) ||
  2088. (removed_from_head));
  2089. }
  2090. }
  2091. }
  2092. void html_printer::write_header (void)
  2093. {
  2094. if (! header.header_buffer.empty()) {
  2095. int space = current_paragraph->retrieve_para_space() || seen_space;
  2096. if (header.header_level > 7) {
  2097. header.header_level = 7;
  2098. }
  2099. // firstly we must terminate any font and type faces
  2100. current_paragraph->done_para();
  2101. supress_sub_sup = TRUE;
  2102. if (cutoff_heading+2 > header.header_level) {
  2103. // now we save the header so we can issue a list of links
  2104. header.no_of_headings++;
  2105. style st;
  2106. text_glob *h=new text_glob();
  2107. h->text_glob_html(&st,
  2108. header.headings.add_string(header.header_buffer),
  2109. header.header_buffer.length(),
  2110. header.no_of_headings, header.header_level,
  2111. header.no_of_headings, header.header_level);
  2112. header.headers.add(h,
  2113. header.no_of_headings,
  2114. header.no_of_headings, header.no_of_headings,
  2115. header.no_of_headings, header.no_of_headings); // and add this header to the header list
  2116. // lastly we generate a tag
  2117. html.nl().nl().put_string("<a name=\"");
  2118. if (simple_anchors) {
  2119. string buffer(ANCHOR_TEMPLATE);
  2120. buffer += as_string(header.no_of_headings);
  2121. buffer += '\0';
  2122. html.put_string(buffer.contents());
  2123. } else {
  2124. html.put_string(header.header_buffer);
  2125. }
  2126. html.put_string("\"></a>").nl();
  2127. }
  2128. if (manufacture_headings) {
  2129. // line break before a header
  2130. if (!current_paragraph->emitted_text())
  2131. current_paragraph->do_space();
  2132. // user wants manufactured headings which look better than <Hn></Hn>
  2133. if (header.header_level<4) {
  2134. html.put_string("<b><font size=\"+1\">");
  2135. html.put_string(header.header_buffer);
  2136. html.put_string("</font></b>").nl();
  2137. }
  2138. else {
  2139. html.put_string("<b>");
  2140. html.put_string(header.header_buffer);
  2141. html.put_string("</b>").nl();
  2142. }
  2143. }
  2144. else {
  2145. // and now we issue the real header
  2146. html.put_string("<h");
  2147. html.put_number(header.header_level);
  2148. html.put_string(">");
  2149. html.put_string(header.header_buffer);
  2150. html.put_string("</h");
  2151. html.put_number(header.header_level);
  2152. html.put_string(">").nl();
  2153. }
  2154. /* and now we save the file name in which this header will occur */
  2155. style st; // fake style to enable us to use the list data structure
  2156. text_glob *h=new text_glob();
  2157. h->text_glob_html(&st,
  2158. header.headings.add_string(file_list.file_name()),
  2159. file_list.file_name().length(),
  2160. header.no_of_headings, header.header_level,
  2161. header.no_of_headings, header.header_level);
  2162. header.header_filename.add(h,
  2163. header.no_of_headings,
  2164. header.no_of_headings, header.no_of_headings,
  2165. header.no_of_headings, header.no_of_headings);
  2166. current_paragraph->do_para(&html, "", get_troff_indent(), pageoffset, linelength, space);
  2167. }
  2168. }
  2169. void html_printer::determine_header_level (int level)
  2170. {
  2171. if (level == 0) {
  2172. int i;
  2173. for (i=0; ((i<header.header_buffer.length())
  2174. && ((header.header_buffer[i] == '.')
  2175. || is_digit(header.header_buffer[i]))) ; i++) {
  2176. if (header.header_buffer[i] == '.') {
  2177. level++;
  2178. }
  2179. }
  2180. }
  2181. header.header_level = level+1;
  2182. if (header.header_level >= 2 && header.header_level <= split_level) {
  2183. header.no_of_level_one_headings++;
  2184. insert_split_file();
  2185. }
  2186. }
  2187. /*
  2188. * do_heading - handle the .SH and .NH and equivalent commands from troff.
  2189. */
  2190. void html_printer::do_heading (char *arg)
  2191. {
  2192. text_glob *g;
  2193. int level=atoi(arg);
  2194. int horiz;
  2195. header.header_buffer.clear();
  2196. page_contents->glyphs.move_right();
  2197. if (! page_contents->glyphs.is_equal_to_head()) {
  2198. g = page_contents->glyphs.get_data();
  2199. horiz = g->minh;
  2200. do {
  2201. if (g->is_auto_img()) {
  2202. string img=generate_img_src((char *)(g->text_string + 20));
  2203. if (! img.empty()) {
  2204. simple_anchors = TRUE; // we cannot use full heading anchors with images
  2205. if (horiz < g->minh)
  2206. header.header_buffer += " ";
  2207. header.header_buffer += img;
  2208. }
  2209. }
  2210. else if (g->is_in() || g->is_ti() || g->is_po() || g->is_ce() || g->is_ll())
  2211. troff_tag(g);
  2212. else if (g->is_fi())
  2213. fill_on = 1;
  2214. else if (g->is_nf())
  2215. fill_on = 0;
  2216. else if (! (g->is_a_line() || g->is_a_tag())) {
  2217. /*
  2218. * we ignore the other tag commands when constructing a heading
  2219. */
  2220. if (horiz < g->minh)
  2221. header.header_buffer += " ";
  2222. horiz = g->maxh;
  2223. header.header_buffer += string(g->text_string, g->text_length);
  2224. }
  2225. page_contents->glyphs.move_right();
  2226. g = page_contents->glyphs.get_data();
  2227. } while ((! page_contents->glyphs.is_equal_to_head()) &&
  2228. (! g->is_eo_h()));
  2229. }
  2230. determine_header_level(level);
  2231. write_header();
  2232. // finally set the output to neutral for after the header
  2233. g = page_contents->glyphs.get_data();
  2234. page_contents->glyphs.move_left(); // so that next time we use old g
  2235. }
  2236. /*
  2237. * is_courier_until_eol - returns TRUE if we can see a whole line which is courier
  2238. */
  2239. int html_printer::is_courier_until_eol (void)
  2240. {
  2241. text_glob *orig = page_contents->glyphs.get_data();
  2242. int result = TRUE;
  2243. text_glob *g;
  2244. if (! page_contents->glyphs.is_equal_to_tail()) {
  2245. page_contents->glyphs.move_right();
  2246. do {
  2247. g = page_contents->glyphs.get_data();
  2248. if (! g->is_a_tag() && (! is_font_courier(g->text_style.f)))
  2249. result = FALSE;
  2250. page_contents->glyphs.move_right();
  2251. } while (result &&
  2252. (! page_contents->glyphs.is_equal_to_head()) &&
  2253. (! g->is_fi()) && (! g->is_eol()));
  2254. /*
  2255. * now restore our previous position.
  2256. */
  2257. while (page_contents->glyphs.get_data() != orig)
  2258. page_contents->glyphs.move_left();
  2259. }
  2260. return result;
  2261. }
  2262. /*
  2263. * do_linelength - handle the .ll command from troff.
  2264. */
  2265. void html_printer::do_linelength (char *arg)
  2266. {
  2267. if (max_linelength == -1)
  2268. max_linelength = atoi(arg);
  2269. next_linelength = atoi(arg);
  2270. seen_linelength = TRUE;
  2271. }
  2272. /*
  2273. * do_pageoffset - handle the .po command from troff.
  2274. */
  2275. void html_printer::do_pageoffset (char *arg)
  2276. {
  2277. next_pageoffset = atoi(arg);
  2278. seen_pageoffset = TRUE;
  2279. }
  2280. /*
  2281. * get_troff_indent - returns the indent value.
  2282. */
  2283. int html_printer::get_troff_indent (void)
  2284. {
  2285. if (end_tempindent > 0)
  2286. return temp_indent;
  2287. else
  2288. return troff_indent;
  2289. }
  2290. /*
  2291. * do_indentation - handle the .in command from troff.
  2292. */
  2293. void html_printer::do_indentation (char *arg)
  2294. {
  2295. next_indent = atoi(arg);
  2296. seen_indent = TRUE;
  2297. }
  2298. /*
  2299. * do_tempindent - handle the .ti command from troff.
  2300. */
  2301. void html_printer::do_tempindent (char *arg)
  2302. {
  2303. if (fill_on) {
  2304. /*
  2305. * we set the end_tempindent to 2 as the first .br
  2306. * activates the .ti and the second terminates it.
  2307. */
  2308. end_tempindent = 2;
  2309. temp_indent = atoi(arg);
  2310. }
  2311. }
  2312. /*
  2313. * shutdown_table - shuts down the current table.
  2314. */
  2315. void html_printer::shutdown_table (void)
  2316. {
  2317. if (table != NULL) {
  2318. current_paragraph->done_para();
  2319. table->emit_finish_table();
  2320. // dont delete this table as it will be deleted when we destroy the text_glob
  2321. table = NULL;
  2322. }
  2323. }
  2324. /*
  2325. * do_indent - remember the indent parameters and if
  2326. * indent is > pageoff and indent has changed
  2327. * then we start a html table to implement the indentation.
  2328. */
  2329. void html_printer::do_indent (int in, int pageoff, int linelen)
  2330. {
  2331. if ((device_indent != -1) &&
  2332. (pageoffset+device_indent != in+pageoff)) {
  2333. int space = current_paragraph->retrieve_para_space() || seen_space;
  2334. current_paragraph->done_para();
  2335. device_indent = in;
  2336. pageoffset = pageoff;
  2337. if (linelen <= max_linelength)
  2338. linelength = linelen;
  2339. current_paragraph->do_para(&html, "", device_indent,
  2340. pageoffset, max_linelength, space);
  2341. }
  2342. }
  2343. /*
  2344. * do_verticalspacing - handle the .vs command from troff.
  2345. */
  2346. void html_printer::do_verticalspacing (char *arg)
  2347. {
  2348. vertical_spacing = atoi(arg);
  2349. }
  2350. /*
  2351. * do_pointsize - handle the .ps command from troff.
  2352. */
  2353. void html_printer::do_pointsize (char *arg)
  2354. {
  2355. /*
  2356. * firstly check to see whether this point size is really associated with a .tl tag
  2357. */
  2358. if (! page_contents->glyphs.is_empty()) {
  2359. text_glob *g = page_contents->glyphs.get_data();
  2360. text_glob *t = page_contents->glyphs.get_data();
  2361. while (t->is_a_tag() && (! page_contents->glyphs.is_equal_to_head())) {
  2362. if (t->is_tl()) {
  2363. /*
  2364. * found title therefore ignore this .ps tag
  2365. */
  2366. while (t != g) {
  2367. page_contents->glyphs.move_left();
  2368. t = page_contents->glyphs.get_data();
  2369. }
  2370. return;
  2371. }
  2372. page_contents->glyphs.move_right();
  2373. t = page_contents->glyphs.get_data();
  2374. }
  2375. /*
  2376. * move back to original position
  2377. */
  2378. while (t != g) {
  2379. page_contents->glyphs.move_left();
  2380. t = page_contents->glyphs.get_data();
  2381. }
  2382. /*
  2383. * collect legal pointsize
  2384. */
  2385. pointsize = atoi(arg);
  2386. }
  2387. }
  2388. /*
  2389. * do_fill - records whether troff has requested that text be filled.
  2390. */
  2391. void html_printer::do_fill (char *arg)
  2392. {
  2393. int on = atoi(arg);
  2394. output_hpos = get_troff_indent()+pageoffset;
  2395. supress_sub_sup = TRUE;
  2396. if (fill_on != on) {
  2397. if (on)
  2398. current_paragraph->do_para("", seen_space);
  2399. fill_on = on;
  2400. }
  2401. }
  2402. /*
  2403. * do_eol - handle the end of line
  2404. */
  2405. void html_printer::do_eol (void)
  2406. {
  2407. if (! fill_on) {
  2408. if (current_paragraph->ever_emitted_text()) {
  2409. current_paragraph->do_newline();
  2410. current_paragraph->do_break();
  2411. }
  2412. }
  2413. output_hpos = get_troff_indent()+pageoffset;
  2414. }
  2415. /*
  2416. * do_check_center - checks to see whether we have seen a `.ce' tag
  2417. * during the previous line.
  2418. */
  2419. void html_printer::do_check_center(void)
  2420. {
  2421. if (seen_center) {
  2422. seen_center = FALSE;
  2423. if (next_center > 0) {
  2424. if (end_center == 0) {
  2425. int space = current_paragraph->retrieve_para_space() || seen_space;
  2426. current_paragraph->done_para();
  2427. supress_sub_sup = TRUE;
  2428. current_paragraph->do_para("align=center", space);
  2429. } else
  2430. if (strcmp("align=center",
  2431. current_paragraph->get_alignment()) != 0) {
  2432. /*
  2433. * different alignment, so shutdown paragraph and open
  2434. * a new one.
  2435. */
  2436. int space = current_paragraph->retrieve_para_space() || seen_space;
  2437. current_paragraph->done_para();
  2438. supress_sub_sup = TRUE;
  2439. current_paragraph->do_para("align=center", space);
  2440. } else
  2441. /*
  2442. * same alignment, if we have emitted text then issue a break.
  2443. */
  2444. if (current_paragraph->emitted_text())
  2445. current_paragraph->do_break();
  2446. } else
  2447. /*
  2448. * next_center == 0
  2449. */
  2450. if (end_center > 0) {
  2451. seen_space = seen_space || current_paragraph->retrieve_para_space();
  2452. current_paragraph->done_para();
  2453. supress_sub_sup = TRUE;
  2454. current_paragraph->do_para("", seen_space);
  2455. }
  2456. end_center = next_center;
  2457. }
  2458. }
  2459. /*
  2460. * do_eol_ce - handle end of line specifically for a .ce
  2461. */
  2462. void html_printer::do_eol_ce (void)
  2463. {
  2464. if (end_center > 0) {
  2465. if (end_center > 1)
  2466. if (current_paragraph->emitted_text())
  2467. current_paragraph->do_break();
  2468. end_center--;
  2469. if (end_center == 0) {
  2470. current_paragraph->done_para();
  2471. supress_sub_sup = TRUE;
  2472. }
  2473. }
  2474. }
  2475. /*
  2476. * do_flush - flushes all output and tags.
  2477. */
  2478. void html_printer::do_flush (void)
  2479. {
  2480. current_paragraph->done_para();
  2481. }
  2482. /*
  2483. * do_links - moves onto a new temporary file and sets auto_links to FALSE.
  2484. */
  2485. void html_printer::do_links (void)
  2486. {
  2487. html.end_line(); // flush line
  2488. auto_links = FALSE; /* from now on only emit under user request */
  2489. file_list.add_new_file(xtmpfile());
  2490. file_list.set_links_required();
  2491. html.set_file(file_list.get_file());
  2492. }
  2493. /*
  2494. * insert_split_file -
  2495. */
  2496. void html_printer::insert_split_file (void)
  2497. {
  2498. if (multiple_files) {
  2499. current_paragraph->done_para(); // flush paragraph
  2500. html.end_line(); // flush line
  2501. html.set_file(file_list.get_file()); // flush current file
  2502. file_list.add_new_file(xtmpfile());
  2503. string split_file = job_name;
  2504. split_file += string("-");
  2505. split_file += as_string(header.no_of_level_one_headings);
  2506. split_file += string(".html");
  2507. split_file += '\0';
  2508. file_list.set_file_name(split_file);
  2509. html.set_file(file_list.get_file());
  2510. }
  2511. }
  2512. /*
  2513. * do_job_name - assigns the job_name to name.
  2514. */
  2515. void html_printer::do_job_name (char *name)
  2516. {
  2517. if (! multiple_files) {
  2518. multiple_files = TRUE;
  2519. while (name != NULL && (*name != (char)0) && (*name == ' '))
  2520. name++;
  2521. job_name = name;
  2522. }
  2523. }
  2524. /*
  2525. * do_head - adds a string to head_info which is to be included into
  2526. * the <head> </head> section of the html document.
  2527. */
  2528. void html_printer::do_head (char *name)
  2529. {
  2530. head_info += string(name);
  2531. head_info += '\n';
  2532. }
  2533. /*
  2534. * do_break - handles the ".br" request and also
  2535. * undoes an outstanding ".ti" command
  2536. * and calls indent if the indentation
  2537. * related registers have changed.
  2538. */
  2539. void html_printer::do_break (void)
  2540. {
  2541. int seen_temp_indent = FALSE;
  2542. current_paragraph->do_break();
  2543. if (end_tempindent > 0) {
  2544. end_tempindent--;
  2545. if (end_tempindent > 0)
  2546. seen_temp_indent = TRUE;
  2547. }
  2548. if (seen_indent || seen_pageoffset || seen_linelength || seen_temp_indent) {
  2549. if (seen_indent && (! seen_temp_indent))
  2550. troff_indent = next_indent;
  2551. if (! seen_pageoffset)
  2552. next_pageoffset = pageoffset;
  2553. if (! seen_linelength)
  2554. next_linelength = linelength;
  2555. do_indent(get_troff_indent(), next_pageoffset, next_linelength);
  2556. }
  2557. seen_indent = seen_temp_indent;
  2558. seen_linelength = FALSE;
  2559. seen_pageoffset = FALSE;
  2560. do_check_center();
  2561. output_hpos = get_troff_indent()+pageoffset;
  2562. supress_sub_sup = TRUE;
  2563. }
  2564. void html_printer::do_space (char *arg)
  2565. {
  2566. int n = atoi(arg);
  2567. seen_space = atoi(arg);
  2568. as.check_sp(seen_space);
  2569. #if 0
  2570. if (n>0 && table)
  2571. table->set_space(TRUE);
  2572. #endif
  2573. while (n>0) {
  2574. current_paragraph->do_space();
  2575. n--;
  2576. }
  2577. supress_sub_sup = TRUE;
  2578. }
  2579. /*
  2580. * do_tab_ts - start a table, which will have already been defined.
  2581. */
  2582. void html_printer::do_tab_ts (text_glob *g)
  2583. {
  2584. html_table *t = g->get_table();
  2585. if (t != NULL) {
  2586. current_column = 0;
  2587. current_paragraph->done_pre();
  2588. current_paragraph->done_para();
  2589. current_paragraph->remove_para_space();
  2590. #if defined(DEBUG_TABLES)
  2591. html.simple_comment("TABS");
  2592. #endif
  2593. t->set_linelength(max_linelength);
  2594. t->add_indent(pageoffset);
  2595. #if 0
  2596. t->emit_table_header(seen_space);
  2597. #else
  2598. t->emit_table_header(FALSE);
  2599. row_space = current_paragraph->retrieve_para_space() || seen_space;
  2600. seen_space = FALSE;
  2601. #endif
  2602. }
  2603. table = t;
  2604. }
  2605. /*
  2606. * do_tab_te - finish a table.
  2607. */
  2608. void html_printer::do_tab_te (void)
  2609. {
  2610. if (table) {
  2611. current_paragraph->done_para();
  2612. current_paragraph->remove_para_space();
  2613. table->emit_finish_table();
  2614. }
  2615. table = NULL;
  2616. restore_troff_indent();
  2617. }
  2618. /*
  2619. * do_tab - handle the "devtag:tab" tag
  2620. */
  2621. void html_printer::do_tab (char *s)
  2622. {
  2623. if (table) {
  2624. while (isspace(*s))
  2625. s++;
  2626. s++;
  2627. int col = table->find_column(atoi(s) + pageoffset + get_troff_indent());
  2628. if (col > 0) {
  2629. current_paragraph->done_para();
  2630. table->emit_col(col);
  2631. }
  2632. }
  2633. }
  2634. /*
  2635. * do_tab0 - handle the "devtag:tab0" tag
  2636. */
  2637. void html_printer::do_tab0 (void)
  2638. {
  2639. if (table) {
  2640. int col = table->find_column(pageoffset+get_troff_indent());
  2641. if (col > 0) {
  2642. current_paragraph->done_para();
  2643. table->emit_col(col);
  2644. }
  2645. }
  2646. }
  2647. /*
  2648. * do_col - start column, s.
  2649. */
  2650. void html_printer::do_col (char *s)
  2651. {
  2652. if (table) {
  2653. if (atoi(s) < current_column)
  2654. row_space = seen_space;
  2655. current_column = atoi(s);
  2656. current_paragraph->done_para();
  2657. table->emit_col(current_column);
  2658. current_paragraph->do_para("", row_space);
  2659. }
  2660. }
  2661. /*
  2662. * troff_tag - processes the troff tag and manipulates the troff
  2663. * state machine.
  2664. */
  2665. void html_printer::troff_tag (text_glob *g)
  2666. {
  2667. /*
  2668. * firstly skip over devtag:
  2669. */
  2670. char *t=(char *)g->text_string+strlen("devtag:");
  2671. if (strncmp(g->text_string, "html</p>:", strlen("html</p>:")) == 0) {
  2672. do_end_para(g);
  2673. } else if (g->is_eol()) {
  2674. do_eol();
  2675. } else if (g->is_eol_ce()) {
  2676. do_eol_ce();
  2677. } else if (strncmp(t, ".sp", 3) == 0) {
  2678. char *a = (char *)t+3;
  2679. do_space(a);
  2680. } else if (strncmp(t, ".br", 3) == 0) {
  2681. seen_break = 1;
  2682. as.check_br(1);
  2683. do_break();
  2684. } else if (strcmp(t, ".centered-image") == 0) {
  2685. do_centered_image();
  2686. } else if (strcmp(t, ".right-image") == 0) {
  2687. do_right_image();
  2688. } else if (strcmp(t, ".left-image") == 0) {
  2689. do_left_image();
  2690. } else if (strncmp(t, ".auto-image", 11) == 0) {
  2691. char *a = (char *)t+11;
  2692. do_auto_image(g, a);
  2693. } else if (strncmp(t, ".ce", 3) == 0) {
  2694. char *a = (char *)t+3;
  2695. supress_sub_sup = TRUE;
  2696. do_center(a);
  2697. } else if (g->is_tl()) {
  2698. supress_sub_sup = TRUE;
  2699. title.with_h1 = TRUE;
  2700. do_title();
  2701. } else if (strncmp(t, ".html-tl", 8) == 0) {
  2702. supress_sub_sup = TRUE;
  2703. title.with_h1 = FALSE;
  2704. do_title();
  2705. } else if (strncmp(t, ".fi", 3) == 0) {
  2706. char *a = (char *)t+3;
  2707. do_fill(a);
  2708. } else if ((strncmp(t, ".SH", 3) == 0) || (strncmp(t, ".NH", 3) == 0)) {
  2709. char *a = (char *)t+3;
  2710. do_heading(a);
  2711. } else if (strncmp(t, ".ll", 3) == 0) {
  2712. char *a = (char *)t+3;
  2713. do_linelength(a);
  2714. } else if (strncmp(t, ".po", 3) == 0) {
  2715. char *a = (char *)t+3;
  2716. do_pageoffset(a);
  2717. } else if (strncmp(t, ".in", 3) == 0) {
  2718. char *a = (char *)t+3;
  2719. do_indentation(a);
  2720. } else if (strncmp(t, ".ti", 3) == 0) {
  2721. char *a = (char *)t+3;
  2722. do_tempindent(a);
  2723. } else if (strncmp(t, ".vs", 3) == 0) {
  2724. char *a = (char *)t+3;
  2725. do_verticalspacing(a);
  2726. } else if (strncmp(t, ".ps", 3) == 0) {
  2727. char *a = (char *)t+3;
  2728. do_pointsize(a);
  2729. } else if (strcmp(t, ".links") == 0) {
  2730. do_links();
  2731. } else if (strncmp(t, ".job-name", 9) == 0) {
  2732. char *a = (char *)t+9;
  2733. do_job_name(a);
  2734. } else if (strncmp(t, ".head", 5) == 0) {
  2735. char *a = (char *)t+5;
  2736. do_head(a);
  2737. } else if (strcmp(t, ".no-auto-rule") == 0) {
  2738. auto_rule = FALSE;
  2739. } else if (strcmp(t, ".tab-ts") == 0) {
  2740. do_tab_ts(g);
  2741. } else if (strcmp(t, ".tab-te") == 0) {
  2742. do_tab_te();
  2743. } else if (strncmp(t, ".col ", 5) == 0) {
  2744. char *a = (char *)t+4;
  2745. do_col(a);
  2746. } else if (strncmp(t, "tab ", 4) == 0) {
  2747. char *a = (char *)t+3;
  2748. do_tab(a);
  2749. } else if (strncmp(t, "tab0", 4) == 0) {
  2750. do_tab0();
  2751. }
  2752. }
  2753. /*
  2754. * is_in_middle - returns TRUE if the positions left..right are in the center of the page.
  2755. */
  2756. int html_printer::is_in_middle (int left, int right)
  2757. {
  2758. return( abs(abs(left-pageoffset) - abs(pageoffset+linelength-right))
  2759. <= CENTER_TOLERANCE );
  2760. }
  2761. /*
  2762. * flush_globs - runs through the text glob list and emits html.
  2763. */
  2764. void html_printer::flush_globs (void)
  2765. {
  2766. text_glob *g;
  2767. if (! page_contents->glyphs.is_empty()) {
  2768. page_contents->glyphs.start_from_head();
  2769. do {
  2770. g = page_contents->glyphs.get_data();
  2771. #if 0
  2772. fprintf(stderr, "[%s:%d:%d:%d:%d]",
  2773. g->text_string, g->minv, g->minh, g->maxv, g->maxh) ;
  2774. fflush(stderr);
  2775. #endif
  2776. handle_state_assertion(g);
  2777. if (strcmp(g->text_string, "XXXXXXX") == 0)
  2778. stop();
  2779. if (g->is_a_tag())
  2780. troff_tag(g);
  2781. else if (g->is_a_line())
  2782. emit_line(g);
  2783. else {
  2784. as.check_sp(seen_space);
  2785. as.check_br(seen_break);
  2786. seen_break = 0;
  2787. seen_space = 0;
  2788. emit_html(g);
  2789. }
  2790. as.check_fi(fill_on);
  2791. as.check_ce(end_center);
  2792. /*
  2793. * after processing the title (and removing it) the glyph list might be empty
  2794. */
  2795. if (! page_contents->glyphs.is_empty()) {
  2796. page_contents->glyphs.move_right();
  2797. }
  2798. } while (! page_contents->glyphs.is_equal_to_head());
  2799. }
  2800. }
  2801. /*
  2802. * calc_nf - calculates the _no_ format flag, given the
  2803. * text glob, g.
  2804. */
  2805. int html_printer::calc_nf (text_glob *g, int nf)
  2806. {
  2807. if (g != NULL) {
  2808. if (g->is_fi()) {
  2809. as.check_fi(TRUE);
  2810. return FALSE;
  2811. }
  2812. if (g->is_nf()) {
  2813. as.check_fi(FALSE);
  2814. return TRUE;
  2815. }
  2816. }
  2817. as.check_fi(! nf);
  2818. return nf;
  2819. }
  2820. /*
  2821. * calc_po_in - calculates the, in, po, registers
  2822. */
  2823. void html_printer::calc_po_in (text_glob *g, int nf)
  2824. {
  2825. if (g->is_in())
  2826. troff_indent = g->get_arg();
  2827. else if (g->is_po())
  2828. pageoffset = g->get_arg();
  2829. else if (g->is_ti()) {
  2830. temp_indent = g->get_arg();
  2831. end_tempindent = 2;
  2832. } else if (g->is_br() || (nf && g->is_eol())) {
  2833. if (end_tempindent > 0)
  2834. end_tempindent--;
  2835. }
  2836. }
  2837. /*
  2838. * next_horiz_pos - returns the next horiz position.
  2839. * -1 is returned if it doesn't exist.
  2840. */
  2841. int html_printer::next_horiz_pos (text_glob *g, int nf)
  2842. {
  2843. int next = -1;
  2844. if ((g != NULL) && (g->is_br() || (nf && g->is_eol())))
  2845. if (! page_contents->glyphs.is_empty()) {
  2846. page_contents->glyphs.move_right_get_data();
  2847. if (g == NULL) {
  2848. page_contents->glyphs.start_from_head();
  2849. as.reset();
  2850. }
  2851. else {
  2852. next = g->minh;
  2853. page_contents->glyphs.move_left();
  2854. }
  2855. }
  2856. return next;
  2857. }
  2858. /*
  2859. * insert_tab_ts - inserts a tab-ts before, where.
  2860. */
  2861. text_glob *html_printer::insert_tab_ts (text_glob *where)
  2862. {
  2863. text_glob *start_of_table;
  2864. text_glob *old_pos = page_contents->glyphs.get_data();
  2865. page_contents->glyphs.move_to(where);
  2866. page_contents->glyphs.move_left();
  2867. page_contents->insert_tag(string("devtag:.tab-ts")); // tab table start
  2868. page_contents->glyphs.move_right();
  2869. start_of_table = page_contents->glyphs.get_data();
  2870. page_contents->glyphs.move_to(old_pos);
  2871. return start_of_table;
  2872. }
  2873. /*
  2874. * insert_tab_te - inserts a tab-te before the current position
  2875. * (it skips backwards over .sp/.br)
  2876. */
  2877. void html_printer::insert_tab_te (void)
  2878. {
  2879. text_glob *g = page_contents->glyphs.get_data();
  2880. page_contents->dump_page();
  2881. while (page_contents->glyphs.get_data()->is_a_tag())
  2882. page_contents->glyphs.move_left();
  2883. page_contents->insert_tag(string("devtag:.tab-te")); // tab table end
  2884. while (g != page_contents->glyphs.get_data())
  2885. page_contents->glyphs.move_right();
  2886. page_contents->dump_page();
  2887. }
  2888. /*
  2889. * insert_tab_0 - inserts a tab0 before, where.
  2890. */
  2891. void html_printer::insert_tab_0 (text_glob *where)
  2892. {
  2893. text_glob *old_pos = page_contents->glyphs.get_data();
  2894. page_contents->glyphs.move_to(where);
  2895. page_contents->glyphs.move_left();
  2896. page_contents->insert_tag(string("devtag:tab0")); // tab0 start of line
  2897. page_contents->glyphs.move_right();
  2898. page_contents->glyphs.move_to(old_pos);
  2899. }
  2900. /*
  2901. * remove_tabs - removes the tabs tags on this line.
  2902. */
  2903. void html_printer::remove_tabs (void)
  2904. {
  2905. text_glob *orig = page_contents->glyphs.get_data();
  2906. text_glob *g;
  2907. if (! page_contents->glyphs.is_equal_to_tail()) {
  2908. do {
  2909. g = page_contents->glyphs.get_data();
  2910. if (g->is_tab()) {
  2911. page_contents->glyphs.sub_move_right();
  2912. if (g == orig)
  2913. orig = page_contents->glyphs.get_data();
  2914. } else
  2915. page_contents->glyphs.move_right();
  2916. } while ((! page_contents->glyphs.is_equal_to_head()) &&
  2917. (! g->is_eol()));
  2918. /*
  2919. * now restore our previous position.
  2920. */
  2921. while (page_contents->glyphs.get_data() != orig)
  2922. page_contents->glyphs.move_left();
  2923. }
  2924. }
  2925. void html_printer::remove_courier_tabs (void)
  2926. {
  2927. text_glob *g;
  2928. int line_start = TRUE;
  2929. int nf = FALSE;
  2930. if (! page_contents->glyphs.is_empty()) {
  2931. page_contents->glyphs.start_from_head();
  2932. as.reset();
  2933. line_start = TRUE;
  2934. do {
  2935. g = page_contents->glyphs.get_data();
  2936. handle_state_assertion(g);
  2937. nf = calc_nf(g, nf);
  2938. if (line_start) {
  2939. if (line_start && nf && is_courier_until_eol()) {
  2940. remove_tabs();
  2941. g = page_contents->glyphs.get_data();
  2942. }
  2943. }
  2944. // line_start = g->is_br() || g->is_nf() || g->is_fi() || (nf && g->is_eol());
  2945. line_start = g->is_br() || (nf && g->is_eol());
  2946. page_contents->glyphs.move_right();
  2947. } while (! page_contents->glyphs.is_equal_to_head());
  2948. }
  2949. }
  2950. void html_printer::insert_tab0_foreach_tab (void)
  2951. {
  2952. text_glob *start_of_line = NULL;
  2953. text_glob *g = NULL;
  2954. int seen_tab = FALSE;
  2955. int seen_col = FALSE;
  2956. int nf = FALSE;
  2957. if (! page_contents->glyphs.is_empty()) {
  2958. page_contents->glyphs.start_from_head();
  2959. as.reset();
  2960. start_of_line = page_contents->glyphs.get_data();
  2961. do {
  2962. g = page_contents->glyphs.get_data();
  2963. handle_state_assertion(g);
  2964. nf = calc_nf(g, nf);
  2965. if (g->is_tab())
  2966. seen_tab = TRUE;
  2967. if (g->is_col())
  2968. seen_col = TRUE;
  2969. if (g->is_br() || (nf && g->is_eol())) {
  2970. do {
  2971. page_contents->glyphs.move_right();
  2972. g = page_contents->glyphs.get_data();
  2973. handle_state_assertion(g);
  2974. nf = calc_nf(g, nf);
  2975. if (page_contents->glyphs.is_equal_to_head()) {
  2976. if (seen_tab && !seen_col)
  2977. insert_tab_0(start_of_line);
  2978. return;
  2979. }
  2980. } while (g->is_br() || (nf && g->is_eol()) || g->is_ta());
  2981. // printf("\nstart_of_line is: %s\n", g->text_string);
  2982. if (seen_tab && !seen_col) {
  2983. insert_tab_0(start_of_line);
  2984. page_contents->glyphs.move_to(g);
  2985. }
  2986. seen_tab = FALSE;
  2987. seen_col = FALSE;
  2988. start_of_line = g;
  2989. }
  2990. page_contents->glyphs.move_right();
  2991. } while (! page_contents->glyphs.is_equal_to_head());
  2992. if (seen_tab && !seen_col)
  2993. insert_tab_0(start_of_line);
  2994. }
  2995. }
  2996. /*
  2997. * update_min_max - updates the extent of a column, given the left and right
  2998. * extents of a glyph, g.
  2999. */
  3000. void html_printer::update_min_max (colType type_of_col, int *minimum, int *maximum, text_glob *g)
  3001. {
  3002. switch (type_of_col) {
  3003. case tab_tag:
  3004. break;
  3005. case tab0_tag:
  3006. *minimum = g->minh;
  3007. break;
  3008. case col_tag:
  3009. *minimum = g->minh;
  3010. *maximum = g->maxh;
  3011. break;
  3012. default:
  3013. break;
  3014. }
  3015. }
  3016. /*
  3017. * add_table_end - moves left one glyph, adds a table end tag and adds a
  3018. * debugging string.
  3019. */
  3020. void html_printer::add_table_end (const char *
  3021. #if defined(DEBUG_TABLES)
  3022. debug_string
  3023. #endif
  3024. )
  3025. {
  3026. page_contents->glyphs.move_left();
  3027. insert_tab_te();
  3028. #if defined(DEBUG_TABLES)
  3029. page_contents->insert_tag(string(debug_string));
  3030. #endif
  3031. }
  3032. /*
  3033. * lookahead_for_tables - checks for .col tags and inserts table
  3034. * start/end tags
  3035. */
  3036. void html_printer::lookahead_for_tables (void)
  3037. {
  3038. text_glob *g;
  3039. text_glob *start_of_line = NULL;
  3040. text_glob *start_of_table = NULL;
  3041. text_glob *last = NULL;
  3042. colType type_of_col = none;
  3043. int left = 0;
  3044. int found_col = FALSE;
  3045. int seen_text = FALSE;
  3046. int ncol = 0;
  3047. int colmin = 0; // pacify compiler
  3048. int colmax = 0; // pacify compiler
  3049. html_table *tbl = new html_table(&html, -1);
  3050. const char *tab_defs = NULL;
  3051. char align = 'L';
  3052. int nf = FALSE;
  3053. int old_pageoffset = pageoffset;
  3054. remove_courier_tabs();
  3055. page_contents->dump_page();
  3056. insert_tab0_foreach_tab();
  3057. page_contents->dump_page();
  3058. if (! page_contents->glyphs.is_empty()) {
  3059. page_contents->glyphs.start_from_head();
  3060. as.reset();
  3061. g = page_contents->glyphs.get_data();
  3062. if (g->is_br()) {
  3063. g = page_contents->glyphs.move_right_get_data();
  3064. handle_state_assertion(g);
  3065. if (page_contents->glyphs.is_equal_to_head()) {
  3066. if (tbl != NULL) {
  3067. delete tbl;
  3068. tbl = NULL;
  3069. }
  3070. return;
  3071. }
  3072. start_of_line = g;
  3073. seen_text = FALSE;
  3074. ncol = 0;
  3075. left = next_horiz_pos(g, nf);
  3076. if (found_col)
  3077. last = g;
  3078. found_col = FALSE;
  3079. }
  3080. do {
  3081. #if defined(DEBUG_TABLES)
  3082. fprintf(stderr, " [") ;
  3083. fprintf(stderr, g->text_string) ;
  3084. fprintf(stderr, "] ") ;
  3085. fflush(stderr);
  3086. if (strcmp(g->text_string, "XXXXXXX") == 0)
  3087. stop();
  3088. #endif
  3089. nf = calc_nf(g, nf);
  3090. calc_po_in(g, nf);
  3091. if (g->is_col()) {
  3092. if (type_of_col == tab_tag && start_of_table != NULL) {
  3093. page_contents->glyphs.move_left();
  3094. insert_tab_te();
  3095. start_of_table->remember_table(tbl);
  3096. tbl = new html_table(&html, -1);
  3097. page_contents->insert_tag(string("*** TAB -> COL ***"));
  3098. if (tab_defs != NULL)
  3099. tbl->tab_stops->init(tab_defs);
  3100. start_of_table = NULL;
  3101. last = NULL;
  3102. }
  3103. type_of_col = col_tag;
  3104. found_col = TRUE;
  3105. ncol = g->get_arg();
  3106. align = 'L';
  3107. colmin = 0;
  3108. colmax = 0;
  3109. } else if (g->is_tab()) {
  3110. type_of_col = tab_tag;
  3111. colmin = g->get_tab_args(&align);
  3112. align = 'L'; // for now as 'C' and 'R' are broken
  3113. ncol = tbl->find_tab_column(colmin);
  3114. colmin += pageoffset + get_troff_indent();
  3115. colmax = tbl->get_tab_pos(ncol+1);
  3116. if (colmax > 0)
  3117. colmax += pageoffset + get_troff_indent();
  3118. } else if (g->is_tab0()) {
  3119. if (type_of_col == col_tag && start_of_table != NULL) {
  3120. page_contents->glyphs.move_left();
  3121. insert_tab_te();
  3122. start_of_table->remember_table(tbl);
  3123. tbl = new html_table(&html, -1);
  3124. page_contents->insert_tag(string("*** COL -> TAB ***"));
  3125. start_of_table = NULL;
  3126. last = NULL;
  3127. }
  3128. if (tab_defs != NULL)
  3129. tbl->tab_stops->init(tab_defs);
  3130. type_of_col = tab0_tag;
  3131. ncol = 1;
  3132. colmin = 0;
  3133. colmax = tbl->get_tab_pos(2) + pageoffset + get_troff_indent();
  3134. } else if (! g->is_a_tag())
  3135. update_min_max(type_of_col, &colmin, &colmax, g);
  3136. if ((! g->is_a_tag()) || g->is_tab())
  3137. seen_text = TRUE;
  3138. if ((g->is_col() || g->is_tab() || g->is_tab0())
  3139. && (start_of_line != NULL) && (start_of_table == NULL)) {
  3140. start_of_table = insert_tab_ts(start_of_line);
  3141. start_of_line = NULL;
  3142. seen_text = FALSE;
  3143. } else if (g->is_ce() && (start_of_table != NULL)) {
  3144. add_table_end("*** CE ***");
  3145. start_of_table->remember_table(tbl);
  3146. tbl = new html_table(&html, -1);
  3147. start_of_table = NULL;
  3148. last = NULL;
  3149. } else if (g->is_ta()) {
  3150. tab_defs = g->text_string;
  3151. if (type_of_col == col_tag)
  3152. tbl->tab_stops->check_init(tab_defs);
  3153. if (!tbl->tab_stops->compatible(tab_defs)) {
  3154. if (start_of_table != NULL) {
  3155. add_table_end("*** TABS ***");
  3156. start_of_table->remember_table(tbl);
  3157. tbl = new html_table(&html, -1);
  3158. start_of_table = NULL;
  3159. type_of_col = none;
  3160. last = NULL;
  3161. }
  3162. tbl->tab_stops->init(tab_defs);
  3163. }
  3164. }
  3165. if (((! g->is_a_tag()) || g->is_tab()) && (start_of_table != NULL)) {
  3166. // we are in a table and have a glyph
  3167. if ((ncol == 0) || (! tbl->add_column(ncol, colmin, colmax, align))) {
  3168. if (ncol == 0)
  3169. add_table_end("*** NCOL == 0 ***");
  3170. else
  3171. add_table_end("*** CROSSED COLS ***");
  3172. start_of_table->remember_table(tbl);
  3173. tbl = new html_table(&html, -1);
  3174. start_of_table = NULL;
  3175. type_of_col = none;
  3176. last = NULL;
  3177. }
  3178. }
  3179. /*
  3180. * move onto next glob, check whether we are starting a new line
  3181. */
  3182. g = page_contents->glyphs.move_right_get_data();
  3183. handle_state_assertion(g);
  3184. if (g == NULL) {
  3185. if (found_col) {
  3186. page_contents->glyphs.start_from_head();
  3187. as.reset();
  3188. last = g;
  3189. found_col = FALSE;
  3190. }
  3191. } else if (g->is_br() || (nf && g->is_eol())) {
  3192. do {
  3193. g = page_contents->glyphs.move_right_get_data();
  3194. handle_state_assertion(g);
  3195. nf = calc_nf(g, nf);
  3196. } while ((g != NULL) && (g->is_br() || (nf && g->is_eol())));
  3197. start_of_line = g;
  3198. seen_text = FALSE;
  3199. ncol = 0;
  3200. left = next_horiz_pos(g, nf);
  3201. if (found_col)
  3202. last = g;
  3203. found_col = FALSE;
  3204. }
  3205. } while ((g != NULL) && (! page_contents->glyphs.is_equal_to_head()));
  3206. #if defined(DEBUG_TABLES)
  3207. fprintf(stderr, "finished scanning for tables\n");
  3208. #endif
  3209. page_contents->glyphs.start_from_head();
  3210. if (start_of_table != NULL) {
  3211. if (last != NULL)
  3212. while (last != page_contents->glyphs.get_data())
  3213. page_contents->glyphs.move_left();
  3214. insert_tab_te();
  3215. start_of_table->remember_table(tbl);
  3216. tbl = NULL;
  3217. page_contents->insert_tag(string("*** LAST ***"));
  3218. }
  3219. }
  3220. if (tbl != NULL) {
  3221. delete tbl;
  3222. tbl = NULL;
  3223. }
  3224. // and reset the registers
  3225. pageoffset = old_pageoffset;
  3226. troff_indent = 0;
  3227. temp_indent = 0;
  3228. end_tempindent = 0;
  3229. }
  3230. void html_printer::flush_page (void)
  3231. {
  3232. supress_sub_sup = TRUE;
  3233. flush_sbuf();
  3234. page_contents->dump_page();
  3235. lookahead_for_tables();
  3236. page_contents->dump_page();
  3237. flush_globs();
  3238. current_paragraph->done_para();
  3239. // move onto a new page
  3240. delete page_contents;
  3241. #if defined(DEBUG_TABLES)
  3242. fprintf(stderr, "\n\n*** flushed page ***\n\n");
  3243. html.simple_comment("new page called");
  3244. #endif
  3245. page_contents = new page;
  3246. }
  3247. /*
  3248. * determine_space - works out whether we need to write a space.
  3249. * If last glyph is ajoining then no space emitted.
  3250. */
  3251. void html_printer::determine_space (text_glob *g)
  3252. {
  3253. if (current_paragraph->is_in_pre()) {
  3254. /*
  3255. * .nf has been specified
  3256. */
  3257. while (output_hpos < g->minh) {
  3258. output_hpos += space_width;
  3259. current_paragraph->emit_space();
  3260. }
  3261. } else {
  3262. if ((output_vpos != g->minv) || (output_hpos < g->minh)) {
  3263. current_paragraph->emit_space();
  3264. }
  3265. }
  3266. }
  3267. /*
  3268. * is_line_start - returns TRUE if we are at the start of a line.
  3269. */
  3270. int html_printer::is_line_start (int nf)
  3271. {
  3272. int line_start = FALSE;
  3273. int result = TRUE;
  3274. text_glob *orig = page_contents->glyphs.get_data();
  3275. text_glob *g;
  3276. if (! page_contents->glyphs.is_equal_to_head()) {
  3277. do {
  3278. page_contents->glyphs.move_left();
  3279. g = page_contents->glyphs.get_data();
  3280. result = g->is_a_tag();
  3281. if (g->is_fi())
  3282. nf = FALSE;
  3283. else if (g->is_nf())
  3284. nf = TRUE;
  3285. line_start = g->is_col() || g->is_br() || (nf && g->is_eol());
  3286. } while ((!line_start) && (result));
  3287. /*
  3288. * now restore our previous position.
  3289. */
  3290. while (page_contents->glyphs.get_data() != orig)
  3291. page_contents->glyphs.move_right();
  3292. }
  3293. return result;
  3294. }
  3295. /*
  3296. * is_font_courier - returns TRUE if the font, f, is courier.
  3297. */
  3298. int html_printer::is_font_courier (font *f)
  3299. {
  3300. if (f != 0) {
  3301. const char *fontname = f->get_name();
  3302. return( (fontname != 0) && (fontname[0] == 'C') );
  3303. }
  3304. return FALSE;
  3305. }
  3306. /*
  3307. * end_font - shuts down the font corresponding to fontname.
  3308. */
  3309. void html_printer::end_font (const char *fontname)
  3310. {
  3311. if (strcmp(fontname, "B") == 0) {
  3312. current_paragraph->done_bold();
  3313. } else if (strcmp(fontname, "I") == 0) {
  3314. current_paragraph->done_italic();
  3315. } else if (strcmp(fontname, "BI") == 0) {
  3316. current_paragraph->done_bold();
  3317. current_paragraph->done_italic();
  3318. } else if (strcmp(fontname, "CR") == 0) {
  3319. current_paragraph->done_tt();
  3320. } else if (strcmp(fontname, "CI") == 0) {
  3321. current_paragraph->done_italic();
  3322. current_paragraph->done_tt();
  3323. } else if (strcmp(fontname, "CB") == 0) {
  3324. current_paragraph->done_bold();
  3325. current_paragraph->done_tt();
  3326. } else if (strcmp(fontname, "CBI") == 0) {
  3327. current_paragraph->done_bold();
  3328. current_paragraph->done_italic();
  3329. current_paragraph->done_tt();
  3330. }
  3331. }
  3332. /*
  3333. * start_font - starts the font corresponding to name.
  3334. */
  3335. void html_printer::start_font (const char *fontname)
  3336. {
  3337. if (strcmp(fontname, "R") == 0) {
  3338. current_paragraph->done_bold();
  3339. current_paragraph->done_italic();
  3340. current_paragraph->done_tt();
  3341. } else if (strcmp(fontname, "B") == 0) {
  3342. current_paragraph->do_bold();
  3343. } else if (strcmp(fontname, "I") == 0) {
  3344. current_paragraph->do_italic();
  3345. } else if (strcmp(fontname, "BI") == 0) {
  3346. current_paragraph->do_bold();
  3347. current_paragraph->do_italic();
  3348. } else if (strcmp(fontname, "CR") == 0) {
  3349. if ((! fill_on) && (is_courier_until_eol()) &&
  3350. is_line_start(! fill_on)) {
  3351. current_paragraph->do_pre();
  3352. }
  3353. current_paragraph->do_tt();
  3354. } else if (strcmp(fontname, "CI") == 0) {
  3355. if ((! fill_on) && (is_courier_until_eol()) &&
  3356. is_line_start(! fill_on)) {
  3357. current_paragraph->do_pre();
  3358. }
  3359. current_paragraph->do_tt();
  3360. current_paragraph->do_italic();
  3361. } else if (strcmp(fontname, "CB") == 0) {
  3362. if ((! fill_on) && (is_courier_until_eol()) &&
  3363. is_line_start(! fill_on)) {
  3364. current_paragraph->do_pre();
  3365. }
  3366. current_paragraph->do_tt();
  3367. current_paragraph->do_bold();
  3368. } else if (strcmp(fontname, "CBI") == 0) {
  3369. if ((! fill_on) && (is_courier_until_eol()) &&
  3370. is_line_start(! fill_on)) {
  3371. current_paragraph->do_pre();
  3372. }
  3373. current_paragraph->do_tt();
  3374. current_paragraph->do_italic();
  3375. current_paragraph->do_bold();
  3376. }
  3377. }
  3378. /*
  3379. * start_size - from is old font size, to is the new font size.
  3380. * The html increase <big> and <small> decrease alters the
  3381. * font size by 20%. We try and map these onto glyph sizes.
  3382. */
  3383. void html_printer::start_size (int from, int to)
  3384. {
  3385. if (from < to) {
  3386. while (from < to) {
  3387. current_paragraph->do_big();
  3388. from += SIZE_INCREMENT;
  3389. }
  3390. } else if (from > to) {
  3391. while (from > to) {
  3392. current_paragraph->do_small();
  3393. from -= SIZE_INCREMENT;
  3394. }
  3395. }
  3396. }
  3397. /*
  3398. * do_font - checks to see whether we need to alter the html font.
  3399. */
  3400. void html_printer::do_font (text_glob *g)
  3401. {
  3402. /*
  3403. * check if the output_style.point_size has not been set yet
  3404. * this allow users to place .ps at the top of their troff files
  3405. * and grohtml can then treat the .ps value as the base font size (3)
  3406. */
  3407. if (output_style.point_size == -1) {
  3408. output_style.point_size = pointsize;
  3409. }
  3410. if (g->text_style.f != output_style.f) {
  3411. if (output_style.f != 0) {
  3412. end_font(output_style.f->get_name());
  3413. }
  3414. output_style.f = g->text_style.f;
  3415. if (output_style.f != 0) {
  3416. start_font(output_style.f->get_name());
  3417. }
  3418. }
  3419. if (output_style.point_size != g->text_style.point_size) {
  3420. do_sup_or_sub(g);
  3421. if ((output_style.point_size > 0) &&
  3422. (g->text_style.point_size > 0)) {
  3423. start_size(output_style.point_size, g->text_style.point_size);
  3424. }
  3425. if (g->text_style.point_size > 0) {
  3426. output_style.point_size = g->text_style.point_size;
  3427. }
  3428. }
  3429. if (output_style.col != g->text_style.col) {
  3430. current_paragraph->done_color();
  3431. output_style.col = g->text_style.col;
  3432. current_paragraph->do_color(&output_style.col);
  3433. }
  3434. }
  3435. /*
  3436. * start_subscript - returns TRUE if, g, looks like a subscript start.
  3437. */
  3438. int html_printer::start_subscript (text_glob *g)
  3439. {
  3440. int r = font::res;
  3441. int height = output_style.point_size*r/72;
  3442. return( (output_style.point_size != 0) &&
  3443. (output_vpos < g->minv) &&
  3444. (output_vpos-height > g->maxv) &&
  3445. (output_style.point_size > g->text_style.point_size) );
  3446. }
  3447. /*
  3448. * start_superscript - returns TRUE if, g, looks like a superscript start.
  3449. */
  3450. int html_printer::start_superscript (text_glob *g)
  3451. {
  3452. int r = font::res;
  3453. int height = output_style.point_size*r/72;
  3454. return( (output_style.point_size != 0) &&
  3455. (output_vpos > g->minv) &&
  3456. (output_vpos-height < g->maxv) &&
  3457. (output_style.point_size > g->text_style.point_size) );
  3458. }
  3459. /*
  3460. * end_subscript - returns TRUE if, g, looks like the end of a subscript.
  3461. */
  3462. int html_printer::end_subscript (text_glob *g)
  3463. {
  3464. int r = font::res;
  3465. int height = output_style.point_size*r/72;
  3466. return( (output_style.point_size != 0) &&
  3467. (g->minv < output_vpos) &&
  3468. (output_vpos-height > g->maxv) &&
  3469. (output_style.point_size < g->text_style.point_size) );
  3470. }
  3471. /*
  3472. * end_superscript - returns TRUE if, g, looks like the end of a superscript.
  3473. */
  3474. int html_printer::end_superscript (text_glob *g)
  3475. {
  3476. int r = font::res;
  3477. int height = output_style.point_size*r/72;
  3478. return( (output_style.point_size != 0) &&
  3479. (g->minv > output_vpos) &&
  3480. (output_vpos-height < g->maxv) &&
  3481. (output_style.point_size < g->text_style.point_size) );
  3482. }
  3483. /*
  3484. * do_sup_or_sub - checks to see whether the next glyph is a subscript/superscript
  3485. * start/end and it calls the services of html-text to issue the
  3486. * appropriate tags.
  3487. */
  3488. void html_printer::do_sup_or_sub (text_glob *g)
  3489. {
  3490. if (! supress_sub_sup) {
  3491. if (start_subscript(g)) {
  3492. current_paragraph->do_sub();
  3493. } else if (start_superscript(g)) {
  3494. current_paragraph->do_sup();
  3495. } else if (end_subscript(g)) {
  3496. current_paragraph->done_sub();
  3497. } else if (end_superscript(g)) {
  3498. current_paragraph->done_sup();
  3499. }
  3500. }
  3501. }
  3502. /*
  3503. * do_end_para - writes out the html text after shutting down the
  3504. * current paragraph.
  3505. */
  3506. void html_printer::do_end_para (text_glob *g)
  3507. {
  3508. do_font(g);
  3509. current_paragraph->done_para();
  3510. current_paragraph->remove_para_space();
  3511. html.put_string(g->text_string+9);
  3512. output_vpos = g->minv;
  3513. output_hpos = g->maxh;
  3514. output_vpos_max = g->maxv;
  3515. supress_sub_sup = FALSE;
  3516. }
  3517. /*
  3518. * emit_html - write out the html text
  3519. */
  3520. void html_printer::emit_html (text_glob *g)
  3521. {
  3522. do_font(g);
  3523. determine_space(g);
  3524. current_paragraph->do_emittext(g->text_string, g->text_length);
  3525. output_vpos = g->minv;
  3526. output_hpos = g->maxh;
  3527. output_vpos_max = g->maxv;
  3528. supress_sub_sup = FALSE;
  3529. }
  3530. /*
  3531. * flush_sbuf - flushes the current sbuf into the list of glyphs.
  3532. */
  3533. void html_printer::flush_sbuf()
  3534. {
  3535. if (sbuf.length() > 0) {
  3536. int r=font::res; // resolution of the device
  3537. set_style(sbuf_style);
  3538. if (overstrike_detected && (! is_bold(sbuf_style.f))) {
  3539. font *bold_font = make_bold(sbuf_style.f);
  3540. if (bold_font != NULL)
  3541. sbuf_style.f = bold_font;
  3542. }
  3543. page_contents->add(&sbuf_style, sbuf,
  3544. line_number,
  3545. sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
  3546. sbuf_vpos , sbuf_end_hpos);
  3547. output_hpos = sbuf_end_hpos;
  3548. output_vpos = sbuf_vpos;
  3549. last_sbuf_length = 0;
  3550. sbuf_prev_hpos = sbuf_end_hpos;
  3551. overstrike_detected = FALSE;
  3552. sbuf.clear();
  3553. }
  3554. }
  3555. void html_printer::set_line_thickness(const environment *env)
  3556. {
  3557. line_thickness = env->size;
  3558. }
  3559. void html_printer::draw(int code, int *p, int np, const environment *env)
  3560. {
  3561. switch (code) {
  3562. case 'l':
  3563. # if 0
  3564. if (np == 2) {
  3565. page_contents->add_line(&sbuf_style,
  3566. line_number,
  3567. env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1], line_thickness);
  3568. } else {
  3569. error("2 arguments required for line");
  3570. }
  3571. # endif
  3572. break;
  3573. case 't':
  3574. {
  3575. if (np == 0) {
  3576. line_thickness = -1;
  3577. } else {
  3578. // troff gratuitously adds an extra 0
  3579. if (np != 1 && np != 2) {
  3580. error("0 or 1 argument required for thickness");
  3581. break;
  3582. }
  3583. line_thickness = p[0];
  3584. }
  3585. break;
  3586. }
  3587. case 'P':
  3588. break;
  3589. case 'p':
  3590. break;
  3591. case 'E':
  3592. break;
  3593. case 'e':
  3594. break;
  3595. case 'C':
  3596. break;
  3597. case 'c':
  3598. break;
  3599. case 'a':
  3600. break;
  3601. case '~':
  3602. break;
  3603. case 'f':
  3604. break;
  3605. case 'F':
  3606. // fill with color env->fill
  3607. if (background != NULL)
  3608. delete background;
  3609. background = new color;
  3610. *background = *env->fill;
  3611. break;
  3612. default:
  3613. error("unrecognised drawing command `%1'", char(code));
  3614. break;
  3615. }
  3616. }
  3617. html_printer::html_printer()
  3618. : html(0, MAX_LINE_LENGTH),
  3619. no_of_printed_pages(0),
  3620. last_sbuf_length(0),
  3621. overstrike_detected(FALSE),
  3622. output_hpos(-1),
  3623. output_vpos(-1),
  3624. output_vpos_max(-1),
  3625. line_thickness(-1),
  3626. inside_font_style(0),
  3627. page_number(0),
  3628. header_indent(-1),
  3629. supress_sub_sup(TRUE),
  3630. cutoff_heading(100),
  3631. indent(NULL),
  3632. table(NULL),
  3633. end_center(0),
  3634. end_tempindent(0),
  3635. next_tag(INLINE),
  3636. fill_on(TRUE),
  3637. max_linelength(-1),
  3638. linelength(0),
  3639. pageoffset(0),
  3640. troff_indent(0),
  3641. device_indent(0),
  3642. temp_indent(0),
  3643. pointsize(base_point_size),
  3644. line_number(0),
  3645. background(default_background),
  3646. seen_indent(FALSE),
  3647. next_indent(0),
  3648. seen_pageoffset(FALSE),
  3649. next_pageoffset(0),
  3650. seen_linelength(FALSE),
  3651. next_linelength(0),
  3652. seen_center(FALSE),
  3653. next_center(0),
  3654. seen_space(0),
  3655. seen_break(0),
  3656. current_column(0),
  3657. row_space(FALSE)
  3658. {
  3659. file_list.add_new_file(xtmpfile());
  3660. html.set_file(file_list.get_file());
  3661. if (font::hor != 24)
  3662. fatal("horizontal resolution must be 24");
  3663. if (font::vert != 40)
  3664. fatal("vertical resolution must be 40");
  3665. #if 0
  3666. // should be sorted html..
  3667. if (font::res % (font::sizescale*72) != 0)
  3668. fatal("res must be a multiple of 72*sizescale");
  3669. #endif
  3670. int r = font::res;
  3671. int point = 0;
  3672. while (r % 10 == 0) {
  3673. r /= 10;
  3674. point++;
  3675. }
  3676. res = r;
  3677. html.set_fixed_point(point);
  3678. space_char_index = font::name_to_index("space");
  3679. space_width = font::hor;
  3680. paper_length = font::paperlength;
  3681. linelength = font::res*13/2;
  3682. if (paper_length == 0)
  3683. paper_length = 11*font::res;
  3684. page_contents = new page();
  3685. }
  3686. /*
  3687. * add_to_sbuf - adds character code or name to the sbuf.
  3688. */
  3689. void html_printer::add_to_sbuf (int idx, const string &s)
  3690. {
  3691. if (sbuf_style.f == NULL)
  3692. return;
  3693. char *html_glyph = NULL;
  3694. unsigned int code = sbuf_style.f->get_code(idx);
  3695. if (s.empty()) {
  3696. if (sbuf_style.f->contains(idx))
  3697. html_glyph = (char *)sbuf_style.f->get_special_device_encoding(idx);
  3698. else
  3699. html_glyph = NULL;
  3700. if ((html_glyph == NULL) && (code >= UNICODE_DESC_START))
  3701. html_glyph = to_unicode(code);
  3702. } else
  3703. html_glyph = get_html_translation(sbuf_style.f, s);
  3704. last_sbuf_length = sbuf.length();
  3705. if (html_glyph == NULL)
  3706. sbuf += ((char)code);
  3707. else
  3708. sbuf += html_glyph;
  3709. }
  3710. int html_printer::sbuf_continuation (int idx, const char *name,
  3711. const environment *env, int w)
  3712. {
  3713. /*
  3714. * lets see whether the glyph is closer to the end of sbuf
  3715. */
  3716. if ((sbuf_end_hpos == env->hpos)
  3717. || ((sbuf_prev_hpos < sbuf_end_hpos)
  3718. && (env->hpos < sbuf_end_hpos)
  3719. && ((sbuf_end_hpos-env->hpos < env->hpos-sbuf_prev_hpos)))) {
  3720. add_to_sbuf(idx, name);
  3721. sbuf_prev_hpos = sbuf_end_hpos;
  3722. sbuf_end_hpos += w + sbuf_kern;
  3723. return TRUE;
  3724. } else {
  3725. if ((env->hpos >= sbuf_end_hpos) &&
  3726. ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
  3727. /*
  3728. * lets see whether a space is needed or not
  3729. */
  3730. if (env->hpos-sbuf_end_hpos < space_width) {
  3731. add_to_sbuf(idx, name);
  3732. sbuf_prev_hpos = sbuf_end_hpos;
  3733. sbuf_end_hpos = env->hpos + w;
  3734. return TRUE;
  3735. }
  3736. }
  3737. }
  3738. return FALSE ;
  3739. }
  3740. /*
  3741. * get_html_translation - given the position of the character and its name
  3742. * return the device encoding for such character.
  3743. */
  3744. char *get_html_translation (font *f, const string &name)
  3745. {
  3746. int idx;
  3747. if ((f == 0) || name.empty())
  3748. return NULL;
  3749. else {
  3750. idx = f->name_to_index((char *)(name + '\0').contents());
  3751. if (idx == 0) {
  3752. error("character `%s' not found", (name + '\0').contents());
  3753. return NULL;
  3754. } else
  3755. if (f->contains(idx))
  3756. return (char *)f->get_special_device_encoding(idx);
  3757. else
  3758. return NULL;
  3759. }
  3760. }
  3761. /*
  3762. * overstrike - returns TRUE if the glyph (i, name) is going to overstrike
  3763. * a previous glyph in sbuf.
  3764. * If TRUE the font is changed to bold and the previous sbuf
  3765. * is flushed.
  3766. */
  3767. int html_printer::overstrike(int idx, const char *name, const environment *env, int w)
  3768. {
  3769. if ((env->hpos < sbuf_end_hpos)
  3770. || ((sbuf_kern != 0) && (sbuf_end_hpos - sbuf_kern < env->hpos))) {
  3771. /*
  3772. * at this point we have detected an overlap
  3773. */
  3774. if (overstrike_detected) {
  3775. /* already detected, remove previous glyph and use this glyph */
  3776. sbuf.set_length(last_sbuf_length);
  3777. add_to_sbuf(idx, name);
  3778. sbuf_end_hpos = env->hpos + w;
  3779. return TRUE;
  3780. } else {
  3781. /* first time we have detected an overstrike in the sbuf */
  3782. sbuf.set_length(last_sbuf_length); /* remove previous glyph */
  3783. if (! is_bold(sbuf_style.f))
  3784. flush_sbuf();
  3785. overstrike_detected = TRUE;
  3786. add_to_sbuf(idx, name);
  3787. sbuf_end_hpos = env->hpos + w;
  3788. return TRUE;
  3789. }
  3790. }
  3791. return FALSE ;
  3792. }
  3793. /*
  3794. * set_char - adds a character into the sbuf if it is a continuation
  3795. * with the previous word otherwise flush the current sbuf
  3796. * and add character anew.
  3797. */
  3798. void html_printer::set_char(int i, font *f, const environment *env,
  3799. int w, const char *name)
  3800. {
  3801. style sty(f, env->size, env->height, env->slant, env->fontno, *env->col);
  3802. if (sty.slant != 0) {
  3803. if (sty.slant > 80 || sty.slant < -80) {
  3804. error("silly slant `%1' degrees", sty.slant);
  3805. sty.slant = 0;
  3806. }
  3807. }
  3808. if (((! sbuf.empty()) && (sty == sbuf_style) && (sbuf_vpos == env->vpos))
  3809. && (sbuf_continuation(i, name, env, w) || overstrike(i, name, env, w)))
  3810. return;
  3811. flush_sbuf();
  3812. if (sbuf_style.f == NULL)
  3813. sbuf_style = sty;
  3814. add_to_sbuf(i, name);
  3815. sbuf_end_hpos = env->hpos + w;
  3816. sbuf_start_hpos = env->hpos;
  3817. sbuf_prev_hpos = env->hpos;
  3818. sbuf_vpos = env->vpos;
  3819. sbuf_style = sty;
  3820. sbuf_kern = 0;
  3821. }
  3822. /*
  3823. * set_numbered_char - handle numbered characters.
  3824. * Negative values are interpreted as unbreakable spaces;
  3825. * the value (taken positive) gives the width.
  3826. */
  3827. void html_printer::set_numbered_char(int num, const environment *env,
  3828. int *widthp)
  3829. {
  3830. int nbsp_width = 0;
  3831. if (num < 0) {
  3832. nbsp_width = -num;
  3833. num = 160; // &nbsp;
  3834. }
  3835. int i = font::number_to_index(num);
  3836. int fn = env->fontno;
  3837. if (fn < 0 || fn >= nfonts) {
  3838. error("bad font position `%1'", fn);
  3839. return;
  3840. }
  3841. font *f = font_table[fn];
  3842. if (f == 0) {
  3843. error("no font mounted at `%1'", fn);
  3844. return;
  3845. }
  3846. if (!f->contains(i)) {
  3847. error("font `%1' does not contain numbered character %2",
  3848. f->get_name(),
  3849. num);
  3850. return;
  3851. }
  3852. int w;
  3853. if (nbsp_width)
  3854. w = nbsp_width;
  3855. else
  3856. w = f->get_width(i, env->size);
  3857. w = round_width(w);
  3858. if (widthp)
  3859. *widthp = w;
  3860. set_char(i, f, env, w, 0);
  3861. }
  3862. int html_printer::set_char_and_width(const char *nm, const environment *env,
  3863. int *widthp, font **f)
  3864. {
  3865. int i = font::name_to_index(nm);
  3866. int fn = env->fontno;
  3867. if (fn < 0 || fn >= nfonts) {
  3868. error("bad font position `%1'", fn);
  3869. return -1;
  3870. }
  3871. *f = font_table[fn];
  3872. if (*f == 0) {
  3873. error("no font mounted at `%1'", fn);
  3874. return -1;
  3875. }
  3876. if (!(*f)->contains(i)) {
  3877. if (nm[0] != '\0' && nm[1] == '\0')
  3878. error("font `%1' does not contain ascii character `%2'",
  3879. (*f)->get_name(),
  3880. nm[0]);
  3881. else
  3882. error("font `%1' does not contain special character `%2'",
  3883. (*f)->get_name(),
  3884. nm);
  3885. return -1;
  3886. }
  3887. int w = (*f)->get_width(i, env->size);
  3888. w = round_width(w);
  3889. if (widthp)
  3890. *widthp = w;
  3891. return i;
  3892. }
  3893. /*
  3894. * write_title - writes the title to this document
  3895. */
  3896. void html_printer::write_title (int in_head)
  3897. {
  3898. if (title.has_been_found) {
  3899. if (in_head) {
  3900. html.put_string("<title>");
  3901. html.put_string(title.text);
  3902. html.put_string("</title>").nl().nl();
  3903. } else {
  3904. title.has_been_written = TRUE;
  3905. if (title.with_h1) {
  3906. html.put_string("<h1 align=center>");
  3907. html.put_string(title.text);
  3908. html.put_string("</h1>").nl().nl();
  3909. }
  3910. }
  3911. } else if (in_head) {
  3912. // place empty title tags to help conform to `tidy'
  3913. html.put_string("<title></title>").nl();
  3914. }
  3915. }
  3916. /*
  3917. * write_rule - emits a html rule tag, if the auto_rule boolean is true.
  3918. */
  3919. static void write_rule (void)
  3920. {
  3921. if (auto_rule)
  3922. fputs("<hr>\n", stdout);
  3923. }
  3924. void html_printer::begin_page(int n)
  3925. {
  3926. page_number = n;
  3927. #if defined(DEBUGGING)
  3928. html.begin_comment("Page: ").put_string(i_to_a(page_number)).end_comment();;
  3929. #endif
  3930. no_of_printed_pages++;
  3931. output_style.f = 0;
  3932. output_style.point_size= -1;
  3933. output_space_code = 32;
  3934. output_draw_point_size = -1;
  3935. output_line_thickness = -1;
  3936. output_hpos = -1;
  3937. output_vpos = -1;
  3938. output_vpos_max = -1;
  3939. current_paragraph = new html_text(&html);
  3940. do_indent(get_troff_indent(), pageoffset, linelength);
  3941. current_paragraph->do_para("", FALSE);
  3942. }
  3943. void html_printer::end_page(int)
  3944. {
  3945. flush_sbuf();
  3946. flush_page();
  3947. }
  3948. font *html_printer::make_font(const char *nm)
  3949. {
  3950. return html_font::load_html_font(nm);
  3951. }
  3952. void html_printer::do_body (void)
  3953. {
  3954. if (background == NULL)
  3955. fputs("<body>\n\n", stdout);
  3956. else {
  3957. unsigned int r, g, b;
  3958. char buf[6+1];
  3959. background->get_rgb(&r, &g, &b);
  3960. // we have to scale 0..0xFFFF to 0..0xFF
  3961. sprintf(buf, "%.2X%.2X%.2X", r/0x101, g/0x101, b/0x101);
  3962. fputs("<body bgcolor=\"#", stdout);
  3963. fputs(buf, stdout);
  3964. fputs("\">\n\n", stdout);
  3965. }
  3966. }
  3967. /*
  3968. * emit_link - generates: <a href="to">name</a>
  3969. */
  3970. void html_printer::emit_link (const string &to, const char *name)
  3971. {
  3972. fputs("<a href=\"", stdout);
  3973. fputs(to.contents(), stdout);
  3974. fputs("\">", stdout);
  3975. fputs(name, stdout);
  3976. fputs("</a>", stdout);
  3977. }
  3978. /*
  3979. * write_navigation - writes out the links which navigate between
  3980. * file fragments.
  3981. */
  3982. void html_printer::write_navigation (const string &top, const string &prev,
  3983. const string &next, const string &current)
  3984. {
  3985. int need_bar = FALSE;
  3986. if (multiple_files) {
  3987. write_rule();
  3988. fputs("[ ", stdout);
  3989. if ((strcmp(prev.contents(), "") != 0) && prev != top && prev != current) {
  3990. emit_link(prev, "prev");
  3991. need_bar = TRUE;
  3992. }
  3993. if ((strcmp(next.contents(), "") != 0) && next != top && next != current) {
  3994. if (need_bar)
  3995. fputs(" | ", stdout);
  3996. emit_link(next, "next");
  3997. need_bar = TRUE;
  3998. }
  3999. if (top != "<standard input>" && (strcmp(top.contents(), "") != 0) && top != current) {
  4000. if (need_bar)
  4001. fputs(" | ", stdout);
  4002. emit_link(top, "top");
  4003. }
  4004. fputs(" ]\n", stdout);
  4005. write_rule();
  4006. }
  4007. }
  4008. /*
  4009. * do_file_components - scan the file list copying each temporary
  4010. * file in turn. This is used twofold:
  4011. *
  4012. * firstly to emit section heading links,
  4013. * between file fragments if required and
  4014. * secondly to generate jobname file fragments
  4015. * if required.
  4016. */
  4017. void html_printer::do_file_components (void)
  4018. {
  4019. int fragment_no = 1;
  4020. string top;
  4021. string prev;
  4022. string next;
  4023. string current;
  4024. file_list.start_of_list();
  4025. top = string(job_name);
  4026. top += string(".html");
  4027. top += '\0';
  4028. next = file_list.next_file_name();
  4029. next += '\0';
  4030. current = next;
  4031. while (file_list.get_file() != 0) {
  4032. if (fseek(file_list.get_file(), 0L, 0) < 0)
  4033. fatal("fseek on temporary file failed");
  4034. html.copy_file(file_list.get_file());
  4035. fclose(file_list.get_file());
  4036. file_list.move_next();
  4037. if (file_list.is_new_output_file()) {
  4038. if (fragment_no > 1)
  4039. write_navigation(top, prev, next, current);
  4040. prev = current;
  4041. current = next;
  4042. next = file_list.next_file_name();
  4043. next += '\0';
  4044. string split_file = file_list.file_name();
  4045. split_file += '\0';
  4046. fflush(stdout);
  4047. freopen(split_file.contents(), "w", stdout);
  4048. fragment_no++;
  4049. writeHeadMetaStyle();
  4050. write_navigation(top, prev, next, current);
  4051. }
  4052. if (file_list.are_links_required())
  4053. header.write_headings(stdout, TRUE);
  4054. }
  4055. if (fragment_no > 1)
  4056. write_navigation(top, prev, next, current);
  4057. else
  4058. write_rule();
  4059. }
  4060. /*
  4061. * writeHeadMetaStyle - emits the <head> <meta> and <style> tags and
  4062. * related information.
  4063. */
  4064. void html_printer::writeHeadMetaStyle (void)
  4065. {
  4066. fputs("<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\n", stdout);
  4067. fputs("\"http://www.w3.org/TR/html4/loose.dtd\">\n", stdout);
  4068. fputs("<html>\n", stdout);
  4069. fputs("<head>\n", stdout);
  4070. fputs("<meta name=\"generator\" "
  4071. "content=\"groff -Thtml, see www.gnu.org\">\n", stdout);
  4072. fputs("<meta http-equiv=\"Content-Type\" "
  4073. "content=\"text/html; charset=US-ASCII\">\n", stdout);
  4074. fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
  4075. fputs("<style type=\"text/css\">\n", stdout);
  4076. fputs(" p { margin-top: 0; margin-bottom: 0; }\n", stdout);
  4077. fputs(" pre { margin-top: 0; margin-bottom: 0; }\n", stdout);
  4078. fputs(" table { margin-top: 0; margin-bottom: 0; }\n", stdout);
  4079. fputs("</style>\n", stdout);
  4080. }
  4081. html_printer::~html_printer()
  4082. {
  4083. #ifdef LONG_FOR_TIME_T
  4084. long t;
  4085. #else
  4086. time_t t;
  4087. #endif
  4088. current_paragraph->flush_text();
  4089. html.end_line();
  4090. html.set_file(stdout);
  4091. html.begin_comment("Creator : ")
  4092. .put_string("groff ")
  4093. .put_string("version ")
  4094. .put_string(Version_string)
  4095. .end_comment();
  4096. t = time(0);
  4097. html.begin_comment("CreationDate: ")
  4098. .put_string(ctime(&t), strlen(ctime(&t))-1)
  4099. .end_comment();
  4100. writeHeadMetaStyle();
  4101. write_title(TRUE);
  4102. head_info += '\0';
  4103. fputs(head_info.contents(), stdout);
  4104. fputs("</head>\n", stdout);
  4105. do_body();
  4106. write_title(FALSE);
  4107. header.write_headings(stdout, FALSE);
  4108. write_rule();
  4109. #if defined(DEBUGGING)
  4110. html.begin_comment("Total number of pages: ").put_string(i_to_a(no_of_printed_pages)).end_comment();
  4111. #endif
  4112. html.end_line();
  4113. html.end_line();
  4114. if (multiple_files) {
  4115. fputs("</body>\n", stdout);
  4116. fputs("</html>\n", stdout);
  4117. do_file_components();
  4118. } else {
  4119. do_file_components();
  4120. fputs("</body>\n", stdout);
  4121. fputs("</html>\n", stdout);
  4122. }
  4123. }
  4124. /*
  4125. * get_str - returns a dupicate of string, s. The duplicate
  4126. * string is terminated at the next ',' or ']'.
  4127. */
  4128. static char *get_str (const char *s, char **n)
  4129. {
  4130. int i=0;
  4131. char *v;
  4132. while ((s[i] != (char)0) && (s[i] != ',') && (s[i] != ']'))
  4133. i++;
  4134. if (i>0) {
  4135. v = new char[i+1];
  4136. memcpy(v, s, i+1);
  4137. v[i] = (char)0;
  4138. if (s[i] == ',')
  4139. (*n) = (char *)&s[i+1];
  4140. else
  4141. (*n) = (char *)&s[i];
  4142. return v;
  4143. }
  4144. if (s[i] == ',')
  4145. (*n) = (char *)&s[1];
  4146. else
  4147. (*n) = (char *)s;
  4148. return NULL;
  4149. }
  4150. /*
  4151. * make_val - creates a string from if s is NULL.
  4152. */
  4153. char *make_val (char *s, int v, char *id, char *f, char *l)
  4154. {
  4155. if (s == NULL) {
  4156. char buf[30];
  4157. sprintf(buf, "%d", v);
  4158. return strsave(buf);
  4159. }
  4160. else {
  4161. /*
  4162. * check that value, s, is the same as, v.
  4163. */
  4164. char *t = s;
  4165. while (*t == '=')
  4166. t++;
  4167. if (atoi(t) != v) {
  4168. if (f == NULL)
  4169. f = (char *)"stdin";
  4170. if (l == NULL)
  4171. l = (char *)"<none>";
  4172. fprintf(stderr, "%s:%s: grohtml assertion failed at id%s expecting %d and was given %s\n",
  4173. f, l, id, v, s);
  4174. }
  4175. return s;
  4176. }
  4177. }
  4178. /*
  4179. * handle_assertion - handles the assertions created via .www:ASSERT
  4180. * in www.tmac. See www.tmac for examples.
  4181. * This method should be called as we are
  4182. * parsing the ditroff input. It checks the x, y
  4183. * position assertions. It does _not_ check the
  4184. * troff state assertions as these are unknown at this
  4185. * point.
  4186. */
  4187. void html_printer::handle_assertion (int minv, int minh, int maxv, int maxh, const char *s)
  4188. {
  4189. char *n;
  4190. char *cmd = get_str(s, &n);
  4191. char *id = get_str(n, &n);
  4192. char *val = get_str(n, &n);
  4193. char *file= get_str(n, &n);
  4194. char *line= get_str(n, &n);
  4195. if (strcmp(cmd, "assertion:[x") == 0)
  4196. as.addx(cmd, id, make_val(val, minh, id, file, line), file, line);
  4197. else if (strcmp(cmd, "assertion:[y") == 0)
  4198. as.addy(cmd, id, make_val(val, minv, id, file, line), file, line);
  4199. else
  4200. if (strncmp(cmd, "assertion:[", strlen("assertion:[")) == 0)
  4201. page_contents->add_tag(&sbuf_style, string(s),
  4202. line_number, minv, minh, maxv, maxh);
  4203. }
  4204. /*
  4205. * build_state_assertion - builds the troff state assertions.
  4206. */
  4207. void html_printer::handle_state_assertion (text_glob *g)
  4208. {
  4209. if (g != NULL && g->is_a_tag() &&
  4210. (strncmp(g->text_string, "assertion:[", 11) == 0)) {
  4211. char *n = (char *)&g->text_string[11];
  4212. char *cmd = get_str(n, &n);
  4213. char *val = get_str(n, &n);
  4214. (void)get_str(n, &n); // unused
  4215. char *file= get_str(n, &n);
  4216. char *line= get_str(n, &n);
  4217. as.build(cmd, val, file, line);
  4218. }
  4219. }
  4220. /*
  4221. * special - handle all x X requests from troff. For post-html they
  4222. * allow users to pass raw html commands, turn auto linked
  4223. * headings off/on etc.
  4224. */
  4225. void html_printer::special(char *s, const environment *env, char type)
  4226. {
  4227. if (type != 'p')
  4228. return;
  4229. if (s != 0) {
  4230. flush_sbuf();
  4231. if (env->fontno >= 0) {
  4232. style sty(get_font_from_index(env->fontno), env->size, env->height,
  4233. env->slant, env->fontno, *env->col);
  4234. sbuf_style = sty;
  4235. }
  4236. if (strncmp(s, "html:", 5) == 0) {
  4237. int r=font::res; /* resolution of the device */
  4238. font *f=sbuf_style.f;
  4239. if (f == NULL) {
  4240. int found=FALSE;
  4241. f = font::load_font("TR", &found);
  4242. }
  4243. /*
  4244. * need to pass rest of string through to html output during flush
  4245. */
  4246. page_contents->add_and_encode(&sbuf_style, string(&s[5]),
  4247. line_number,
  4248. env->vpos-env->size*r/72, env->hpos,
  4249. env->vpos , env->hpos,
  4250. FALSE);
  4251. /*
  4252. * assume that the html command has no width, if it does then
  4253. * hopefully troff will have fudged this in a macro by
  4254. * requesting that the formatting move right by the appropriate
  4255. * amount.
  4256. */
  4257. } else if (strncmp(s, "html</p>:", 9) == 0) {
  4258. int r=font::res; /* resolution of the device */
  4259. font *f=sbuf_style.f;
  4260. if (f == NULL) {
  4261. int found=FALSE;
  4262. f = font::load_font("TR", &found);
  4263. }
  4264. /*
  4265. * need to pass all of string through to html output during flush
  4266. */
  4267. page_contents->add_and_encode(&sbuf_style, string(s),
  4268. line_number,
  4269. env->vpos-env->size*r/72, env->hpos,
  4270. env->vpos , env->hpos,
  4271. TRUE);
  4272. /*
  4273. * assume that the html command has no width, if it does then
  4274. * hopefully troff will have fudged this in a macro by
  4275. * requesting that the formatting move right by the appropriate
  4276. * amount.
  4277. */
  4278. } else if (strncmp(s, "index:", 6) == 0) {
  4279. cutoff_heading = atoi(&s[6]);
  4280. } else if (strncmp(s, "assertion:[", 11) == 0) {
  4281. int r=font::res; /* resolution of the device */
  4282. handle_assertion(env->vpos-env->size*r/72, env->hpos,
  4283. env->vpos, env->hpos, s);
  4284. }
  4285. }
  4286. }
  4287. /*
  4288. * devtag - handles device troff tags sent from the `troff'.
  4289. * These include the troff state machine tags:
  4290. * .br, .sp, .in, .tl, .ll etc
  4291. *
  4292. * (see man 5 grohtml_tags).
  4293. */
  4294. void html_printer::devtag (char *s, const environment *env, char type)
  4295. {
  4296. if (type != 'p')
  4297. return;
  4298. if (s != 0) {
  4299. flush_sbuf();
  4300. if (env->fontno >= 0) {
  4301. style sty(get_font_from_index(env->fontno), env->size, env->height,
  4302. env->slant, env->fontno, *env->col);
  4303. sbuf_style = sty;
  4304. }
  4305. if (strncmp(s, "devtag:", strlen("devtag:")) == 0) {
  4306. int r=font::res; /* resolution of the device */
  4307. page_contents->add_tag(&sbuf_style, string(s),
  4308. line_number,
  4309. env->vpos-env->size*r/72, env->hpos,
  4310. env->vpos , env->hpos);
  4311. }
  4312. }
  4313. }
  4314. /*
  4315. * taken from number.cpp in src/roff/troff, [hunits::hunits(units x)]
  4316. */
  4317. int html_printer::round_width(int x)
  4318. {
  4319. int r = font::hor;
  4320. int n;
  4321. // don't depend on the rounding direction for division of negative integers
  4322. if (r == 1)
  4323. n = x;
  4324. else
  4325. n = (x < 0
  4326. ? -((-x + r/2 - 1)/r)
  4327. : (x + r/2 - 1)/r);
  4328. return n * r;
  4329. }
  4330. int main(int argc, char **argv)
  4331. {
  4332. program_name = argv[0];
  4333. static char stderr_buf[BUFSIZ];
  4334. setbuf(stderr, stderr_buf);
  4335. int c;
  4336. static const struct option long_options[] = {
  4337. { "help", no_argument, 0, CHAR_MAX + 1 },
  4338. { "version", no_argument, 0, 'v' },
  4339. { NULL, 0, 0, 0 }
  4340. };
  4341. while ((c = getopt_long(argc, argv, "a:bdD:F:g:hi:I:j:lno:prs:S:v",
  4342. long_options, NULL))
  4343. != EOF)
  4344. switch(c) {
  4345. case 'a':
  4346. /* text antialiasing bits - handled by pre-html */
  4347. break;
  4348. case 'b':
  4349. // set background color to white
  4350. default_background = new color;
  4351. default_background->set_gray(color::MAX_COLOR_VAL);
  4352. break;
  4353. case 'd':
  4354. /* handled by pre-html */
  4355. break;
  4356. case 'D':
  4357. /* handled by pre-html */
  4358. break;
  4359. case 'F':
  4360. font::command_line_font_dir(optarg);
  4361. break;
  4362. case 'g':
  4363. /* graphic antialiasing bits - handled by pre-html */
  4364. break;
  4365. case 'h':
  4366. /* do not use the Hn headings of html, but manufacture our own */
  4367. manufacture_headings = TRUE;
  4368. break;
  4369. case 'i':
  4370. /* handled by pre-html */
  4371. break;
  4372. case 'I':
  4373. /* handled by pre-html */
  4374. break;
  4375. case 'j':
  4376. multiple_files = TRUE;
  4377. job_name = optarg;
  4378. break;
  4379. case 'l':
  4380. auto_links = FALSE;
  4381. break;
  4382. case 'n':
  4383. simple_anchors = TRUE;
  4384. break;
  4385. case 'o':
  4386. /* handled by pre-html */
  4387. break;
  4388. case 'p':
  4389. /* handled by pre-html */
  4390. break;
  4391. case 'r':
  4392. auto_rule = FALSE;
  4393. break;
  4394. case 's':
  4395. base_point_size = atoi(optarg);
  4396. break;
  4397. case 'S':
  4398. split_level = atoi(optarg) + 1;
  4399. break;
  4400. case 'v':
  4401. printf("GNU post-grohtml (groff) version %s\n", Version_string);
  4402. exit(0);
  4403. break;
  4404. case CHAR_MAX + 1: // --help
  4405. usage(stdout);
  4406. exit(0);
  4407. break;
  4408. case '?':
  4409. usage(stderr);
  4410. exit(1);
  4411. break;
  4412. default:
  4413. assert(0);
  4414. }
  4415. if (optind >= argc) {
  4416. do_file("-");
  4417. } else {
  4418. for (int i = optind; i < argc; i++)
  4419. do_file(argv[i]);
  4420. }
  4421. return 0;
  4422. }
  4423. static void usage(FILE *stream)
  4424. {
  4425. fprintf(stream, "usage: %s [-vblnh] [-D dir] [-I image_stem] [-F dir] [files ...]\n",
  4426. program_name);
  4427. }