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