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