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