* html.cc (create_tmp_file, create_temp_name): Removed.
[s-roff.git] / src / devices / grohtml / html.cc
blobb94f55c182ebf09287f005c44a892080e5d70a1a
1 // -*- C++ -*-
2 /* Copyright (C) 1999 Free Software Foundation, Inc.
4 * Gaius Mulley (gaius@glam.ac.uk) wrote grohtml
5 * but it owes a huge amount of ideas and raw code from
6 * James Clark (jjc@jclark.com) grops/ps.cc.
7 */
9 /*
10 This file is part of groff.
12 groff is free software; you can redistribute it and/or modify it under
13 the terms of the GNU General Public License as published by the Free
14 Software Foundation; either version 2, or (at your option) any later
15 version.
17 groff is distributed in the hope that it will be useful, but WITHOUT ANY
18 WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 for more details.
22 You should have received a copy of the GNU General Public License along
23 with groff; see the file COPYING. If not, write to the Free Software
24 Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
26 #include "driver.h"
27 #include "stringclass.h"
28 #include "cset.h"
30 #include "html.h"
31 #include "html_chars.h"
32 #include <time.h>
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
38 #include <stdio.h>
39 #include <fcntl.h>
41 #include "ordered_list.h"
43 #if !defined(TRUE)
44 # define TRUE (1==1)
45 #endif
46 #if !defined(FALSE)
47 # define FALSE (1==0)
48 #endif
50 #define MAX_TEMP_NAME 1024
51 #define MAX_STRING_LENGTH 4096
52 #define MAX_CHAR_SIZE 50 // maximum length of character name
54 #define Y_FUDGE_MARGIN +0.83
55 #define A4_PAGE_LENGTH (11.6944-Y_FUDGE_MARGIN)
56 #define DEFAULT_IMAGE_RES 80
57 #define IMAGE_BOARDER_PIXELS 10
58 #define MAX_WORDS_PER_LINE 1000 // only used for table indentation
59 #define GAP_SPACES 3 // how many spaces needed to guess a gap?
60 #define GAP_WIDTH_ONE_LINE 2 // 1/GAP_WIDTH_ONE_LINE inches required for one line table
61 #define CENTER_TOLERANCE 2 // how many pixels off center will we think a line or region is centered
62 #define MIN_COLUMN 7 // minimum column size pixels for multiple lines
63 #define MIN_COLUMN_FOR_TWO_LINES 20 // minimum column size pixels for a 2 line table
64 #define MIN_TEXT_PERCENT 5 // try and round to this percentage value for used columns
65 #define PERCENT_THRESHOLD 20 // don't bother trying to increase and width greater than this
69 * Only uncomment one of the following to determine default image type.
72 #define IMAGE_DEFAULT_PNG
73 /* #define IMAGE_DEFAULT_GIF */
76 #if defined(IMAGE_DEFAULT_GIF)
77 static enum { gif, png } image_type = gif;
78 static char *image_device = "gif";
79 #elif defined(IMAGE_DEFAULT_PNG)
80 static enum { gif, png } image_type = png;
81 static char *image_device = "png256";
82 #else
83 # error "you must define either IMAGE_DEFAULT_GIF or IMAGE_DEFAULT_PNG"
84 #endif
86 static int debug_on = FALSE;
87 static int guess_on = TRUE;
88 static int margin_on = FALSE;
89 static int auto_on = TRUE;
90 static int table_on = TRUE;
91 static int image_res = DEFAULT_IMAGE_RES;
92 static int debug_table_on = FALSE;
93 static int table_image_on = TRUE; // default is to create images for tbl
95 static int linewidth = -1;
97 #define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
98 #define MAX_LINE_LENGTH 72
99 #define FILL_MAX 1000
101 void stop () {}
105 * start with a few favorites
108 static int min (int a, int b)
110 if (a < b) {
111 return( a );
112 } else {
113 return( b );
117 static int max (int a, int b)
119 if (a > b) {
120 return( a );
121 } else {
122 return( b );
127 * is_subsection - returns TRUE if a1..a2 is within b1..b2
130 static int is_subsection (int a1, int a2, int b1, int b2)
132 // easier to see whether this is not the case
133 return( !((a1 < b1) || (a1 > b2) || (a2 < b1) || (a2 > b2)) );
137 * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
140 static int is_intersection (int a1, int a2, int b1, int b2)
142 // again easier to prove NOT outside limits
143 return( ! ((a1 > b2) || (a2 < b1)) );
147 * is_digit - returns TRUE if character, ch, is a digit.
150 static int is_digit (char ch)
152 return( (ch >= '0') && (ch <= '9') );
156 * more_than_line_break - returns TRUE should v1 and v2 differ by more than
157 * a simple line break.
160 static int more_than_line_break (int v1, int v2, int size)
162 return( abs(v1-v2)>size );
166 * the class and methods for styles
169 struct style {
170 font *f;
171 int point_size;
172 int font_no;
173 int height;
174 int slant;
175 style ();
176 style (font *, int, int, int, int);
177 int operator == (const style &) const;
178 int operator != (const style &) const;
181 style::style()
182 : f(0)
186 style::style(font *p, int sz, int h, int sl, int no)
187 : f(p), point_size(sz), font_no(no), height(h), slant(sl)
191 int style::operator==(const style &s) const
193 return (f == s.f && point_size == s.point_size
194 && height == s.height && slant == s.slant);
197 int style::operator!=(const style &s) const
199 return !(*this == s);
204 * the class and methods for retaining ascii text
207 struct char_block {
208 enum { SIZE = 256 };
209 char buffer[SIZE];
210 int used;
211 char_block *next;
213 char_block();
216 char_block::char_block()
217 : used(0), next(0)
221 class char_buffer {
222 public:
223 char_buffer();
224 ~char_buffer();
225 char *add_string(char *, unsigned int);
226 private:
227 char_block *head;
228 char_block *tail;
231 char_buffer::char_buffer()
232 : head(0), tail(0)
236 char_buffer::~char_buffer()
238 while (head != 0) {
239 char_block *temp = head;
240 head = head->next;
241 delete temp;
245 char *char_buffer::add_string (char *s, unsigned int length)
247 int i=0;
248 unsigned int old_used;
250 if (tail == 0) {
251 tail = new char_block;
252 head = tail;
253 } else {
254 if (tail->used + length+1 > char_block::SIZE) {
255 tail->next = new char_block;
256 tail = tail->next;
259 // at this point we have a tail which is ready for the string.
260 if (tail->used + length+1 > char_block::SIZE) {
261 fatal("need to increase char_block::SIZE");
264 old_used = tail->used;
265 do {
266 tail->buffer[tail->used] = s[i];
267 tail->used++;
268 i++;
269 length--;
270 } while (length>0);
272 // add terminating nul character
274 tail->buffer[tail->used] = '\0';
275 tail->used++;
277 // and return start of new string
279 return( &tail->buffer[old_used] );
283 * the classes and methods for maintaining pages and text positions and graphic regions
286 class text_glob {
287 public:
288 int is_less (text_glob *a, text_glob *b);
289 text_glob (style *s, char *string, unsigned int length,
290 int min_vertical, int min_horizontal,
291 int max_vertical, int max_horizontal, int is_command, int is_html);
292 text_glob (void);
293 ~text_glob (void);
295 style text_style;
296 char *text_string;
297 unsigned int text_length;
298 int minv, maxv, minh, maxh;
299 int is_raw_command; // should the text be sent directly to the device?
300 int is_html_command; // is the raw command definitely for the html device ie not an eqn?
303 text_glob::text_glob (style *s, char *string, unsigned int length,
304 int min_vertical, int min_horizontal,
305 int max_vertical, int max_horizontal, int is_command, int is_html)
306 : text_style(*s), text_string(string), text_length(length),
307 minv(min_vertical), maxv(max_vertical), minh(min_horizontal), maxh(max_horizontal),
308 is_raw_command(is_command), is_html_command(is_html)
312 text_glob::text_glob ()
313 : text_string(0), text_length(0), minv(-1), maxv(-1), minh(-1), maxh(-1),
314 is_raw_command(FALSE), is_html_command(FALSE)
318 text_glob::~text_glob ()
322 int text_glob::is_less (text_glob *a, text_glob *b)
324 if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
325 return( a->minh < b->minh );
326 } else {
327 return( a->maxv < b->maxv );
331 struct xycoord {
332 int x;
333 int y;
336 class graphic_glob {
337 public:
338 int is_less (graphic_glob *a, graphic_glob *b);
339 graphic_glob (int troff_code);
340 graphic_glob (void);
341 ~graphic_glob (void);
343 int minv, maxv, minh, maxh;
344 int xc, yc;
345 int nopoints; // number of points allocated in array below
346 struct xycoord *point;
347 int size;
348 int fill;
349 int code;
352 graphic_glob::graphic_glob ()
353 : minv(-1), maxv(-1), minh(-1), maxh(-1), nopoints(0), point(0), size(0), code(0)
357 graphic_glob::~graphic_glob ()
359 if (point != 0) {
360 free(point);
364 graphic_glob::graphic_glob (int troff_code)
365 : minv(-1), maxv(-1), minh(-1), maxh(-1), nopoints(0), point(0), size(0), code(troff_code)
369 int graphic_glob::is_less (graphic_glob *a, graphic_glob *b)
371 return( (a->minv < b->minv) || ((a->minv == b->minv) && (a->minh < b->minh)) );
374 class region_glob {
375 public:
376 region_glob (void);
377 ~region_glob (void);
378 int is_less (region_glob *a, region_glob *b);
380 int minv, maxv, minh, maxh;
383 int region_glob::is_less (region_glob *a, region_glob *b)
385 return( (a->minv < b->minv) || ((a->minv == b->minv) && (a->minh < b->minh)) );
388 region_glob::region_glob (void)
389 : minv(-1), maxv(-1), minh(-1), maxh(-1)
393 region_glob::~region_glob (void)
397 class page {
398 public:
399 page (void);
400 void add (style *s, char *string, unsigned int length,
401 int min_vertical, int min_horizontal,
402 int max_vertical, int max_horizontal);
403 void add_html_command (style *s, char *string, unsigned int length,
404 int min_vertical, int min_horizontal,
405 int max_vertical, int max_horizontal);
406 void add_special_char (style *s, char *string, unsigned int length,
407 int min_vertical, int min_horizontal,
408 int max_vertical, int max_horizontal);
409 void add_line (int code, int x1, int y1, int x2, int y2, int size, int fill);
410 void add_arc (int code, int xc, int yc, int *p, double *c, int size, int fill);
411 void add_polygon (int code, int np, int *p, int oh, int ov, int size, int fill);
412 void add_spline (int code, int xc, int yc, int np, int *p, int size, int fill);
413 void calculate_region (void);
414 int is_in_region (graphic_glob *g);
415 int can_grow_region (graphic_glob *g);
416 void make_new_region (graphic_glob *g);
417 int has_line (region_glob *r);
418 int has_word (region_glob *r);
419 int no_raw_commands (int minv, int maxv);
421 // and the data
423 ordered_list <region_glob> regions; // squares of bitmapped pics,eqn,tbl's
424 ordered_list <text_glob> words; // position of words on page
425 ordered_list <graphic_glob> lines; // position of lines on page
426 char_buffer buffer; // all characters for this page
427 int is_in_graphic; // should graphics and words go below or above
428 ordered_list <text_glob> region_words; // temporary accumulation of words in a region
429 ordered_list <graphic_glob> region_lines; // (as above) and used so that we can determine
430 // the regions vertical limits
433 page::page()
434 : is_in_graphic(FALSE)
438 void page::add (style *s, char *string, unsigned int length,
439 int min_vertical, int min_horizontal,
440 int max_vertical, int max_horizontal)
442 if (length > 0) {
443 text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
444 min_vertical, min_horizontal, max_vertical, max_horizontal, FALSE, FALSE);
445 if (is_in_graphic) {
446 region_words.add(g);
447 } else {
448 words.add(g);
454 * add_html_command - it only makes sense to add html commands when we are not inside
455 * a graphical entity.
458 void page::add_html_command (style *s, char *string, unsigned int length,
459 int min_vertical, int min_horizontal,
460 int max_vertical, int max_horizontal)
462 if ((length > 0) && (! is_in_graphic)) {
463 text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
464 min_vertical, min_horizontal, max_vertical, max_horizontal, TRUE, TRUE);
465 words.add(g);
470 * add_special_char - it only makes sense to add special characters when we are inside
471 * a graphical entity.
474 void page::add_special_char (style *s, char *string, unsigned int length,
475 int min_vertical, int min_horizontal,
476 int max_vertical, int max_horizontal)
478 if ((length > 0) && (is_in_graphic)) {
479 text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
480 min_vertical, min_horizontal, max_vertical, max_horizontal, TRUE, FALSE);
481 region_words.add(g);
485 void page::add_line (int code, int x1, int y1, int x2, int y2, int size, int fill)
487 graphic_glob *g = new graphic_glob(code);
489 g->minh = min(x1, x2);
490 g->maxh = max(x1, x2);
491 g->minv = min(y1, y2);
492 g->maxv = max(y1, y2);
493 g->point = (struct xycoord *)malloc(sizeof(xycoord)*2);
494 g->nopoints = 2;
495 g->point[0].x = x1 ;
496 g->point[0].y = y1 ;
497 g->point[1].x = x2 ;
498 g->point[1].y = y2 ;
499 g->xc = 0;
500 g->yc = 0;
501 g->size = size;
502 g->fill = fill;
504 if (is_in_graphic) {
505 region_lines.add(g);
506 } else {
507 lines.add(g);
512 * assign_min_max_for_arc - works out the smallest box that will encompass an
513 * arc defined by: origin: g->xc, g->xc
514 * and vector (p[0], p[1]) and (p[2], p[3])
517 void assign_min_max_for_arc (graphic_glob *g, int *p, double *c)
519 int radius = (int) sqrt(c[0]*c[0]+c[1]*c[1]);
520 int xv1 = p[0];
521 int yv1 = p[1];
522 int xv2 = p[2];
523 int yv2 = p[3];
524 int x1 = g->xc+xv1;
525 int y1 = g->yc+yv1;
526 int x2 = g->xc+xv1+xv2;
527 int y2 = g->yc+yv1+yv2;
529 // firstly lets use the 'circle' limitation
530 g->minh = x1-radius;
531 g->maxh = x1+radius;
532 g->minv = y1-radius;
533 g->maxv = y1+radius;
535 // incidentally I'm sure there is a better way to do this, but I don't know it
536 // please can someone let me know or "improve" this function
538 // now see which min/max can be reduced and increased for the limits of the arc
541 // Q2 | Q1
542 // -----+-----
543 // Q3 | Q4
547 if ((xv1>=0) && (yv1>=0)) {
548 // first vector in Q3
549 if ((xv2>=0) && (yv2>=0)) {
550 // second in Q1
551 g->maxh = x2;
552 g->minv = y1;
553 } else if ((xv2<0) && (yv2>=0)) {
554 // second in Q2
555 g->maxh = x2;
556 g->minv = y1;
557 } else if ((xv2>=0) && (yv2<0)) {
558 // second in Q4
559 g->minv = min(y1, y2);
560 } else if ((xv2<0) && (yv2<0)) {
561 // second in Q3
562 if (x1>=x2) {
563 g->minh = x2;
564 g->maxh = x1;
565 g->minv = min(y1, y2);
566 g->maxv = max(y1, y2);
567 } else {
568 // xv2, yv2 could all be zero?
571 } else if ((xv1>=0) && (yv1<0)) {
572 // first vector in Q2
573 if ((xv2>=0) && (yv2>=0)) {
574 // second in Q1
575 g->maxh = max(x1, x2);
576 g->minh = min(x1, x2);
577 g->minv = y1;
578 } else if ((xv2<0) && (yv2>=0)) {
579 // second in Q2
580 if (x1<x2) {
581 g->maxh = x2;
582 g->minh = x1;
583 g->minv = min(y1, y2);
584 g->maxv = max(y1, y2);
585 } else {
586 // otherwise almost full circle anyway
588 } else if ((xv2>=0) && (yv2<0)) {
589 // second in Q4
590 g->minv = y2;
591 g->minh = x1;
592 } else if ((xv2<0) && (yv2<0)) {
593 // second in Q3
594 g->minh = min(x1, x2);
596 } else if ((xv1<0) && (yv1<0)) {
597 // first vector in Q1
598 if ((xv2>=0) && (yv2>=0)) {
599 // second in Q1
600 if (x1<x2) {
601 g->minh = x1;
602 g->maxh = x2;
603 g->minv = min(y1, y2);
604 g->maxv = max(y1, y2);
605 } else {
606 // nearly full circle
608 } else if ((xv2<0) && (yv2>=0)) {
609 // second in Q2
610 g->maxv = max(y1, y2);
611 } else if ((xv2>=0) && (yv2<0)) {
612 // second in Q4
613 g->minv = min(y1, y2);
614 g->maxv = max(y1, y2);
615 g->minh = min(x1, x2);
616 } else if ((xv2<0) && (yv2<0)) {
617 // second in Q3
618 g->minh = x2;
619 g->maxv = y1;
621 } else if ((xv1<0) && (yv1>=0)) {
622 // first vector in Q4
623 if ((xv2>=0) && (yv2>=0)) {
624 // second in Q1
625 g->maxh = max(x1, x2);
626 } else if ((xv2<0) && (yv2>=0)) {
627 // second in Q2
628 g->maxv = max(y1, y2);
629 g->maxh = max(x1, x2);
630 } else if ((xv2>=0) && (yv2<0)) {
631 // second in Q4
632 if (x1>=x2) {
633 g->minv = min(y1, y2);
634 g->maxv = max(y1, y2);
635 g->minh = min(x1, x2);
636 g->maxh = max(x2, x2);
637 } else {
638 // nearly full circle
640 } else if ((xv2<0) && (yv2<0)) {
641 // second in Q3
642 g->maxv = max(y1, y2);
643 g->minh = min(x1, x2);
644 g->maxh = max(x1, x2);
647 // this should *never* happen but if it does it means a case above is wrong..
649 // this code is only present for safety sake
650 if (g->maxh < g->minh) {
651 if (debug_on) {
652 fprintf(stderr, "assert failed minh > maxh\n"); fflush(stderr);
653 // stop();
655 g->maxh = g->minh;
657 if (g->maxv < g->minv) {
658 if (debug_on) {
659 fprintf(stderr, "assert failed minv > maxv\n"); fflush(stderr);
660 // stop();
662 g->maxv = g->minv;
666 void page::add_arc (int code, int xc, int yc, int *p, double *c, int size, int fill)
668 graphic_glob *g = new graphic_glob(code);
670 g->point = (struct xycoord *)malloc(sizeof(xycoord)*2);
671 g->nopoints = 2;
672 g->point[0].x = p[0] ;
673 g->point[0].y = p[1] ;
674 g->point[1].x = p[2] ;
675 g->point[1].y = p[3] ;
676 g->xc = xc;
677 g->yc = yc;
678 g->size = size;
679 g->fill = fill;
681 assign_min_max_for_arc(g, p, c);
683 if (is_in_graphic) {
684 region_lines.add(g);
685 } else {
686 lines.add(g);
691 void page::add_polygon (int code, int np, int *p, int oh, int ov, int size, int fill)
693 graphic_glob *g = new graphic_glob(code);
694 int j = 0;
695 int i;
697 g->point = (struct xycoord *)malloc(sizeof(xycoord)*np/2);
698 g->nopoints = np/2;
700 for (i=0; i<g->nopoints; i++) {
701 g->point[i].x = p[j];
702 j++;
703 g->point[i].y = p[j];
704 j++;
706 // now calculate min/max
707 g->minh = g->point[0].x;
708 g->minv = g->point[0].y;
709 g->maxh = g->point[0].x;
710 g->maxv = g->point[0].y;
711 for (i=1; i<g->nopoints; i++) {
712 g->minh = min(g->minh, g->point[i].x);
713 g->minv = min(g->minv, g->point[i].y);
714 g->maxh = max(g->maxh, g->point[i].x);
715 g->maxv = max(g->maxv, g->point[i].y);
717 g->size = size;
718 g->xc = oh;
719 g->yc = ov;
720 g->fill = fill;
722 if (is_in_graphic) {
723 region_lines.add(g);
724 } else {
725 lines.add(g);
729 void page::add_spline (int code, int xc, int yc, int np, int *p, int size, int fill)
731 graphic_glob *g = new graphic_glob(code);
732 int j = 0;
733 int i;
735 g->point = (struct xycoord *)malloc(sizeof(xycoord)*np/2);
736 g->nopoints = np/2;
738 for (i=0; i<g->nopoints; i++) {
739 g->point[i].x = p[j];
740 j++;
741 g->point[i].y = p[j];
742 j++;
744 // now calculate min/max
745 g->minh = min(g->point[0].x, g->point[0].x/2);
746 g->minv = min(g->point[0].y, g->point[0].y/2);
747 g->maxh = max(g->point[0].x, g->point[0].x/2);
748 g->maxv = max(g->point[0].y, g->point[0].y/2);
750 /* tnum/tden should be between 0 and 1; the closer it is to 1
751 the tighter the curve will be to the guiding lines; 2/3
752 is the standard value */
753 const int tnum = 2;
754 const int tden = 3;
756 for (i=1; i<g->nopoints-1; i++) {
757 g->minh = min(g->minh, g->point[i].x*tnum/(2*tden));
758 g->minv = min(g->minv, g->point[i].y*tnum/(2*tden));
759 g->maxh = max(g->maxh, g->point[i].x*tnum/(2*tden));
760 g->maxv = max(g->maxv, g->point[i].y*tnum/(2*tden));
762 g->minh = min(g->minh, g->point[i].x/2+(g->point[i+1].x*(tden-tden))/(2*tden));
763 g->minv = min(g->minv, g->point[i].y/2+(g->point[i+1].y*(tden-tden))/(2*tden));
764 g->maxh = max(g->maxh, g->point[i].x/2+(g->point[i+1].x*(tden-tden))/(2*tden));
765 g->maxv = max(g->maxv, g->point[i].y/2+(g->point[i+1].y*(tden-tden))/(2*tden));
767 g->minh = min(g->minh, (g->point[i].x-g->point[i].x/2) + g->point[i+1].x/2);
768 g->minv = min(g->minv, (g->point[i].y-g->point[i].y/2) + g->point[i+1].y/2);
769 g->maxh = max(g->maxh, (g->point[i].x-g->point[i].x/2) + g->point[i+1].x/2);
770 g->maxv = max(g->maxv, (g->point[i].y-g->point[i].y/2) + g->point[i+1].y/2);
772 i = g->nopoints-1;
774 g->minh = min(g->minh, (g->point[i].x-g->point[i].x/2)) + xc;
775 g->minv = min(g->minv, (g->point[i].y-g->point[i].y/2)) + yc;
776 g->maxh = max(g->maxh, (g->point[i].x-g->point[i].x/2)) + xc;
777 g->maxv = max(g->maxv, (g->point[i].y-g->point[i].y/2)) + yc;
779 g->size = size;
780 g->xc = xc;
781 g->yc = yc;
782 g->fill = fill;
784 if (is_in_graphic) {
785 region_lines.add(g);
786 } else {
787 lines.add(g);
791 class html_font : public font {
792 html_font(const char *);
793 public:
794 int encoding_index;
795 char *encoding;
796 char *reencoded_name;
797 ~html_font();
798 void handle_unknown_font_command(const char *command, const char *arg,
799 const char *filename, int lineno);
800 static html_font *load_html_font(const char *);
803 html_font *html_font::load_html_font(const char *s)
805 html_font *f = new html_font(s);
806 if (!f->load()) {
807 delete f;
808 return 0;
810 return f;
813 html_font::html_font(const char *nm)
814 : font(nm)
818 html_font::~html_font()
822 void html_font::handle_unknown_font_command(const char *command, const char *arg,
823 const char *filename, int lineno)
825 if (strcmp(command, "encoding") == 0) {
826 if (arg == 0)
827 error_with_file_and_line(filename, lineno,
828 "`encoding' command requires an argument");
829 else
830 encoding = strsave(arg);
836 * a simple class to contain the header to this document
839 class title_desc {
840 public:
841 title_desc ();
842 ~title_desc ();
844 int has_been_written;
845 int has_been_found;
846 char text[MAX_STRING_LENGTH];
850 title_desc::title_desc ()
851 : has_been_written(FALSE), has_been_found(FALSE)
855 title_desc::~title_desc ()
859 class header_desc {
860 public:
861 header_desc ();
862 ~header_desc ();
864 int no_of_headings; // how many headings have we found?
865 char_buffer headings; // all the headings used in the document
866 ordered_list <text_glob> headers;
867 int header_level; // current header level
868 int written_header; // have we written the header yet?
869 char header_buffer[MAX_STRING_LENGTH]; // current header text
871 void write_headings (FILE *f);
874 header_desc::header_desc ()
875 : no_of_headings(0), header_level(2), written_header(0)
879 header_desc::~header_desc ()
884 * paragraph_type - alignment for a new paragraph
887 typedef enum { left_alignment, center_alignment } paragraph_type;
890 * text_defn - defines the limit of text, initially these are stored in the
891 * column array as words. Later we examine the white space between
892 * the words in successive lines to find out whether we can detect
893 * distinct columns. The columns are generated via html tables.
896 struct text_defn {
897 int left; // the start of a word or text
898 int right; // the end of the text and beginning of white space
899 int is_used; // will this this column be used for words or space
900 int right_hits; // count of the number of words touching right position
901 int percent; // what percentage width should we use for this cell?
905 * introduce a paragraph class so that we can nest paragraphs
906 * from plain html text and html tables.
909 class html_paragraph {
910 public:
911 html_paragraph (int in, int need, paragraph_type type, html_paragraph *prev);
912 ~html_paragraph ();
914 int in_paragraph;
915 int need_paragraph;
916 paragraph_type para_type;
917 html_paragraph *previous;
921 * html_paragraph - constructor, fill in the public fields.
924 html_paragraph::html_paragraph (int in, int need, paragraph_type type, html_paragraph *prev)
925 : in_paragraph(in), need_paragraph(need),
926 para_type(type), previous(prev)
931 * html_paragraph - deconstructor
934 html_paragraph::~html_paragraph ()
939 * note that html_tables are currently only used to provide a better
940 * indentation mechanism for html text (in particular it allows grohtml
941 * to render .IP and .2C together with autoformatting).
944 class html_table {
945 public:
946 html_table ();
947 ~html_table ();
949 int no_of_columns; // how many columns are we using?
950 struct text_defn *columns; // left and right margins for each column
951 int vertical_limit; // the limit of the table
952 int wrap_margin; // is the current rightmost margin able to wrap words?
955 html_table::html_table ()
956 : no_of_columns(0), columns(0), vertical_limit(0), wrap_margin(0)
960 html_table::~html_table ()
964 class html_printer : public printer {
965 FILE *tempfp;
966 simple_output html;
967 simple_output troff;
968 int res;
969 int postscript_res;
970 int space_char_index;
971 int no_of_printed_pages;
972 int paper_length;
973 enum { SBUF_SIZE = 8192 };
974 char sbuf[SBUF_SIZE];
975 int sbuf_len;
976 int sbuf_start_hpos;
977 int sbuf_vpos;
978 int sbuf_end_hpos;
979 int sbuf_kern;
980 style sbuf_style;
981 int sbuf_dmark_hpos;
982 style output_style;
983 int output_hpos;
984 int output_vpos;
985 int output_draw_point_size;
986 int line_thickness;
987 int output_line_thickness;
988 int fill;
989 unsigned char output_space_code;
990 string defs;
991 char *inside_font_style;
992 int page_number;
993 title_desc title;
994 header_desc header;
995 int header_indent;
996 page *page_contents;
997 html_table indentation;
998 int left_margin_indent;
999 int right_margin_indent;
1000 int need_one_newline;
1001 int issued_newline;
1002 html_paragraph *current_paragraph;
1003 char image_name[MAX_STRING_LENGTH];
1004 int image_number;
1005 int graphic_level;
1006 int supress_sub_sup;
1008 int start_region_vpos;
1009 int start_region_hpos;
1010 int end_region_vpos;
1011 int end_region_hpos;
1012 int cutoff_heading;
1014 struct graphic_glob *start_graphic;
1015 struct text_glob *start_text;
1017 void flush_sbuf ();
1018 void set_style (const style &);
1019 void set_space_code (unsigned char c);
1020 void do_exec (char *, const environment *);
1021 void do_import (char *, const environment *);
1022 void do_def (char *, const environment *);
1023 void do_mdef (char *, const environment *);
1024 void do_file (char *, const environment *);
1025 void set_line_thickness (const environment *);
1026 void change_font (text_glob *g, int is_to_html);
1027 void terminate_current_font (void);
1028 void flush_font (void);
1029 void flush_page (void);
1030 void add_char_to_sbuf (unsigned char code);
1031 void add_to_sbuf (char code, const char *name);
1032 void display_word (text_glob *g, int is_to_html);
1033 void html_display_word (text_glob *g);
1034 void troff_display_word (text_glob *g);
1035 void display_line (graphic_glob *g, int is_to_html);
1036 void display_fill (graphic_glob *g);
1037 void calculate_margin (void);
1038 void traverse_page_regions (void);
1039 void dump_page (void);
1040 int is_within_region (graphic_glob *g);
1041 int is_within_region (text_glob *t);
1042 int is_less (graphic_glob *g, text_glob *t);
1043 void display_globs (int is_to_html);
1044 void move_horizontal (text_glob *g, int left_margin);
1045 void move_vertical (text_glob *g, paragraph_type p);
1046 void write_html_font_face (const char *fontname, const char *left, const char *right);
1047 void write_html_font_type (const char *fontname, const char *left, const char *right);
1048 void html_change_font (text_glob *g, const char *fontname, int size);
1049 char *html_position_text (text_glob *g, int left_margin, int right_margin);
1050 int html_position_region (void);
1051 void troff_change_font (const char *fontname, int size, int font_no);
1052 void troff_position_text (text_glob *g);
1053 int pretend_is_on_same_line (text_glob *g, int left_margin, int right_margin);
1054 int is_on_same_line (text_glob *g, int vpos);
1055 int looks_like_subscript (text_glob *g);
1056 int looks_like_superscript (text_glob *g);
1057 int looks_like_smaller_font (text_glob *g);
1058 int looks_like_larger_font (text_glob *g);
1059 void begin_paragraph (paragraph_type p);
1060 void begin_paragraph_no_height (paragraph_type p);
1061 void force_begin_paragraph (void);
1062 void end_paragraph (void);
1063 void save_paragraph (void);
1064 void restore_paragraph (void);
1065 void html_newline (void);
1066 void convert_to_image (char *troff_src, char *image_name);
1067 void write_title (int in_head);
1068 void find_title (void);
1069 int is_bold (text_glob *g);
1070 void write_header (text_glob *g);
1071 void determine_header_level (void);
1072 void build_header (text_glob *g);
1073 void make_html_indent (int indent);
1074 int is_whole_line_bold (text_glob *g);
1075 int is_a_header (text_glob *g);
1076 int processed_header (text_glob *g);
1077 void make_new_image_name (void);
1078 void calculate_region_margins (region_glob *r);
1079 void remove_redundant_regions (void);
1080 void remove_duplicate_regions (void);
1081 void move_region_to_page (void);
1082 void calculate_region_range (graphic_glob *r);
1083 void flush_graphic (void);
1084 void write_string (graphic_glob *g, int is_to_html);
1085 void prologue (void);
1086 int gs_x (int x);
1087 int gs_y (int y);
1088 void display_regions (void);
1089 int check_able_to_use_table (text_glob *g);
1090 int using_table_for_indent (void);
1091 int collect_columns (struct text_defn *next_words, struct text_defn *next_cols,
1092 struct text_defn *last_words, struct text_defn *last_cols,
1093 int max_words);
1094 void include_into_list (struct text_defn *line, struct text_defn *item);
1095 int is_in_column (struct text_defn *line, struct text_defn *item, int max_words);
1096 int is_column_match (struct text_defn *match, struct text_defn *line1,
1097 struct text_defn *line2, int max_words);
1098 int count_columns (struct text_defn *line);
1099 void rewind_text_to (text_glob *g);
1100 int found_use_for_table (text_glob *start);
1101 void column_display_word (int cell, int vert, int left, int right, int next);
1102 void start_table (void);
1103 void end_table (void);
1104 void foreach_column_include_text (text_glob *start);
1105 void define_cell (int i);
1106 int column_calculate_left_margin (int left, int right);
1107 int column_calculate_right_margin (int left, int right);
1108 void display_columns (const char *word, const char *name, text_defn *line);
1109 void calculate_right (struct text_defn *line, int max_words);
1110 void determine_right_most_column (struct text_defn *line, int max_words);
1111 int remove_white_using_words (struct text_defn *next_guess, struct text_defn *last_guess, struct text_defn *next_line);
1112 void copy_line (struct text_defn *dest, struct text_defn *src);
1113 void combine_line (struct text_defn *dest, struct text_defn *src);
1114 int conflict_with_words (struct text_defn *column_guess, struct text_defn *words);
1115 void remove_entry_in_line (struct text_defn *line, int j);
1116 void remove_redundant_columns (struct text_defn *line);
1117 void add_column_gaps (struct text_defn *line);
1118 int continue_searching_column (text_defn *next_col, text_defn *last_col, text_defn *all_words);
1119 void add_right_full_width (struct text_defn *line, int mingap);
1120 int is_continueous_column (text_defn *last_col, text_defn *next_line);
1121 int is_exact_left (text_defn *last_col, text_defn *next_line);
1122 int find_column_index_in_line (text_glob *t, text_defn *line);
1123 void emit_space (text_glob *g, int force_space);
1124 int is_in_middle (int left, int right);
1125 int check_able_to_use_center (text_glob *g);
1126 void write_centered_line (text_glob *g);
1127 int single_centered_line (text_defn *first, text_defn *second, text_glob *g);
1128 int determine_row_limit (text_glob *start, int v);
1129 void assign_used_columns (text_glob *start);
1130 int find_column_index (text_glob *t);
1131 int large_enough_gap (text_defn *last_col);
1132 int is_worth_column (int left, int right);
1133 int is_subset_of_columns (text_defn *a, text_defn *b);
1134 void count_hits (text_defn *col, int no_of_columns, int limit);
1135 void count_right_hits (text_defn *col, int no_of_columns);
1136 int calculate_min_gap (text_glob *g);
1137 int right_indentation (struct text_defn *last_guess);
1138 void calculate_percentage_width (text_glob *start);
1139 int able_to_steal_width (void);
1140 int need_to_steal_width (void);
1141 int can_distribute_fairly (void);
1142 void utilize_round_off (void);
1143 int will_wrap_text (int i, text_glob *start);
1144 int next_line_on_left_column (int i, text_glob *start);
1145 void remove_table_column (int i);
1146 void remove_unnecessary_unused (text_glob *start);
1147 int is_small_table (int lines, struct text_defn *last_guess,
1148 struct text_defn *words_1, struct text_defn *cols_1,
1149 struct text_defn *words_2, struct text_defn *cols_2,
1150 int *limit, int *limit_1);
1151 int is_column_subset (struct text_defn *cols_1, struct text_defn *cols_2);
1152 int is_appropriate_to_start_table (struct text_defn *cols_1, struct text_defn *cols_2,
1153 struct text_defn *last_guess);
1154 int is_a_full_width_column (void);
1155 int right_most_column (struct text_defn *col);
1156 int large_enough_gap_for_two (struct text_defn *col);
1157 void remove_zero_percentage_column (void);
1158 void translate_to_html (text_glob *g);
1159 int html_knows_about (char *troff);
1160 void determine_diacritical_mark (const char *name, const environment *env);
1161 int sbuf_continuation (unsigned char code, const char *name, const environment *env, int w);
1162 char *remove_last_char_from_sbuf ();
1163 const char *check_diacritical_combination (unsigned char code, const char *name);
1164 int seen_backwards_escape (char *s, int l);
1165 int should_defer_table (int lines, struct text_glob *start, struct text_defn *cols_1);
1166 int is_new_exact_right (struct text_defn *last_guess, struct text_defn *last_cols, struct text_defn *next_cols);
1167 void issue_left_paragraph (void);
1168 void adjust_margin_percentages (void);
1169 int total_percentages (void);
1170 int get_left (void);
1171 void can_loose_column (text_glob *start, struct text_defn *last_guess, int limit);
1172 int check_lack_of_hits (struct text_defn *next_guess, struct text_defn *last_guess, text_glob *start, int limit);
1174 // ADD HERE
1176 public:
1177 html_printer();
1178 ~html_printer();
1179 void set_char(int i, font *f, const environment *env, int w, const char *name);
1180 void draw(int code, int *p, int np, const environment *env);
1181 void begin_page(int);
1182 void end_page(int);
1183 void special(char *arg, const environment *env);
1184 font *make_font(const char *);
1185 void end_of_line();
1188 html_printer::html_printer()
1189 : html(0, MAX_LINE_LENGTH),
1190 troff(0, MAX_LINE_LENGTH),
1191 no_of_printed_pages(0),
1192 sbuf_len(0),
1193 sbuf_dmark_hpos(-1),
1194 output_hpos(-1),
1195 output_vpos(-1),
1196 line_thickness(-1),
1197 fill(FILL_MAX + 1),
1198 inside_font_style(0),
1199 page_number(0),
1200 header_indent(-1),
1201 left_margin_indent(0),
1202 right_margin_indent(0),
1203 need_one_newline(0),
1204 issued_newline(0),
1205 image_number(0),
1206 graphic_level(0),
1207 supress_sub_sup(TRUE),
1208 start_region_vpos(0),
1209 start_region_hpos(0),
1210 end_region_vpos(0),
1211 end_region_hpos(0),
1212 cutoff_heading(100)
1214 tempfp = xtmpfile();
1215 html.set_file(tempfp);
1216 if (linewidth < 0)
1217 linewidth = DEFAULT_LINEWIDTH;
1218 if (font::hor != 1)
1219 fatal("horizontal resolution must be 1");
1220 if (font::vert != 1)
1221 fatal("vertical resolution must be 1");
1222 #if 0
1223 // should be sorted html..
1224 if (font::res % (font::sizescale*72) != 0)
1225 fatal("res must be a multiple of 72*sizescale");
1226 #endif
1227 int r = font::res;
1228 int point = 0;
1229 while (r % 10 == 0) {
1230 r /= 10;
1231 point++;
1233 res = r;
1234 html.set_fixed_point(point);
1235 space_char_index = font::name_to_index("space");
1236 paper_length = font::paperlength;
1237 if (paper_length == 0)
1238 paper_length = 11*font::res;
1239 page_contents = new page;
1241 postscript_res = 72000;
1242 current_paragraph = new html_paragraph(FALSE, FALSE, left_alignment, 0);
1246 * add_char_to_sbuf - adds a single character to the sbuf.
1249 void html_printer::add_char_to_sbuf (unsigned char code)
1251 if (sbuf_len < SBUF_SIZE) {
1252 sbuf[sbuf_len] = code;
1253 sbuf_len++;
1254 } else {
1255 fatal("need to increase SBUF_SIZE");
1260 * add_to_sbuf - adds character code or name to the sbuf.
1261 * It escapes \ with \\
1262 * We need to preserve the name of characters if they exist
1263 * because we may need to send this character to two different
1264 * devices: html and postscript.
1267 void html_printer::add_to_sbuf (char code, const char *name)
1269 if (name == 0) {
1270 if (code == '\\') {
1271 add_char_to_sbuf('\\');
1273 add_char_to_sbuf(code);
1274 } else {
1275 int l=strlen(name);
1276 int i=0;
1278 add_char_to_sbuf('\\');
1279 add_char_to_sbuf('(');
1280 while (i<l) {
1281 if (name[i] == '\\') {
1282 add_char_to_sbuf('\\');
1284 add_char_to_sbuf(name[i]);
1285 i++;
1287 add_char_to_sbuf('\\');
1288 add_char_to_sbuf(')');
1292 int html_printer::sbuf_continuation (unsigned char code, const char *name,
1293 const environment *env, int w)
1295 if ((sbuf_end_hpos == env->hpos) || (sbuf_dmark_hpos == env->hpos)) {
1296 name = check_diacritical_combination(code, name);
1297 add_to_sbuf(code, name);
1298 determine_diacritical_mark(name, env);
1299 sbuf_end_hpos += w + sbuf_kern;
1300 return( TRUE );
1301 } else {
1302 if ((sbuf_len < SBUF_SIZE-1) && (env->hpos >= sbuf_end_hpos) &&
1303 ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
1305 * lets see whether a space is needed or not
1307 int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size);
1309 if (env->hpos-sbuf_end_hpos < space_width) {
1310 name = check_diacritical_combination(code, name);
1311 add_to_sbuf(code, name);
1312 determine_diacritical_mark(name, env);
1313 sbuf_end_hpos = env->hpos + w;
1314 return( TRUE );
1316 } else if ((sbuf_len > 0) && (sbuf_dmark_hpos)) {
1318 * check whether the diacritical mark is on the same character
1320 int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size);
1322 if (abs(sbuf_dmark_hpos-env->hpos) < space_width) {
1323 name = check_diacritical_combination(code, name);
1324 add_to_sbuf(code, name);
1325 determine_diacritical_mark(name, env);
1326 sbuf_end_hpos = env->hpos + w;
1327 return( TRUE );
1331 return( FALSE );
1335 * seen_backwards_escape - returns TRUE if we can see a escape at position i..l in s
1338 int html_printer::seen_backwards_escape (char *s, int l)
1341 * this is tricky so it is broken into components for clarity
1342 * (we let the compiler put in all back into a complex expression)
1344 if ((l>0) && (sbuf[l] == '(') && (sbuf[l-1] == '\\')) {
1346 * ok seen '\(' but we must now check for '\\('
1348 if ((l>1) && (sbuf[l-2] == '\\')) {
1350 * escaped the escape
1352 return( FALSE );
1353 } else {
1354 return( TRUE );
1356 } else {
1357 return( FALSE );
1362 * reverse - return reversed string.
1365 char *reverse (char *s)
1367 int i=0;
1368 int j=strlen(s)-1;
1369 char t;
1371 while (i<j) {
1372 t = s[i];
1373 s[i] = s[j];
1374 s[j] = t;
1375 i++;
1376 j--;
1378 return( s );
1382 * remove_last_char_from_sbuf - removes the last character from sbuf.
1385 char *html_printer::remove_last_char_from_sbuf ()
1387 int l=sbuf_len;
1388 static char last[MAX_STRING_LENGTH];
1390 if (l>0) {
1391 l--;
1392 if ((sbuf[l] == ')') && (l>0) && (sbuf[l-1] == '\\')) {
1394 * found terminating escape
1396 int i=0;
1398 l -= 2;
1399 while ((l>0) && (! seen_backwards_escape(sbuf, l))) {
1400 if (sbuf[l] == '\\') {
1401 if (sbuf[l-1] == '\\') {
1402 last[i] = sbuf[l];
1403 i++;
1404 l--;
1406 } else {
1407 last[i] = sbuf[l];
1408 i++;
1410 l--;
1412 last[i] = (char)0;
1413 sbuf_len = l;
1414 if (seen_backwards_escape(sbuf, l)) {
1415 sbuf_len--;
1417 return( reverse(last) );
1418 } else {
1419 if ((sbuf[l] == '\\') && (l>0) && (sbuf[l-1] == '\\')) {
1420 l -= 2;
1421 sbuf_len = l;
1422 return( "\\" );
1423 } else {
1424 sbuf_len--;
1425 last[0] = sbuf[sbuf_len];
1426 last[1] = (char)0;
1427 return( last );
1430 } else {
1431 return( NULL );
1436 * check_diacriticial_combination - checks to see whether the character code
1437 * if combined with the previous diacriticial mark
1438 * forms a new character.
1441 const char *html_printer::check_diacritical_combination (unsigned char code, const char *name)
1443 static char troff_char[2];
1445 if ((name == 0) && (sbuf_dmark_hpos >= 0)) {
1446 // last character was a diacritical mark
1447 char *last = remove_last_char_from_sbuf();
1449 int i=0;
1450 int j;
1452 while (diacritical_table[i].mark != NULL) {
1453 if (strcmp(diacritical_table[i].mark, last) == 0) {
1454 j=0;
1455 while ((diacritical_table[i].second_troff_char[j] != (char)0) &&
1456 (diacritical_table[i].second_troff_char[j] != code)) {
1457 j++;
1459 if (diacritical_table[i].second_troff_char[j] == code) {
1460 troff_char[0] = diacritical_table[i].translation;
1461 troff_char[1] = code;
1462 troff_char[2] = (char)0;
1463 return( troff_char );
1466 i++;
1468 add_to_sbuf(last[0], last);
1470 return( name );
1474 * determine_diacritical_mark - if name is a diacriticial mark the record the position.
1475 * --fixme-- is there a better way of doing this
1476 * this must be done in troff somewhere.
1479 void html_printer::determine_diacritical_mark (const char *name, const environment *env)
1481 if (name != 0) {
1482 int i=0;
1484 while (diacritical_table[i].mark != NULL) {
1485 if (strcmp(name, diacritical_table[i].mark) == 0) {
1486 sbuf_dmark_hpos = env->hpos;
1487 return;
1489 i++;
1492 sbuf_dmark_hpos = -1;
1496 * set_char - adds a character into the sbuf if it is a continuation with the previous
1497 * word otherwise flush the current sbuf and add character anew.
1500 void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name)
1502 unsigned char code = f->get_code(i);
1504 #if 0
1505 if (code == ' ') {
1506 stop();
1508 #endif
1509 style sty(f, env->size, env->height, env->slant, env->fontno);
1510 if (sty.slant != 0) {
1511 if (sty.slant > 80 || sty.slant < -80) {
1512 error("silly slant `%1' degrees", sty.slant);
1513 sty.slant = 0;
1516 if ((name != 0) && (page_contents->is_in_graphic)) {
1517 flush_sbuf();
1518 int r=font::res; // resolution of the device
1519 page_contents->add_special_char(&sty, (char *)name, strlen(name),
1520 env->vpos-sty.point_size*r/72, env->hpos,
1521 env->vpos , env->hpos+w);
1522 sbuf_end_hpos = env->hpos + w;
1523 sbuf_start_hpos = env->hpos;
1524 sbuf_vpos = env->vpos;
1525 sbuf_style = sty;
1526 sbuf_kern = 0;
1527 } else {
1528 if ((sbuf_len > 0) && (sbuf_len < SBUF_SIZE) && (sty == sbuf_style) &&
1529 (sbuf_vpos == env->vpos) && (sbuf_continuation(code, name, env, w))) {
1530 return;
1531 } else {
1532 flush_sbuf();
1533 sbuf_len = 0;
1534 add_to_sbuf(code, name);
1535 determine_diacritical_mark(name, env);
1536 sbuf_end_hpos = env->hpos + w;
1537 sbuf_start_hpos = env->hpos;
1538 sbuf_vpos = env->vpos;
1539 sbuf_style = sty;
1540 sbuf_kern = 0;
1546 * make_new_image_name - creates a new file name ready for a image file.
1549 void html_printer::make_new_image_name (void)
1551 image_number++;
1553 if ((current_filename == 0) ||
1554 (strcmp(current_filename, "<standard input>") == 0) ||
1555 (strcmp(current_filename, "-") == 0) ||
1556 (strchr(current_filename, '/') != 0)) {
1557 sprintf(image_name, "grohtml-%d-%ld", image_number, (long)getpid());
1558 } else {
1559 sprintf(image_name, "%s-%d-%ld", current_filename, image_number, (long)getpid());
1564 * write_title - writes the title to this document
1567 void html_printer::write_title (int in_head)
1569 if (title.has_been_found) {
1570 if (in_head) {
1571 html.put_string("<title>");
1572 html.put_string(title.text);
1573 html.put_string("</title>\n");
1574 } else {
1575 title.has_been_written = TRUE;
1576 html.put_string("<h1 align=center>");
1577 html.put_string(title.text);
1578 html.put_string("</h1>\n");
1584 * get_html_translation - given the position of the character and its name
1585 * return the device encoding for such character.
1588 char *get_html_translation (font *f, char *name)
1590 int index;
1592 if ((f == 0) || (name == 0) || (strcmp(name, "") == 0)) {
1593 return( NULL );
1594 } else {
1595 index = f->name_to_index(name);
1596 if (index == 0) {
1597 error("character `%s' not found", name);
1598 return( NULL );
1599 } else {
1600 return( (char *)f->get_special_device_encoding(index) );
1606 * str_translate_to_html - converts a string, str, into html text. It places
1607 * the output input buffer, buf. It truncates string, str, if
1608 * there is not enough space in buf.
1609 * It looks up the html character encoding of single characters
1610 * if, and_single, is TRUE. Characters such as < > & etc.
1613 void str_translate_to_html (font *f, char *buf, int buflen, char *str, int len, int and_single)
1615 int l;
1616 char *translation;
1617 int e;
1618 char escaped_char[MAX_STRING_LENGTH];
1619 int i=0;
1620 int b=0;
1621 int t=0;
1623 #if 0
1624 if (strcmp(str, "\\(\\\\-\\)") == 0) {
1625 stop();
1627 #endif
1628 while (str[i] != (char)0) {
1629 if ((str[i]=='\\') && (i+1<len)) {
1630 i++; // skip the leading backslash
1631 if (str[i] == '(') {
1632 // start of escape
1633 i++;
1634 e = 0;
1635 while ((str[i] != (char)0) &&
1636 (! ((str[i] == '\\') && (i+1<len) && (str[i+1] == ')')))) {
1637 if (str[i] == '\\') {
1638 i++;
1640 escaped_char[e] = str[i];
1641 e++;
1642 i++;
1644 if ((str[i] == '\\') && (i+1<len) && (str[i+1] == ')')) {
1645 i += 2;
1647 escaped_char[e] = (char)0;
1648 if (e > 0) {
1649 translation = get_html_translation(f, escaped_char);
1650 if (translation) {
1651 l = strlen(translation);
1652 t = max(0, min(l, buflen-b));
1653 strncpy(&buf[b], translation, t);
1654 b += t;
1655 } else {
1656 int index=f->name_to_index(escaped_char);
1658 if (index != 0) {
1659 buf[b] = f->get_code(index);
1660 b++;
1665 } else {
1666 if (and_single) {
1667 char name[2];
1669 name[0] = str[i];
1670 name[1] = (char)0;
1671 translation = get_html_translation(f, name);
1672 if (translation) {
1673 l = strlen(translation);
1674 t = max(0, min(l, buflen-b));
1675 strncpy(&buf[b], translation, t);
1676 b += t;
1677 } else {
1678 if (b<buflen) {
1679 buf[b] = str[i];
1680 b++;
1683 } else {
1685 * do not attempt to encode single characters
1687 if (b<buflen) {
1688 buf[b] = str[i];
1689 b++;
1692 i++;
1695 buf[min(b, buflen)] = (char)0;
1699 * find_title - finds a title to this document, if it exists.
1702 void html_printer::find_title (void)
1704 text_glob *t;
1705 int r=font::res;
1706 int removed_from_head;
1707 char buf[MAX_STRING_LENGTH];
1709 if ((page_number == 1) && (guess_on)) {
1710 if (! page_contents->words.is_empty()) {
1712 int end_title_hpos = 0;
1713 int start_title_vpos = 0;
1714 int found_title_start = FALSE;
1715 int height = 0;
1716 int start_region =-1;
1718 if (! page_contents->regions.is_empty()) {
1719 region_glob *r;
1721 page_contents->regions.start_from_head();
1722 r = page_contents->regions.get_data();
1723 if (r->minv > 0) {
1724 start_region = r->minv;
1728 page_contents->words.start_from_head();
1729 do {
1730 t = page_contents->words.get_data();
1731 removed_from_head = FALSE;
1732 if ((found_title_start) && (start_region != -1) && (t->maxv >= start_region)) {
1734 * we have just encountered the first graphic region so
1735 * we stop looking for a title.
1737 title.has_been_found = TRUE;
1738 return;
1739 } else if (t->is_raw_command) {
1740 // skip raw commands
1741 page_contents->words.move_right(); // move onto next word
1742 } else if ((!found_title_start) && (t->minh > left_margin_indent) &&
1743 ((start_region == -1) || (t->maxv < start_region))) {
1744 start_title_vpos = t->minv;
1745 end_title_hpos = t->minh;
1746 str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE);
1747 strcpy((char *)title.text, buf);
1748 height = t->text_style.point_size*r/72;
1749 found_title_start = TRUE;
1750 page_contents->words.sub_move_right();
1751 removed_from_head = ((!page_contents->words.is_empty()) &&
1752 (page_contents->words.is_equal_to_head()));
1753 } else if (found_title_start) {
1754 if ((t->minv == start_title_vpos) ||
1755 ((!more_than_line_break(start_title_vpos, t->minv, (height*3)/2)) &&
1756 (t->minh > left_margin_indent)) ||
1757 (is_bold(t) && (t->minh > left_margin_indent))) {
1758 start_title_vpos = min(t->minv, start_title_vpos);
1759 end_title_hpos = max(t->maxh, end_title_hpos);
1760 strcat(title.text, " ");
1761 str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE);
1762 strcat(title.text, buf);
1763 page_contents->words.sub_move_right();
1764 removed_from_head = ((!page_contents->words.is_empty()) &&
1765 (page_contents->words.is_equal_to_head()));
1766 } else {
1767 // end of title
1768 title.has_been_found = TRUE;
1769 return;
1771 } else if (t->minh <= left_margin_indent) {
1772 // no margin exists
1773 return;
1774 } else {
1775 // move onto next word
1776 page_contents->words.move_right();
1778 } while ((! page_contents->words.is_equal_to_head()) || (removed_from_head));
1784 * html_newline - generates a newline <br>
1787 void html_printer::html_newline (void)
1789 int r = font::res;
1790 int height = output_style.point_size*r/72;
1792 if (current_paragraph->in_paragraph) {
1793 // safe to generate a pretty newline
1794 html.put_string("<br>\n");
1795 } else {
1796 html.put_string("<br>");
1798 output_vpos += height;
1799 issued_newline = TRUE;
1803 * issue_left_paragraph - emits a left paragraph together with appropriate
1804 * margin if header_indent is < left_margin_indent.
1807 void html_printer::issue_left_paragraph (void)
1809 if ((header_indent < left_margin_indent) && (! using_table_for_indent())) {
1810 html.put_string("<p style=\"margin-left: ");
1811 html.put_number(((left_margin_indent-header_indent)*100)/(right_margin_indent-header_indent));
1812 html.put_string("%\">");
1813 } else {
1814 html.put_string("<p>");
1819 * force_begin_paragraph - force the begin_paragraph to be emitted.
1822 void html_printer::force_begin_paragraph (void)
1824 if (current_paragraph->in_paragraph && current_paragraph->need_paragraph) {
1825 switch (current_paragraph->para_type) {
1827 case left_alignment: issue_left_paragraph();
1828 break;
1829 case center_alignment: html.put_string("<p align=center>");
1830 break;
1831 default: fatal("unknown paragraph alignment type");
1833 current_paragraph->need_paragraph = FALSE;
1838 * begin_paragraph - starts a new paragraph. It does nothing if a paragraph
1839 * has already been started.
1842 void html_printer::begin_paragraph (paragraph_type p)
1844 if (! current_paragraph->in_paragraph) {
1845 int r = font::res;
1846 int height = output_style.point_size*r/72;
1848 if (output_vpos >=0) {
1849 // we leave it alone if it is set to the top of page
1850 output_vpos += height;
1852 current_paragraph->need_paragraph = TRUE; // delay the <p> just in case we don't actually emit text
1853 current_paragraph->in_paragraph = TRUE;
1854 current_paragraph->para_type = p;
1855 issued_newline = TRUE;
1861 * begin_paragraph_no_height - starts a new paragraph. It does nothing if a paragraph
1862 * has already been started. Note it does not alter output_vpos.
1865 void html_printer::begin_paragraph_no_height (paragraph_type p)
1867 if (! current_paragraph->in_paragraph) {
1868 current_paragraph->need_paragraph = TRUE; // delay the <p> just in case we don't actually emit text
1869 current_paragraph->in_paragraph = TRUE;
1870 current_paragraph->para_type = p;
1871 issued_newline = TRUE;
1876 * end_paragraph - end the current paragraph. It does nothing if a paragraph
1877 * has not been started.
1880 void html_printer::end_paragraph (void)
1882 if (current_paragraph->in_paragraph) {
1883 // check whether we have generated any text inbetween the potential paragraph begin end
1884 if (! current_paragraph->need_paragraph) {
1885 int r = font::res;
1886 int height = output_style.point_size*r/72;
1888 output_vpos += height;
1889 terminate_current_font();
1890 html.put_string("</p>\n");
1891 } else {
1892 terminate_current_font();
1894 current_paragraph->para_type = left_alignment;
1895 current_paragraph->in_paragraph = FALSE;
1900 * save_paragraph - saves the current paragraph state and
1901 * creates new paragraph state.
1904 void html_printer::save_paragraph (void)
1906 if (current_paragraph == 0) {
1907 fatal("current_paragraph is NULL");
1909 current_paragraph = new html_paragraph(current_paragraph->in_paragraph,
1910 current_paragraph->need_paragraph,
1911 current_paragraph->para_type,
1912 current_paragraph);
1913 terminate_current_font();
1917 * restore_paragraph - restores the previous paragraph state.
1920 void html_printer::restore_paragraph (void)
1922 html_paragraph *old = current_paragraph;
1924 current_paragraph = current_paragraph->previous;
1925 free(old);
1929 * calculate_margin - runs through the words and graphics globs
1930 * and finds the start of the left most margin.
1933 void html_printer::calculate_margin (void)
1935 if (! margin_on) {
1936 text_glob *w;
1937 graphic_glob *g;
1939 // remove margin
1941 right_margin_indent = 0;
1943 if (! page_contents->words.is_empty()) {
1945 // firstly check the words to determine the right margin
1947 page_contents->words.start_from_head();
1948 do {
1949 w = page_contents->words.get_data();
1950 if ((w->maxh >= 0) && (w->maxh > right_margin_indent)) {
1951 right_margin_indent = w->maxh;
1952 #if 0
1953 if (right_margin_indent == 758) stop();
1954 #endif
1956 page_contents->words.move_right();
1957 } while (! page_contents->words.is_equal_to_head());
1961 * only examine graphics if no words present
1963 if (! page_contents->lines.is_empty()) {
1964 // now check for diagrams for right margin
1965 page_contents->lines.start_from_head();
1966 do {
1967 g = page_contents->lines.get_data();
1968 if ((g->maxh >= 0) && (g->maxh > right_margin_indent)) {
1969 right_margin_indent = g->maxh;
1970 #if 0
1971 if (right_margin_indent == 950) stop();
1972 #endif
1974 page_contents->lines.move_right();
1975 } while (! page_contents->lines.is_equal_to_head());
1979 * now we know the right margin lets do the same to find left margin
1982 if (header_indent == -1) {
1983 header_indent = right_margin_indent;
1985 left_margin_indent = right_margin_indent;
1987 if (! page_contents->words.is_empty()) {
1988 do {
1989 w = page_contents->words.get_data();
1990 if ((w->minh >= 0) && (w->minh < left_margin_indent)) {
1991 if (! is_a_header(w) && (! w->is_raw_command)) {
1992 left_margin_indent = w->minh;
1995 page_contents->words.move_right();
1996 } while (! page_contents->words.is_equal_to_head());
2000 * only examine graphic for margins if text yields nothing
2003 if (! page_contents->lines.is_empty()) {
2004 // now check for diagrams
2005 page_contents->lines.start_from_head();
2006 do {
2007 g = page_contents->lines.get_data();
2008 if ((g->minh >= 0) && (g->minh < left_margin_indent)) {
2009 left_margin_indent = g->minh;
2011 page_contents->lines.move_right();
2012 } while (! page_contents->lines.is_equal_to_head());
2018 * calculate_region - runs through the graphics globs and text globs
2019 * and ensures that all graphic routines
2020 * are defined by the region lists.
2021 * This then allows us to easily
2022 * determine the range of vertical and
2023 * horizontal boundaries for pictures,
2024 * tbl's and eqn's.
2028 void page::calculate_region (void)
2030 graphic_glob *g;
2032 if (! lines.is_empty()) {
2033 lines.start_from_head();
2034 do {
2035 g = lines.get_data();
2036 if (! is_in_region(g)) {
2037 if (! can_grow_region(g)) {
2038 make_new_region(g);
2041 lines.move_right();
2042 } while (! lines.is_equal_to_head());
2047 * remove_redundant_regions - runs through the regions and ensures that
2048 * all are needed. This is required as
2049 * a picture may be empty, or EQ EN pair
2050 * maybe empty.
2053 void html_printer::remove_redundant_regions (void)
2055 region_glob *r;
2057 // firstly run through the region making sure that all are needed
2058 // ie all contain a line or word
2059 if (! page_contents->regions.is_empty()) {
2060 page_contents->regions.start_from_tail();
2061 do {
2062 r = page_contents->regions.get_data();
2063 calculate_region_margins(r);
2064 if (page_contents->has_line(r) || page_contents->has_word(r)) {
2065 page_contents->regions.move_right();
2066 } else {
2067 page_contents->regions.sub_move_right();
2069 } while ((! page_contents->regions.is_empty()) &&
2070 (! page_contents->regions.is_equal_to_tail()));
2074 void html_printer::display_regions (void)
2076 if (debug_table_on) {
2077 region_glob *r;
2079 fprintf(stderr, "==========s t a r t===========\n");
2080 if (! page_contents->regions.is_empty()) {
2081 page_contents->regions.start_from_head();
2082 do {
2083 r = page_contents->regions.get_data();
2084 fprintf(stderr, "region minv %d maxv %d\n", r->minv, r->maxv);
2085 page_contents->regions.move_right();
2086 } while (! page_contents->regions.is_equal_to_head());
2088 fprintf(stderr, "============e n d=============\n");
2089 fflush(stderr);
2094 * remove_duplicate_regions - runs through the regions and ensures that
2095 * no duplicates exist.
2098 void html_printer::remove_duplicate_regions (void)
2100 region_glob *r;
2101 region_glob *l=0;
2103 if (! page_contents->regions.is_empty()) {
2104 page_contents->regions.start_from_head();
2105 l = page_contents->regions.get_data();
2106 page_contents->regions.move_right();
2107 r = page_contents->regions.get_data();
2108 if (l != r) {
2109 do {
2110 r = page_contents->regions.get_data();
2111 // we have a legit region so we check for an intersection
2112 if (is_intersection(r->minv, r->minv, l->minv, l->maxv) &&
2113 is_intersection(r->minh, r->maxh, l->minh, l->maxh)) {
2114 l->minv = min(r->minv, l->minv);
2115 l->maxv = max(r->maxv, l->maxv);
2116 l->minh = min(r->minh, l->minh);
2117 l->maxh = max(r->maxh, l->maxh);
2118 calculate_region_margins(l);
2119 page_contents->regions.sub_move_right();
2120 } else {
2121 l = r;
2122 page_contents->regions.move_right();
2124 } while ((! page_contents->regions.is_empty()) &&
2125 (! page_contents->regions.is_equal_to_head()));
2130 int page::has_line (region_glob *r)
2132 graphic_glob *g;
2134 if (! lines.is_empty()) {
2135 lines.start_from_head();
2136 do {
2137 g = lines.get_data();
2138 if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) &&
2139 is_subsection(g->minh, g->maxh, r->minh, r->maxh)) {
2140 return( TRUE );
2142 lines.move_right();
2143 } while (! lines.is_equal_to_head());
2145 return( FALSE );
2149 int page::has_word (region_glob *r)
2151 text_glob *g;
2153 if (! words.is_empty()) {
2154 words.start_from_head();
2155 do {
2156 g = words.get_data();
2157 if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) &&
2158 is_subsection(g->minh, g->maxh, r->minh, r->maxh)) {
2159 return( TRUE );
2161 words.move_right();
2162 } while (! words.is_equal_to_head());
2164 return( FALSE );
2168 void html_printer::calculate_region_margins (region_glob *r)
2170 text_glob *w;
2171 graphic_glob *g;
2173 r->minh = right_margin_indent;
2174 r->maxh = left_margin_indent;
2176 if (! page_contents->lines.is_empty()) {
2177 page_contents->lines.start_from_head();
2178 do {
2179 g = page_contents->lines.get_data();
2180 if (is_subsection(g->minv, g->maxv, r->minv, r->maxv)) {
2181 r->minh = min(r->minh, g->minh);
2182 r->maxh = max(r->maxh, g->maxh);
2184 page_contents->lines.move_right();
2185 } while (! page_contents->lines.is_equal_to_head());
2187 if (! page_contents->words.is_empty()) {
2188 page_contents->words.start_from_head();
2189 do {
2190 w = page_contents->words.get_data();
2191 if (is_subsection(w->minv, w->maxv, r->minv, r->maxv)) {
2192 r->minh = min(r->minh, w->minh);
2193 r->maxh = max(r->maxh, w->maxh);
2195 page_contents->words.move_right();
2196 } while (! page_contents->words.is_equal_to_head());
2201 int page::is_in_region (graphic_glob *g)
2203 region_glob *r;
2205 if (! regions.is_empty()) {
2206 regions.start_from_head();
2207 do {
2208 r = regions.get_data();
2209 if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) &&
2210 is_subsection(g->minh, g->maxh, r->minh, r->maxh)) {
2211 return( TRUE );
2213 regions.move_right();
2214 } while (! regions.is_equal_to_head());
2216 return( FALSE );
2221 * no_raw_commands - returns TRUE if no html raw commands exist between
2222 * minv and maxv.
2225 int page::no_raw_commands (int minv, int maxv)
2227 text_glob *g;
2229 if (! words.is_empty()) {
2230 words.start_from_head();
2231 do {
2232 g = words.get_data();
2233 if ((g->is_raw_command) && (g->is_html_command) &&
2234 (is_intersection(g->minv, g->maxv, minv, maxv))) {
2235 return( FALSE );
2237 words.move_right();
2238 } while (! words.is_equal_to_head());
2240 return( TRUE );
2244 * can_grow_region - returns TRUE if a region exists which can be extended
2245 * to include graphic_glob *g. The region is extended.
2248 int page::can_grow_region (graphic_glob *g)
2250 region_glob *r;
2251 int quarter_inch=font::res/4;
2253 if (! regions.is_empty()) {
2254 regions.start_from_head();
2255 do {
2256 r = regions.get_data();
2257 // must prevent grohtml from growing a region through a html raw command
2258 if (is_intersection(g->minv, g->maxv, r->minv, r->maxv+quarter_inch) &&
2259 (no_raw_commands(r->minv, r->maxv+quarter_inch))) {
2260 #if defined(DEBUGGING)
2261 stop();
2262 printf("r minh=%d minv=%d maxh=%d maxv=%d\n",
2263 r->minh, r->minv, r->maxh, r->maxv);
2264 printf("g minh=%d minv=%d maxh=%d maxv=%d\n",
2265 g->minh, g->minv, g->maxh, g->maxv);
2266 #endif
2267 r->minv = min(r->minv, g->minv);
2268 r->maxv = max(r->maxv, g->maxv);
2269 r->minh = min(r->minh, g->minh);
2270 r->maxh = max(r->maxh, g->maxh);
2271 #if defined(DEBUGGING)
2272 printf(" r minh=%d minv=%d maxh=%d maxv=%d\n",
2273 r->minh, r->minv, r->maxh, r->maxv);
2274 #endif
2275 return( TRUE );
2277 regions.move_right();
2278 } while (! regions.is_equal_to_head());
2280 return( FALSE );
2285 * make_new_region - creates a new region to contain, g.
2288 void page::make_new_region (graphic_glob *g)
2290 region_glob *r=new region_glob;
2292 r->minv = g->minv;
2293 r->maxv = g->maxv;
2294 r->minh = g->minh;
2295 r->maxv = g->maxv;
2296 regions.add(r);
2300 void html_printer::dump_page(void)
2302 text_glob *g;
2304 printf("\n\ndebugging start\n");
2305 page_contents->words.start_from_head();
2306 do {
2307 g = page_contents->words.get_data();
2308 printf("%s ", g->text_string);
2309 page_contents->words.move_right();
2310 } while (! page_contents->words.is_equal_to_head());
2311 printf("\ndebugging end\n\n");
2316 * traverse_page_regions - runs through the regions in current_page
2317 * and generate html for text, and troff output
2318 * for all graphics.
2321 void html_printer::traverse_page_regions (void)
2323 region_glob *r;
2325 start_region_vpos = 0;
2326 start_region_hpos = 0;
2327 end_region_vpos = -1;
2328 end_region_hpos = -1;
2330 if (! page_contents->regions.is_empty()) {
2331 page_contents->regions.start_from_head();
2332 do {
2333 r = page_contents->regions.get_data();
2334 if (r->minv > 0) {
2335 end_region_vpos = r->minv-1;
2336 } else {
2337 end_region_vpos = 0;
2339 end_region_hpos = -1;
2340 display_globs(TRUE);
2341 calculate_region_margins(r);
2342 start_region_vpos = end_region_vpos;
2343 end_region_vpos = r->maxv;
2344 start_region_hpos = r->minh;
2345 end_region_hpos = r->maxh;
2346 display_globs(FALSE);
2347 start_region_vpos = end_region_vpos+1;
2348 start_region_hpos = 0;
2349 page_contents->regions.move_right();
2350 } while (! page_contents->regions.is_equal_to_head());
2351 start_region_vpos = end_region_vpos+1;
2352 start_region_hpos = 0;
2353 end_region_vpos = -1;
2354 end_region_hpos = -1;
2356 display_globs(TRUE);
2359 int html_printer::is_within_region (text_glob *t)
2361 int he, ve, hs;
2363 if (start_region_hpos == -1) {
2364 hs = t->minh;
2365 } else {
2366 hs = start_region_hpos;
2368 if (end_region_vpos == -1) {
2369 ve = t->maxv;
2370 } else {
2371 ve = end_region_vpos;
2373 if (end_region_hpos == -1) {
2374 he = t->maxh;
2375 } else {
2376 he = end_region_hpos;
2378 return( is_subsection(t->minv, t->maxv, start_region_vpos, ve) &&
2379 is_subsection(t->minh, t->maxh, hs, he) );
2382 int html_printer::is_within_region (graphic_glob *g)
2384 int he, ve, hs;
2386 if (start_region_hpos == -1) {
2387 hs = g->minh;
2388 } else {
2389 hs = start_region_hpos;
2391 if (end_region_vpos == -1) {
2392 ve = g->maxv;
2393 } else {
2394 ve = end_region_vpos;
2396 if (end_region_hpos == -1) {
2397 he = g->maxh;
2398 } else {
2399 he = end_region_hpos;
2401 return( is_subsection(g->minv, g->maxv, start_region_vpos, ve) &&
2402 is_subsection(g->minh, g->maxh, hs, he) );
2405 int html_printer::is_less (graphic_glob *g, text_glob *t)
2407 return( (g->minv < t->minv) || ((g->minv == t->minv) && (g->minh < t->minh)) );
2410 void html_printer::convert_to_image (char *troff_src, char *image_name)
2412 char buffer[1024];
2413 char *ps_src = mktemp(xtmptemplate("-ps-"));
2415 sprintf(buffer, "grops %s > %s\n", troff_src, ps_src);
2416 if (debug_on) {
2417 fprintf(stderr, buffer);
2419 system(buffer);
2421 if (image_type == gif) {
2422 sprintf(buffer,
2423 "echo showpage | gs -q -dSAFER -sDEVICE=ppmraw -r%d -g%dx%d -sOutputFile=- %s - | ppmquant 256 2> /dev/null | ppmtogif 2> /dev/null > %s.gif \n",
2424 image_res,
2425 (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2426 (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2427 ps_src, image_name);
2428 } else {
2429 sprintf(buffer,
2430 "echo showpage | gs -q -dSAFER -sDEVICE=%s -r%d -g%dx%d -sOutputFile=- %s - 2> /dev/null > %s.png \n",
2431 image_device,
2432 image_res,
2433 (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2434 (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2435 ps_src, image_name);
2436 #if 0
2437 sprintf(buffer,
2438 "echo showpage | gs -q -dSAFER -sDEVICE=ppmraw -r%d -g%dx%d -sOutputFile=- %s.ps - > %s.pnm ; pnmtopng -transparent white %s.pnm > %s.png \n",
2439 /* image_device, */
2440 image_res,
2441 (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2442 (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2443 name, name, name, image_name);
2444 #endif
2446 if (debug_on) {
2447 fprintf(stderr, "%s", buffer);
2449 system(buffer);
2450 unlink(ps_src);
2451 unlink(troff_src);
2454 void html_printer::prologue (void)
2456 troff.put_string("x T ps\nx res ");
2457 troff.put_number(postscript_res);
2458 troff.put_string(" 1 1\nx init\np1\n");
2461 void html_printer::display_globs (int is_to_html)
2463 text_glob *t=0;
2464 graphic_glob *g=0;
2465 FILE *f=0;
2466 char *troff_src;
2467 int something=FALSE;
2468 int is_center=FALSE;
2470 end_paragraph();
2472 if (! is_to_html) {
2473 is_center = html_position_region();
2474 make_new_image_name();
2475 f = xtmpfile(&troff_src, "-troff-", FALSE);
2476 troff.set_file(f);
2477 prologue();
2478 output_style.f = 0;
2480 if (! page_contents->words.is_empty()) {
2481 page_contents->words.start_from_head();
2482 t = page_contents->words.get_data();
2485 if (! page_contents->lines.is_empty()) {
2486 page_contents->lines.start_from_head();
2487 g = page_contents->lines.get_data();
2490 do {
2491 #if 0
2492 if ((t != 0) && (strcmp(t->text_string, "(1.a)") == 0)) {
2493 stop();
2495 #endif
2496 if ((t == 0) && (g != 0)) {
2497 if (is_within_region(g)) {
2498 something = TRUE;
2499 display_line(g, is_to_html);
2501 if (page_contents->lines.is_empty() || page_contents->lines.is_equal_to_tail()) {
2502 g = 0;
2503 } else {
2504 g = page_contents->lines.move_right_get_data();
2506 } else if ((g == 0) && (t != 0)) {
2507 if (is_within_region(t)) {
2508 display_word(t, is_to_html);
2509 something = TRUE;
2511 if (page_contents->words.is_empty() || page_contents->words.is_equal_to_tail()) {
2512 t = 0;
2513 } else {
2514 t = page_contents->words.move_right_get_data();
2516 } else {
2517 if ((g == 0) || (t == 0)) {
2518 // hmm nothing to print out...
2519 } else if (is_less(g, t)) {
2520 if (is_within_region(g)) {
2521 display_line(g, is_to_html);
2522 something = TRUE;
2524 if (page_contents->lines.is_empty() || page_contents->lines.is_equal_to_tail()) {
2525 g = 0;
2526 } else {
2527 g = page_contents->lines.move_right_get_data();
2529 } else {
2530 if (is_within_region(t)) {
2531 display_word(t, is_to_html);
2532 something = TRUE;
2534 if (page_contents->words.is_empty() || page_contents->words.is_equal_to_tail()) {
2535 t = 0;
2536 } else {
2537 t = page_contents->words.move_right_get_data();
2541 } while ((t != 0) || (g != 0));
2543 if ((! is_to_html) && (f != 0)) {
2544 fclose(troff.get_file());
2545 if (something) {
2546 convert_to_image(troff_src, image_name);
2548 if (is_center) {
2549 end_paragraph();
2550 begin_paragraph(center_alignment);
2551 force_begin_paragraph();
2553 html.put_string("<img src=\"");
2554 html.put_string(image_name);
2555 if (image_type == gif) {
2556 html.put_string(".gif\"");
2557 } else {
2558 html.put_string(".png\"");
2560 html.put_string(">\n");
2561 html_newline();
2562 if (is_center) {
2563 end_paragraph();
2566 output_vpos = end_region_vpos;
2567 output_hpos = 0;
2568 need_one_newline = FALSE;
2569 output_style.f = 0;
2570 end_paragraph();
2575 void html_printer::flush_page (void)
2577 calculate_margin();
2578 output_vpos = -1;
2579 output_hpos = get_left();
2580 supress_sub_sup = TRUE;
2581 #if 0
2582 dump_page();
2583 #endif
2584 html.begin_comment("left margin: ").comment_arg(itoa(left_margin_indent)).end_comment();;
2585 html.begin_comment("right margin: ").comment_arg(itoa(right_margin_indent)).end_comment();;
2586 remove_redundant_regions();
2587 page_contents->calculate_region();
2588 remove_duplicate_regions();
2589 find_title();
2590 supress_sub_sup = TRUE;
2591 traverse_page_regions();
2592 terminate_current_font();
2593 if (need_one_newline) {
2594 html_newline();
2596 end_paragraph();
2598 // move onto a new page
2599 delete page_contents;
2600 page_contents = new page;
2603 static int convertSizeToHTML (int size)
2605 if (size < 6) {
2606 return( 0 );
2607 } else if (size < 8) {
2608 return( 1 );
2609 } else if (size < 10) {
2610 return( 2 );
2611 } else if (size < 12) {
2612 return( 3 );
2613 } else if (size < 14) {
2614 return( 4 );
2615 } else if (size < 16) {
2616 return( 5 );
2617 } else if (size < 18) {
2618 return( 6 );
2619 } else {
2620 return( 7 );
2625 void html_printer::write_html_font_face (const char *fontname, const char *left, const char *right)
2627 switch (fontname[0]) {
2629 case 'C': html.put_string(left) ; html.put_string("tt"); html.put_string(right);
2630 break;
2631 case 'H': break;
2632 case 'T': break;
2633 default: break;
2638 void html_printer::write_html_font_type (const char *fontname, const char *left, const char *right)
2640 if (strcmp(&fontname[1], "B") == 0) {
2641 html.put_string(left) ; html.put_string("B"); html.put_string(right);
2642 } else if (strcmp(&fontname[1], "I") == 0) {
2643 html.put_string(left) ; html.put_string("I"); html.put_string(right);
2644 } else if (strcmp(&fontname[1], "BI") == 0) {
2645 html.put_string(left) ; html.put_string("EM"); html.put_string(right);
2650 void html_printer::html_change_font (text_glob *g, const char *fontname, int size)
2652 char buffer[1024];
2654 if (output_style.f != 0) {
2655 const char *oldfontname = output_style.f->get_name();
2657 // firstly terminate the current font face and type
2658 if ((oldfontname != 0) && (oldfontname != fontname)) {
2659 write_html_font_face(oldfontname, "</", ">");
2660 write_html_font_type(oldfontname, "</", ">");
2664 if ((output_style.point_size != size) && (output_style.point_size != 0)) {
2665 // shutdown the previous font size
2666 html.put_string("</font>");
2669 if ((output_style.point_size != size) && (size != 0)) {
2670 // now emit the size if it has changed
2671 sprintf(buffer, "<font size=%d>", convertSizeToHTML(size));
2672 html.put_string(buffer);
2673 output_style.point_size = size; // and remember the size
2675 output_style.f = 0; // no style at present
2676 output_style.point_size = size; // remember current font size
2678 if (fontname != 0) {
2679 if (! g->is_raw_command) {
2680 // now emit the new font
2681 write_html_font_face(fontname, "<", ">");
2683 // now emit the new font type
2684 write_html_font_type(fontname, "<", ">");
2686 output_style = g->text_style; // remember style for next time
2692 void html_printer::change_font (text_glob *g, int is_to_html)
2694 if (is_to_html) {
2695 if (output_style != g->text_style) {
2696 const char *fontname=0;
2697 int size=0;
2699 if (g->text_style.f != 0) {
2700 fontname = g->text_style.f->get_name();
2701 size = (font::res/(72*font::sizescale))*g->text_style.point_size;
2703 html_change_font(g, fontname, size);
2705 } else {
2706 // is to troff
2707 if (output_style != g->text_style) {
2708 if (g->text_style.f != 0) {
2709 const char *fontname = g->text_style.f->get_name();
2710 int size = (font::res/(72*font::sizescale))*g->text_style.point_size;
2712 if (fontname == 0) {
2713 fatal("no internalname specified for font");
2716 troff_change_font(fontname, size, g->text_style.font_no);
2717 output_style = g->text_style; // remember style for next time
2724 * is_bold - returns TRUE if the text inside, g, is using a bold face.
2725 * It returns FALSE is g contains a raw html command, even if this uses
2726 * a bold font.
2729 int html_printer::is_bold (text_glob *g)
2731 if (g->text_style.f == 0) {
2732 // unknown font
2733 return( FALSE );
2734 } else if (g->is_raw_command) {
2735 return( FALSE );
2736 } else {
2737 const char *fontname = g->text_style.f->get_name();
2739 if (strlen(fontname) >= 2) {
2740 return( fontname[1] == 'B' );
2741 } else {
2742 return( FALSE );
2747 void html_printer::terminate_current_font (void)
2749 text_glob g;
2751 // we create a dummy glob just so we can tell html_change_font not to start up
2752 // a new font
2753 g.is_raw_command = TRUE;
2754 html_change_font(&g, 0, 0);
2757 void html_printer::write_header (text_glob *g)
2759 if (strlen(header.header_buffer) > 0) {
2760 if (header.header_level > 7) {
2761 header.header_level = 7;
2764 if (cutoff_heading+2 > header.header_level) {
2765 // firstly we must terminate any font and type faces
2766 terminate_current_font();
2767 end_paragraph();
2769 // secondly we generate a tag
2770 html.put_string("<a name=\"");
2771 html.put_string(header.header_buffer);
2772 html.put_string("\"></a>");
2773 // now we save the header so we can issue a list of link
2774 style st;
2776 header.no_of_headings++;
2778 text_glob *h=new text_glob(&st,
2779 header.headings.add_string(header.header_buffer, strlen(header.header_buffer)),
2780 strlen(header.header_buffer),
2781 header.no_of_headings, header.header_level,
2782 header.no_of_headings, header.header_level,
2783 FALSE, FALSE);
2784 header.headers.add(h); // and add this header to the header list
2785 } else {
2786 terminate_current_font();
2787 end_paragraph();
2790 // we adjust the margin if necessary
2792 if (g->minh < left_margin_indent) {
2793 header_indent = g->minh;
2796 // and now we issue the real header
2797 html.put_string("<h");
2798 html.put_number(header.header_level);
2799 html.put_string(">");
2800 html.put_string(header.header_buffer);
2801 html.put_string("</h");
2802 html.put_number(header.header_level);
2803 html.put_string(">");
2805 need_one_newline = FALSE;
2806 begin_paragraph(left_alignment);
2807 header.written_header = TRUE;
2812 * translate_str_to_html - translates a string, str, into html representation.
2813 * len indicates the string length.
2816 void translate_str_to_html (font *f, char *str, int len)
2818 char buf[MAX_STRING_LENGTH];
2820 str_translate_to_html(f, buf, MAX_STRING_LENGTH, str, len, TRUE);
2821 strncpy(str, buf, max(len, strlen(buf)+1));
2825 * write_headings - emits a list of links for the headings in this document
2828 void header_desc::write_headings (FILE *f)
2830 text_glob *g;
2832 if (! headers.is_empty()) {
2833 headers.start_from_head();
2834 do {
2835 g = headers.get_data();
2836 fprintf(f, "<a href=\"#%s\">%s</a><br>\n", g->text_string, g->text_string);
2837 headers.move_right();
2838 } while (! headers.is_equal_to_head());
2842 void html_printer::determine_header_level (void)
2844 int i;
2845 int l=strlen(header.header_buffer);
2846 int stops=0;
2848 for (i=0; ((i<l) && ((header.header_buffer[i] == '.') || is_digit(header.header_buffer[i]))) ; i++) {
2849 if (header.header_buffer[i] == '.') {
2850 stops++;
2853 if (stops > 0) {
2854 header.header_level = stops;
2859 void html_printer::build_header (text_glob *g)
2861 text_glob *l;
2862 int current_vpos;
2863 char buf[MAX_STRING_LENGTH];
2865 strcpy(header.header_buffer, "");
2866 do {
2867 l = g;
2868 current_vpos = g->minv;
2869 str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH, g->text_string, g->text_length, TRUE);
2870 strcat(header.header_buffer, (char *)buf);
2871 page_contents->words.move_right();
2872 g = page_contents->words.get_data();
2873 if (g->minv == current_vpos) {
2874 strcat(header.header_buffer, " ");
2876 } while ((! page_contents->words.is_equal_to_head()) &&
2877 ((g->minv == current_vpos) || (l->maxh == right_margin_indent)));
2879 determine_header_level();
2880 // finally set the output to neutral for after the header
2882 g = page_contents->words.get_data();
2883 output_vpos = g->minv; // set output_vpos to the next line since
2884 output_hpos = left_margin_indent; // html header forces a newline anyway
2885 page_contents->words.move_left(); // so that next time we use old g
2887 need_one_newline = FALSE;
2892 * is_whole_line_bold - returns TRUE if the whole line is bold.
2895 int html_printer::is_whole_line_bold (text_glob *g)
2897 text_glob *n=g;
2898 int current_vpos=g->minv;
2900 do {
2901 if (is_bold(n)) {
2902 page_contents->words.move_right();
2903 n = page_contents->words.get_data();
2904 } else {
2905 while (page_contents->words.get_data() != g) {
2906 page_contents->words.move_left();
2908 return( FALSE );
2910 } while ((! page_contents->words.is_equal_to_head()) && (is_on_same_line(n, current_vpos)));
2911 // was (n->minv == current_vpos)
2912 while (page_contents->words.get_data() != g) {
2913 page_contents->words.move_left();
2915 return( TRUE );
2920 * is_a_header - returns TRUE if the whole sequence of contineous lines are bold.
2921 * It checks to see whether a line is likely to be contineous and
2922 * then checks that all words are bold.
2925 int html_printer::is_a_header (text_glob *g)
2927 text_glob *l;
2928 text_glob *n=g;
2929 int current_vpos;
2931 do {
2932 l = n;
2933 current_vpos = n->minv;
2934 if (is_bold(n)) {
2935 page_contents->words.move_right();
2936 n = page_contents->words.get_data();
2937 } else {
2938 while (page_contents->words.get_data() != g) {
2939 page_contents->words.move_left();
2941 return( FALSE );
2943 } while ((! page_contents->words.is_equal_to_head()) &&
2944 ((n->minv == current_vpos) || (l->maxh == right_margin_indent)));
2945 while (page_contents->words.get_data() != g) {
2946 page_contents->words.move_left();
2948 return( TRUE );
2952 int html_printer::processed_header (text_glob *g)
2954 if ((guess_on) && (g->minh <= left_margin_indent) && (! using_table_for_indent()) &&
2955 (is_a_header(g))) {
2956 build_header(g);
2957 write_header(g);
2958 return( TRUE );
2959 } else {
2960 return( FALSE );
2964 int is_punctuation (char *s, int length)
2966 return( (length == 1) &&
2967 ((s[0] == '(') || (s[0] == ')') || (s[0] == '!') || (s[0] == '.') || (s[0] == '[') ||
2968 (s[0] == ']') || (s[0] == '?') || (s[0] == ',') || (s[0] == ';') || (s[0] == ':') ||
2969 (s[0] == '@') || (s[0] == '#') || (s[0] == '$') || (s[0] == '%') || (s[0] == '^') ||
2970 (s[0] == '&') || (s[0] == '*') || (s[0] == '+') || (s[0] == '-') || (s[0] == '=') ||
2971 (s[0] == '{') || (s[0] == '}') || (s[0] == '|') || (s[0] == '\"') || (s[0] == '\''))
2976 * move_horizontal - moves right into the position, g->minh.
2979 void html_printer::move_horizontal (text_glob *g, int left_margin)
2981 if (g->text_style.f != 0) {
2982 int w = g->text_style.f->get_space_width(g->text_style.point_size);
2984 if (w == 0) {
2985 fatal("space width is zero");
2987 if ((output_hpos == left_margin) && (g->minh > output_hpos)) {
2988 make_html_indent(g->minh-output_hpos);
2989 } else {
2990 emit_space(g, FALSE);
2992 output_hpos = g->maxh;
2993 output_vpos = g->minv;
2995 change_font(g, TRUE);
3000 * looks_like_subscript - returns TRUE if, g, looks like a subscript.
3003 int html_printer::looks_like_subscript (text_glob *g)
3005 int r = font::res;
3006 int height = output_style.point_size*r/72;
3008 /* was return( ((output_vpos < g->minv) && (output_style.point_size != 0) &&
3009 * (output_style.point_size > g->text_style.point_size)) );
3012 return( (output_style.point_size != 0) && (! supress_sub_sup) && (output_vpos+height < g->maxv) );
3016 * looks_like_superscript - returns TRUE if, g, looks like a superscript.
3019 int html_printer::looks_like_superscript (text_glob *g)
3021 int r = font::res;
3022 int height = output_style.point_size*r/72;
3024 /* was
3025 * return(((output_vpos > g->minv) && (output_style.point_size != 0) &&
3026 * (output_style.point_size > g->text_style.point_size)));
3029 return( (output_style.point_size != 0) && (! supress_sub_sup) && (output_vpos+height > g->maxv) );
3033 * looks_like_larger_font - returns TRUE if, g, can be treated as a larger font.
3034 * g needs to be on the same line
3037 int html_printer::looks_like_larger_font (text_glob *g)
3039 int r = font::res;
3040 int height = output_style.point_size*r/72;
3042 return( (output_vpos+height == g->maxv) && (output_style.point_size != 0) &&
3043 (convertSizeToHTML(g->text_style.point_size)+1 == convertSizeToHTML(output_style.point_size)) );
3047 * looks_like_smaller_font - returns TRUE if, g, can be treated as a smaller font.
3048 * g needs to be on the same line
3051 int html_printer::looks_like_smaller_font (text_glob *g)
3053 int r = font::res;
3054 int height = output_style.point_size*r/72;
3056 return( (output_vpos+height == g->maxv) && (output_style.point_size != 0) &&
3057 (convertSizeToHTML(g->text_style.point_size) == convertSizeToHTML(output_style.point_size)+1) );
3061 * pretend_is_on_same_line - returns TRUE if we think, g, is on the same line as the previous glob.
3062 * Note that it believes a single word spanning the left..right as being
3063 * on a different line.
3066 int html_printer::pretend_is_on_same_line (text_glob *g, int left_margin, int right_margin)
3068 return( auto_on && (right_margin == output_hpos) && (left_margin == g->minh) &&
3069 (right_margin != g->maxh) && ((! is_whole_line_bold(g)) || (g->text_style.f == output_style.f)) &&
3070 (! (using_table_for_indent()) || (indentation.wrap_margin)) );
3073 int html_printer::is_on_same_line (text_glob *g, int vpos)
3075 #if 0
3076 if (g->is_html_command) {
3077 stop();
3079 #endif
3080 return(
3081 (vpos >= 0) &&
3082 (is_intersection(vpos, vpos+g->text_style.point_size*font::res/72-1, g->minv, g->maxv))
3088 * make_html_indent - creates a relative indentation.
3091 void html_printer::make_html_indent (int indent)
3093 if (indent > 0) {
3094 html.put_string("<span style=\" text-indent: ");
3095 html.put_number((indent*100)/(right_margin_indent-get_left()));
3096 html.put_string("%;\"></span>");
3101 * using_table_for_indent - returns TRUE if we currently using a table for indentation
3102 * purposes.
3105 int html_printer::using_table_for_indent (void)
3107 return( indentation.no_of_columns != 0 );
3111 * calculate_min_gap - returns the minimum gap by which we deduce columns.
3112 * This is a rough heuristic.
3115 int html_printer::calculate_min_gap (text_glob *g)
3117 text_glob *t = g;
3119 while ((t->is_raw_command) && (! page_contents->words.is_equal_to_tail()) &&
3120 ((t->minv < end_region_vpos) || (end_region_vpos < 0))) {
3121 page_contents->words.move_right();
3122 t=page_contents->words.get_data();
3124 rewind_text_to(g);
3125 if (t->is_raw_command) {
3126 return( font::res * 10 ); // impossibly large gap width
3127 } else {
3128 return( t->text_style.f->get_space_width(t->text_style.point_size)*GAP_SPACES );
3133 * collect_columns - place html text in a column and return the vertical limit reached.
3136 int html_printer::collect_columns (struct text_defn *next_words,
3137 struct text_defn *next_cols,
3138 struct text_defn *last_words,
3139 struct text_defn *last_cols,
3140 int max_words)
3142 text_glob *start = page_contents->words.get_data();
3143 text_glob *t = start;
3144 int upper_limit = 0;
3147 * initialize cols and words
3149 next_words[0].left = 0;
3150 next_words[0].right = 0;
3151 next_cols [0].left = 0;
3152 next_cols [0].right = 0;
3155 * if we have not reached the end collect the words on the current line
3157 if (start != 0) {
3158 int graphic_limit = end_region_vpos;
3160 if (is_whole_line_bold(t) && (t->minh <= left_margin_indent)) {
3162 * found header therefore terminate indentation table.
3163 * Return a negative number so we know a header has
3164 * stopped the column
3166 upper_limit = -t->minv;
3167 } else {
3168 int i =0; // is the index into next_cols
3169 int j =0; // is the column index for last_cols
3170 int k =0; // is the index into next_words
3171 int l =0; // is the index into next_words
3172 int prevh =0;
3173 int mingap =calculate_min_gap(start);
3176 * while words on the same line record them and any significant gaps
3178 while ((t != 0) && (is_on_same_line(t, start->minv) && (i<max_words)) &&
3179 ((graphic_limit == -1) || (graphic_limit > t->minv))) {
3182 * now find column index from the last line which corresponds to, t.
3184 j = find_column_index_in_line(t, last_cols);
3187 * now find word index from the last line which corresponds to, t.
3189 l = find_column_index_in_line(t, last_words);
3192 * Note t->minh might equal t->maxh when we are passing a special device character via \X
3193 * we currently ignore this when considering tables
3195 * if we have found a significant gap then record it
3197 if (((t->minh - prevh >= mingap) ||
3198 ((last_cols != 0) && (last_cols [j].left != 0) && (t->minh == last_cols [j].left))) &&
3199 (t->minh != t->maxh)) {
3200 next_cols[i].left = t->minh;
3201 next_cols[i].right = t->maxh;
3202 i++;
3204 * terminate the array
3206 if (i<max_words) {
3207 next_cols[i].left = 0;
3208 next_cols[i].right = 0;
3210 } else if (i>0) {
3212 * move previous right hand column to align with, t.
3215 if (t->minh > next_cols[i-1].left) {
3217 * a simple precaution in case we get globs which are technically on the same line
3218 * (sadly this does occur sometimes - maybe we should be stricter with is_on_same_line)
3219 * --fixme--
3221 next_cols[i-1].right = max(next_cols[i-1].right, t->maxh);
3225 * remember to record the individual words
3227 next_words[k].left = t->minh;
3228 next_words[k].right = t->maxh;
3229 k++;
3232 * and record the vertical upper limit
3234 upper_limit = max(t->minv, upper_limit);
3237 * and update prevh - used to detect a when a different line is seen
3239 prevh = t->maxh;
3242 * get next word into, t, which equals 0, if no word is found
3244 page_contents->words.move_right();
3245 t = page_contents->words.get_data();
3246 if (page_contents->words.is_equal_to_head()) {
3247 t = 0;
3252 * and terminate the next_words array
3255 if (k<max_words) {
3256 next_words[k].left = 0;
3257 next_words[k].right = 0;
3261 * consistency check, next_cols, after removing redundant colums.
3264 remove_redundant_columns(next_cols);
3266 #if 0
3267 for (k=0; k<count_columns(next_cols); k++) {
3268 if (next_cols[k].left > next_cols[k].right) {
3269 fprintf(stderr, "left > right\n"); fflush(stderr);
3270 stop();
3271 fatal("next_cols has messed up columns");
3273 if ((k>0) && (k+1<count_columns(next_cols)) && (next_cols[k].right > next_cols[k+1].left)) {
3274 fprintf(stderr, "next_cols[k].right > next_cols[k+1].left\n"); fflush(stderr);
3275 stop();
3276 fatal("next_cols has messed up columns");
3279 #endif
3282 return( upper_limit );
3286 * conflict_with_words - returns TRUE if a word sequence crosses a column.
3289 int html_printer::conflict_with_words (struct text_defn *column_guess, struct text_defn *words)
3291 int i=0;
3292 int j;
3294 while ((column_guess[i].left != 0) && (i<MAX_WORDS_PER_LINE)) {
3295 j=0;
3296 while ((words[j].left != 0) && (j<MAX_WORDS_PER_LINE)) {
3297 if ((words[j].left <= column_guess[i].right) && (i+1<MAX_WORDS_PER_LINE) &&
3298 (column_guess[i+1].left != 0) && (words[j].right >= column_guess[i+1].left)) {
3299 if (debug_table_on) {
3300 fprintf(stderr, "is a conflict with words\n");
3301 fflush(stderr);
3303 return( TRUE );
3305 j++;
3307 i++;
3309 if (debug_table_on) {
3310 fprintf(stderr, "is NOT a conflict with words\n");
3311 fflush(stderr);
3313 return( FALSE );
3317 * combine_line - combines dest and src.
3320 void html_printer::combine_line (struct text_defn *dest, struct text_defn *src)
3322 int i;
3324 for (i=0; (i<MAX_WORDS_PER_LINE) && (src[i].left != 0); i++) {
3325 include_into_list(dest, &src[i]);
3327 remove_redundant_columns(dest);
3331 * remove_entry_in_line - removes an entry, j, in, line.
3334 void html_printer::remove_entry_in_line (struct text_defn *line, int j)
3336 while (line[j].left != 0) {
3337 line[j].left = line[j+1].left;
3338 line[j].right = line[j+1].right;
3339 j++;
3344 * remove_redundant_columns - searches through the array columns and removes any redundant entries.
3347 void html_printer::remove_redundant_columns (struct text_defn *line)
3349 int i=0;
3350 int j=0;
3352 while (line[i].left != 0) {
3353 if ((i<MAX_WORDS_PER_LINE) && (line[i+1].left != 0)) {
3354 j = 0;
3355 while ((j<MAX_WORDS_PER_LINE) && (line[j].left != 0)) {
3356 if ((j != i) && (is_intersection(line[i].left, line[i].right, line[j].left, line[j].right))) {
3357 line[i].left = min(line[i].left , line[j].left);
3358 line[i].right = max(line[i].right, line[j].right);
3359 remove_entry_in_line(line, j);
3360 } else {
3361 j++;
3365 i++;
3370 * include_into_list - performs an order set inclusion
3373 void html_printer::include_into_list (struct text_defn *line, struct text_defn *item)
3375 int i=0;
3377 while ((i<MAX_WORDS_PER_LINE) && (line[i].left != 0) && (line[i].left<item->left)) {
3378 i++;
3381 if (line[i].left == 0) {
3382 // add to the end
3383 if (i<MAX_WORDS_PER_LINE) {
3384 if ((i>0) && (line[i-1].left > item->left)) {
3385 fatal("insertion error");
3387 line[i].left = item->left;
3388 line[i].right = item->right;
3389 i++;
3390 line[i].left = 0;
3391 line[i].right = 0;
3393 } else {
3394 if (line[i].left == item->left) {
3395 line[i].right = max(item->right, line[i].right);
3396 } else {
3397 // insert
3398 int left = item->left;
3399 int right = item->right;
3400 int l = line[i].left;
3401 int r = line[i].right;
3403 while ((i+1<MAX_WORDS_PER_LINE) && (line[i].left != 0)) {
3404 line[i].left = left;
3405 line[i].right = right;
3406 i++;
3407 left = l;
3408 right = r;
3409 l = line[i].left;
3410 r = line[i].right;
3412 if (i+1<MAX_WORDS_PER_LINE) {
3413 line[i].left = left;
3414 line[i].right = right;
3415 line[i+1].left = 0;
3416 line[i+1].right = 0;
3423 * is_in_column - return TRUE if value is present in line.
3426 int html_printer::is_in_column (struct text_defn *line, struct text_defn *item, int max_words)
3428 int i=0;
3430 while ((i<max_words) && (line[i].left != 0)) {
3431 if (line[i].left == item->left) {
3432 return( TRUE );
3433 } else {
3434 i++;
3437 return( FALSE );
3441 * calculate_right - calculate the right most margin for each column in line.
3444 void html_printer::calculate_right (struct text_defn *line, int max_words)
3446 int i=0;
3448 while ((i<max_words) && (line[i].left != 0)) {
3449 if (i>0) {
3450 line[i-1].right = line[i].left;
3452 i++;
3457 * add_right_full_width - adds an extra column to the right to bring the table up to
3458 * full width.
3461 void html_printer::add_right_full_width (struct text_defn *line, int mingap)
3463 int i=0;
3465 while ((i<MAX_WORDS_PER_LINE) && (line[i].left != 0)) {
3466 i++;
3469 if ((i>0) && (line[i-1].right != right_margin_indent) && (i+1<MAX_WORDS_PER_LINE)) {
3470 line[i].left = min(line[i-1].right+mingap, right_margin_indent);
3471 line[i].right = right_margin_indent;
3472 i++;
3473 if (i<MAX_WORDS_PER_LINE) {
3474 line[i].left = 0;
3475 line[i].right = 0;
3481 * determine_right_most_column - works out the right most limit of the right most column.
3482 * Required as we might be performing a .2C and only
3483 * have enough text to fill the left column.
3486 void html_printer::determine_right_most_column (struct text_defn *line, int max_words)
3488 int i=0;
3490 while ((i<max_words) && (line[i].left != 0)) {
3491 i++;
3493 if (i>0) {
3494 // remember right_margin_indent is the right most position for this page
3495 line[i-1].right = column_calculate_right_margin(line[i-1].left, right_margin_indent);
3500 * is_column_match - returns TRUE if a word is aligned in the same horizontal alignment
3501 * between two lines, line1 and line2. If so then this horizontal
3502 * position is saved in match.
3505 int html_printer::is_column_match (struct text_defn *match,
3506 struct text_defn *line1, struct text_defn *line2, int max_words)
3508 int i=0;
3509 int j=0;
3510 int found=FALSE;
3511 int first=(match[0].left==0);
3513 if (first) {
3514 struct text_defn t;
3516 t.left = left_margin_indent;
3517 t.right = 0;
3519 include_into_list(match, &t);
3521 while ((line1[i].left != 0) && (line2[i].left != 0)) {
3522 if (line1[i].left == line2[j].left) {
3523 // same horizontal alignment found
3524 include_into_list(match, &line1[i]);
3525 i++;
3526 j++;
3527 found = TRUE;
3528 } else if (line1[i].left < line2[j].left) {
3529 i++;
3530 } else {
3531 j++;
3534 calculate_right(match, max_words);
3535 return( found );
3539 * check_lack_of_hits - returns TRUE if a column has been moved to a position
3540 * of only one hit from a position of more than one hit.
3543 int html_printer::check_lack_of_hits (struct text_defn *next_guess,
3544 struct text_defn *last_guess,
3545 text_glob *start, int limit)
3547 text_glob *current=page_contents->words.get_data();
3548 int n=count_columns(last_guess);
3549 int m=count_columns(next_guess);
3550 int i, j;
3552 if (limit > 0) {
3553 rewind_text_to(start);
3554 count_hits(last_guess, n, limit);
3555 rewind_text_to(current);
3556 i=0;
3557 j=0;
3558 while ((i<n) && (j<m) &&
3559 (last_guess[i].left != 0) && (next_guess[j].left != 0)) {
3560 if ((is_intersection(last_guess[i].left, last_guess[i].right,
3561 next_guess[j].left, next_guess[j].right)) &&
3562 (next_guess[j].left < last_guess[i].left) &&
3563 (last_guess[i].is_used >= 2)) {
3565 * next_guess has to be = 1 as this position is new
3567 return( TRUE );
3569 if (last_guess[i].left < next_guess[j].left) {
3570 i++;
3571 } else {
3572 j++;
3576 return( FALSE );
3580 * remove_white_using_words - remove white space in, last_guess, by examining, next_line
3581 * placing results into next_guess.
3582 * It returns TRUE if the same columns exist in next_guess and last_guess
3583 * we do allow columns to shrink but if a column disappears then we return FALSE.
3586 int html_printer::remove_white_using_words (struct text_defn *next_guess,
3587 struct text_defn *last_guess, struct text_defn *next_line)
3589 int i=0;
3590 int j=0;
3591 int k=0;
3592 int removed=FALSE;
3594 while ((last_guess[j].left != 0) && (next_line[k].left != 0)) {
3595 if (last_guess[j].left == next_line[k].left) {
3596 // same horizontal alignment found
3597 next_guess[i].left = last_guess[j].left;
3598 next_guess[i].right = max(last_guess[j].right, next_line[k].right);
3599 i++;
3600 j++;
3601 k++;
3602 if ((next_guess[i-1].right > last_guess[j].left) && (last_guess[j].left != 0)) {
3603 removed = TRUE;
3605 } else if (last_guess[j].right < next_line[k].left) {
3606 next_guess[i].left = last_guess[j].left;
3607 next_guess[i].right = last_guess[j].right;
3608 i++;
3609 j++;
3610 } else if (last_guess[j].left > next_line[k].right) {
3611 // insert a word sequence from next_line[k]
3612 next_guess[i].left = next_line[k].left;
3613 next_guess[i].right = next_line[k].right;
3614 i++;
3615 k++;
3616 } else if (is_intersection(last_guess[j].left, last_guess[j].right, next_line[k].left, next_line[k].right)) {
3617 // potential for a column disappearing
3618 next_guess[i].left = min(last_guess[j].left , next_line[k].left);
3619 next_guess[i].right = max(last_guess[j].right, next_line[k].right);
3620 i++;
3621 j++;
3622 k++;
3623 if ((next_guess[i-1].right > last_guess[j].left) && (last_guess[j].left != 0)) {
3624 removed = TRUE;
3628 while (next_line[k].left != 0) {
3629 next_guess[i].left = next_line[k].left;
3630 next_guess[i].right = next_line[k].right;
3631 i++;
3632 k++;
3634 if (i<MAX_WORDS_PER_LINE) {
3635 next_guess[i].left = 0;
3636 next_guess[i].right = 0;
3638 if (debug_table_on) {
3639 if (removed) {
3640 fprintf(stderr, "have removed column\n");
3641 } else {
3642 fprintf(stderr, "have NOT removed column\n");
3644 fflush(stderr);
3646 remove_redundant_columns(next_guess);
3647 return( removed );
3651 * count_columns - returns the number of elements inside, line.
3654 int html_printer::count_columns (struct text_defn *line)
3656 int i=0;
3658 while (line[i].left != 0) {
3659 i++;
3661 return( i );
3665 * rewind_text_to - moves backwards until page_contents is looking at, g.
3668 void html_printer::rewind_text_to (text_glob *g)
3670 while (page_contents->words.get_data() != g) {
3671 if (page_contents->words.is_equal_to_head()) {
3672 page_contents->words.start_from_tail();
3673 } else {
3674 page_contents->words.move_left();
3680 * can_loose_column - checks to see whether we should combine two columns.
3681 * This is allowed if there are is only one hit on the
3682 * left hand edge and the previous column is very close.
3685 void html_printer::can_loose_column (text_glob *start, struct text_defn *last_guess, int limit)
3687 text_glob *current=page_contents->words.get_data();
3688 int n=count_columns(last_guess);
3689 int i;
3691 rewind_text_to(start);
3692 count_hits(last_guess, n, limit);
3693 i=0;
3694 while (i<n-1) {
3695 if ((last_guess[i+1].is_used == 1) &&
3696 (calculate_min_gap(start) > (last_guess[i+1].left-last_guess[i].right))) {
3697 last_guess[i].right = last_guess[i+1].right;
3698 remove_entry_in_line(last_guess, i+1);
3699 n = count_columns(last_guess);
3700 i = 0;
3701 } else {
3702 i++;
3705 rewind_text_to(current);
3709 * display_columns - a long overdue debugging function, as this column code is causing me grief :-(
3712 void html_printer::display_columns (const char *word, const char *name, text_defn *line)
3714 int i=0;
3716 fprintf(stderr, "[%s:%s]", name, word);
3717 while (line[i].left != 0) {
3718 fprintf(stderr, " <left=%d right=%d %d%%> ", line[i].left, line[i].right, line[i].percent);
3719 i++;
3721 fprintf(stderr, "\n");
3722 fflush(stderr);
3726 * copy_line - dest = src
3729 void html_printer::copy_line (struct text_defn *dest, struct text_defn *src)
3731 int k;
3733 for (k=0; ((src[k].left != 0) && (k<MAX_WORDS_PER_LINE)); k++) {
3734 dest[k].left = src[k].left;
3735 dest[k].right = src[k].right;
3737 if (k<MAX_WORDS_PER_LINE) {
3738 dest[k].left = 0;
3739 dest[k].right = 0;
3744 * add_column_gaps - adds empty columns between columns which don't exactly align
3747 void html_printer::add_column_gaps (struct text_defn *line)
3749 int i=0;
3750 struct text_defn t;
3752 // firstly lets see whether we need an initial column on the left hand side
3753 if ((line[0].left != get_left()) && (line[0].left != 0) &&
3754 (get_left() < line[0].left) && (is_worth_column(get_left(), line[0].left))) {
3755 t.left = get_left();
3756 t.right = line[0].left;
3757 include_into_list(line, &t);
3760 while ((i<MAX_WORDS_PER_LINE) && (line[i].left != 0)) {
3761 if ((i+1<MAX_WORDS_PER_LINE) && (line[i+1].left != 0) && (line[i].right != line[i+1].left) &&
3762 (is_worth_column(line[i].right, line[i+1].left))) {
3763 t.left = line[i].right;
3764 t.right = line[i+1].left;
3765 include_into_list(line, &t);
3766 i=0;
3767 } else {
3768 i++;
3771 // now let us see whether we need a final column on the right hand side
3772 if ((i>0) && (line[i-1].right != right_margin_indent) &&
3773 (is_worth_column(line[i-1].right, right_margin_indent))) {
3774 t.left = line[i-1].right;
3775 t.right = right_margin_indent;
3776 include_into_list(line, &t);
3781 * is_continueous_column - returns TRUE if a line has a word on one
3782 * of the last_col right most boundaries.
3785 int html_printer::is_continueous_column (text_defn *last_col, text_defn *next_line)
3787 int w = count_columns(next_line);
3788 int c = count_columns(last_col);
3789 int i, j;
3791 for (i=0; i<c; i++) {
3792 for (j=0; j<w; j++) {
3793 if (last_col[i].right == next_line[j].right) {
3794 return( TRUE );
3798 return( FALSE );
3802 * is_exact_left - returns TRUE if a line has a word on one
3803 * of the last_col left most boundaries.
3806 int html_printer::is_exact_left (text_defn *last_col, text_defn *next_line)
3808 int w = count_columns(next_line);
3809 int c = count_columns(last_col);
3810 int i, j;
3812 for (i=0; i<c; i++) {
3813 for (j=0; j<w; j++) {
3814 if ((last_col[i].left == next_line[j].left) ||
3815 (last_col[i].left != left_margin_indent)) {
3816 return( TRUE );
3820 return( FALSE );
3824 * continue_searching_column - decides whether we should carry on searching text for a column.
3827 int html_printer::continue_searching_column (text_defn *next_col,
3828 text_defn *last_col,
3829 text_defn *all_words)
3831 int count = count_columns(next_col);
3832 int words = count_columns(all_words);
3834 if ((words == 0) || ((words == 1) &&
3835 (all_words[0].left == left_margin_indent) &&
3836 (all_words[0].right == right_margin_indent))) {
3837 // no point as we have now seen a full line of contineous text with no gap
3838 return( FALSE );
3840 return( (count == count_columns(last_col)) &&
3841 (last_col[0].left != left_margin_indent) || (last_col[0].right != right_margin_indent) );
3845 * is_worth_column - returns TRUE if the size of this column is worth defining.
3848 int html_printer::is_worth_column (int left, int right)
3850 #if 0
3851 return( abs(right-left) >= MIN_COLUMN );
3852 #endif
3853 return( TRUE );
3857 * large_enough_gap - returns TRUE if a large enough gap for one line was seen.
3858 * We need to make sure that a single line definitely warrents
3859 * a table.
3860 * It also removes other smaller gaps.
3863 int html_printer::large_enough_gap (text_defn *last_col)
3865 int i=0;
3866 int found=FALSE;
3867 int r=font::res;
3868 int gap=r/GAP_WIDTH_ONE_LINE;
3870 if (abs(last_col[i].left - left_margin_indent) >= gap) {
3871 found = TRUE;
3873 while ((last_col[i].left != 0) && (last_col[i+1].left != 0)) {
3874 if (abs(last_col[i+1].left-last_col[i].right) >= gap) {
3875 found = TRUE;
3876 i++;
3877 } else {
3878 // not good enough for a single line, remove it
3879 last_col[i].right = last_col[i+1].right;
3880 remove_entry_in_line(last_col, i+1);
3883 return( found );
3887 * is_subset_of_columns - returns TRUE if line, a, is a subset of line, b.
3890 int html_printer::is_subset_of_columns (text_defn *a, text_defn *b)
3892 int i;
3893 int j;
3895 i=0;
3896 while ((i<MAX_WORDS_PER_LINE) && (a[i].left != 0)) {
3897 j=0;
3898 while ((j<MAX_WORDS_PER_LINE) && (b[j].left != 0) &&
3899 ((b[j].left != a[i].left) || (b[j].right != a[i].right))) {
3900 j++;
3902 if ((j==MAX_WORDS_PER_LINE) || (b[j].left == 0)) {
3903 // found a different column - not a subset
3904 return( FALSE );
3906 i++;
3908 return( TRUE );
3912 * count_hits - counts the number of hits per column. A left hit
3913 * is when the left hand position of a glob hits
3914 * the left hand column.
3917 void html_printer::count_hits (text_defn *col, int no_of_columns, int limit)
3919 int i;
3920 text_glob *start = page_contents->words.get_data();
3921 text_glob *g = start;
3923 // firstly reset the used field
3924 for (i=0; i<no_of_columns; i++) {
3925 col[i].is_used = 0;
3927 // now calculate the left hand hits
3928 while ((g != 0) && (g->minv <= limit)) {
3929 i=0;
3930 while ((i<no_of_columns) && (col[i].right < g->minh)) {
3931 i++;
3933 if ((col[i].left == g->minh) && (col[i].left != 0)) {
3934 col[i].is_used++;
3936 page_contents->words.move_right();
3937 if (page_contents->words.is_equal_to_head()) {
3938 g = 0;
3939 page_contents->words.start_from_tail();
3940 } else {
3941 g=page_contents->words.get_data();
3947 * count_right_hits - counts the number of right hits per column.
3948 * A right hit is when the left hand position
3949 * of a glob hits the right hand column.
3952 void html_printer::count_right_hits (text_defn *col, int no_of_columns)
3954 int i;
3955 text_glob *start = page_contents->words.get_data();
3956 text_glob *g = start;
3958 // firstly reset the used field
3959 for (i=0; i<no_of_columns; i++) {
3960 col[i].right_hits = 0;
3962 // now calculate the left hand hits
3963 while ((g != 0) && (g->minv <= indentation.vertical_limit)) {
3964 i=0;
3965 while ((i<no_of_columns) && (col[i].right < g->minh)) {
3966 i++;
3968 if ((i<no_of_columns) && (col[i].right == g->maxh)) {
3969 if (debug_table_on) {
3970 fprintf(stderr, "found right hit [%s] at %d in %d\n",
3971 g->text_string, g->maxh, i);
3972 fflush(stderr);
3974 col[i].right_hits++;
3976 page_contents->words.move_right();
3977 if (page_contents->words.is_equal_to_head()) {
3978 g = 0;
3979 page_contents->words.start_from_tail();
3980 } else {
3981 g=page_contents->words.get_data();
3987 * right_indentation - returns TRUE if a single column has been found and
3988 * it resembles an indentation. Ie .RS/.RE or ABSTACT
3991 int html_printer::right_indentation (struct text_defn *last_guess)
3993 // it assumes that last_guess contains a single column
3994 return( (last_guess[0].left > left_margin_indent) );
3998 * able_to_steal_width - returns TRUE if we have an unused column which we can steal from.
3999 * It must have more than MIN_TEXT_PERCENT to do this.
4002 int html_printer::able_to_steal_width (void)
4004 int i;
4006 for (i=0; i<indentation.no_of_columns; i++) {
4007 if ((! indentation.columns[i].is_used) &&
4008 (indentation.columns[i].percent > MIN_TEXT_PERCENT)) {
4009 return( TRUE );
4012 return( FALSE );
4016 * is_divisible_by - returns TRUE if n is divisible by d leaving no remainder.
4019 static int is_divisible_by (int n, int d)
4021 return( (n % d) == 0 );
4025 * need_to_steal_width - returns TRUE if a used column need to be
4026 * given a little extra width for safty sake.
4029 int html_printer::need_to_steal_width (void)
4031 int i;
4033 for (i=0; i<indentation.no_of_columns; i++) {
4034 if ((indentation.columns[i].is_used) &&
4035 (indentation.columns[i].percent == (((indentation.columns[i].right - indentation.columns[i].left) * 100) /
4036 (right_margin_indent-left_margin_indent))) &&
4037 (indentation.columns[i].percent < PERCENT_THRESHOLD)) {
4038 return( TRUE );
4041 return( FALSE );
4045 * utilize_round_off - utilize the remaining percent width in text columns
4048 void html_printer::utilize_round_off (void)
4050 int total = total_percentages();
4051 int excess, i;
4053 // use up the spare excess
4055 excess = 100-total;
4057 for (i=0; (i<indentation.no_of_columns) && (excess>0); i++) {
4058 if ((indentation.columns[i].is_used) &&
4059 (indentation.columns[i].percent < PERCENT_THRESHOLD)) {
4060 indentation.columns[i].percent++;
4061 excess--;
4064 // we might as well try and keep any numbers simple if possible
4065 for (i=0; (i<indentation.no_of_columns) && (excess>0); i++) {
4066 if ((indentation.columns[i].is_used) &&
4067 (! is_divisible_by(indentation.columns[i].percent, MIN_TEXT_PERCENT))) {
4068 indentation.columns[i].percent++;
4069 excess--;
4072 // forget the niceties lets just use excess up now!
4073 for (i=0; (i<indentation.no_of_columns) && (excess>0); i++) {
4074 if (indentation.columns[i].is_used) {
4075 indentation.columns[i].percent++;
4076 excess--;
4082 * can_distribute_fairly - returns TRUE if we can redistribute some of the unused width into
4083 * columns that are used.
4086 int html_printer::can_distribute_fairly (void)
4088 int i;
4089 int total=0;
4090 int used =0;
4091 int excess;
4093 // firstly total up all percentages - so we can use round offs
4094 for (i=0; i<indentation.no_of_columns; i++) {
4095 total += indentation.columns[i].percent;
4096 if ((indentation.columns[i].is_used) &&
4097 (indentation.columns[i].percent < PERCENT_THRESHOLD)) {
4098 used++;
4102 excess = 100-total;
4103 if (excess < used) {
4104 for (i=0; i<indentation.no_of_columns; i++) {
4105 if (! indentation.columns[i].is_used) {
4106 if (indentation.columns[i].percent > MIN_TEXT_PERCENT) {
4107 indentation.columns[i].percent--;
4108 excess++;
4113 if (excess >= used) {
4114 for (i=0; i<indentation.no_of_columns; i++) {
4115 if ((indentation.columns[i].is_used) &&
4116 (indentation.columns[i].percent < PERCENT_THRESHOLD) &&
4117 (indentation.columns[i].percent == (((indentation.columns[i].right - indentation.columns[i].left) * 100) /
4118 (right_margin_indent-left_margin_indent)))) {
4119 indentation.columns[i].percent++;
4120 excess--;
4123 return( TRUE );
4125 return( FALSE );
4129 * remove_table_column - removes column, i, from the indentation.
4132 void html_printer::remove_table_column (int i)
4134 while (i<indentation.no_of_columns) {
4135 indentation.columns[i].left = indentation.columns[i+1].left;
4136 indentation.columns[i].right = indentation.columns[i+1].right;
4137 indentation.columns[i].is_used = indentation.columns[i+1].is_used;
4138 indentation.columns[i].percent = indentation.columns[i+1].percent;
4139 i++;
4141 indentation.no_of_columns--;
4145 * next_line_on_left_column - returns TRUE if the next line in
4146 * column, i, has a word on the left margin.
4149 int html_printer::next_line_on_left_column (int i, text_glob *start)
4151 int current_vpos=start->minv;
4153 while ((start != 0) && (start->minv < indentation.vertical_limit) &&
4154 (is_on_same_line(start, current_vpos))) {
4155 if (page_contents->words.is_equal_to_tail()) {
4156 start = 0;
4157 } else {
4158 page_contents->words.move_right();
4159 start = page_contents->words.get_data();
4162 if ((start != 0) && (start->minv < indentation.vertical_limit)) {
4163 // onto next line now
4164 current_vpos=start->minv;
4165 while ((start != 0) && (start->minv < indentation.vertical_limit) &&
4166 (is_on_same_line(start, current_vpos))) {
4167 if (start->minh == indentation.columns[i].left) {
4168 return( TRUE );
4170 if (page_contents->words.is_equal_to_tail()) {
4171 start = 0;
4172 } else {
4173 page_contents->words.move_right();
4174 start = page_contents->words.get_data();
4178 return( FALSE );
4182 * will_wrap_text - returns TRUE if text is wrapped in column, i.
4185 int html_printer::will_wrap_text (int i, text_glob *start)
4187 text_glob *current=page_contents->words.get_data();
4189 if (auto_on) {
4190 rewind_text_to(start);
4191 while ((start != 0) && (start->minv < indentation.vertical_limit)) {
4192 if (indentation.columns[i].right == start->maxh) {
4193 // ok right word is on column boarder - check next line
4194 if (next_line_on_left_column(i, start)) {
4195 rewind_text_to(current);
4196 return( TRUE );
4199 if (page_contents->words.is_equal_to_tail()) {
4200 start = 0;
4201 } else {
4202 page_contents->words.move_right();
4203 start = page_contents->words.get_data();
4207 rewind_text_to(current);
4208 return( FALSE );
4212 * remove_unnecessary_unused - runs through a table and decides whether an unused
4213 * column can be removed. This is only true if the
4214 * column to the left does not wrap text.
4217 void html_printer::remove_unnecessary_unused (text_glob *start)
4219 int i=0;
4220 int left=get_left();
4221 int right;
4223 while (i<indentation.no_of_columns) {
4224 if ((indentation.columns[i].is_used) &&
4225 (i+1<indentation.no_of_columns) && (! indentation.columns[i+1].is_used)) {
4227 * so i+1 is unused and there is a used column to the left.
4228 * Now we check whether we can add the unused column to the column, i.
4229 * This can only be done if column, i, is not wrapping text.
4231 if (! will_wrap_text(i, start)) {
4232 #if 1
4233 if (i+1 < indentation.no_of_columns) {
4234 right = indentation.columns[i+1].right;
4235 } else {
4236 right = right_margin_indent;
4238 indentation.columns[i].percent = (((right - indentation.columns[i].left) * 100) /
4239 (right_margin_indent-left));
4240 #else
4241 indentation.columns[i].percent = (((indentation.columns[i+1].right - indentation.columns[i].left) * 100) /
4242 (right_margin_indent-left));
4243 #endif
4244 remove_table_column(i+1);
4245 i=-1;
4248 i++;
4253 * remove_zero_percentage_column - removes all zero percentage width columns
4256 void html_printer::remove_zero_percentage_column (void)
4258 int i=0;
4260 while (i<indentation.no_of_columns) {
4261 if (indentation.columns[i].percent == 0) {
4262 remove_table_column(i);
4263 i=0;
4264 } else {
4265 i++;
4271 * get_left - returns the actual left most margin.
4274 int html_printer::get_left (void)
4276 if ((header_indent < left_margin_indent) && (header_indent != -1)) {
4277 return( header_indent );
4278 } else {
4279 return( left_margin_indent );
4284 * calculate_percentage_width - calculates the percentage widths,
4285 * this function will be generous to
4286 * columns which have words as some browsers
4287 * produce messy output if the percentage is exactly
4288 * that required for text..
4289 * We try and round up to MIN_TEXT_PERCENT
4290 * of course we can only do this if we can steal from
4291 * an unused column.
4294 void html_printer::calculate_percentage_width (text_glob *start)
4296 int i;
4297 int left=get_left();
4298 int right;
4300 // firstly calculate raw percentages
4301 for (i=0; i<indentation.no_of_columns; i++) {
4302 #if 0
4303 indentation.columns[i].percent = (((indentation.columns[i].right - indentation.columns[i].left) * 100) /
4304 (right_margin_indent-left));
4305 #else
4306 if (i+1 < indentation.no_of_columns) {
4307 right = indentation.columns[i+1].left;
4308 } else {
4309 right = right_margin_indent;
4311 indentation.columns[i].percent = (((right - indentation.columns[i].left) * 100) /
4312 (right_margin_indent-left));
4313 #endif
4315 if (debug_table_on) {
4316 display_columns(start->text_string, "[b4 steal] indentation.columns", indentation.columns);
4319 // now steal from the unused columns..
4320 remove_unnecessary_unused(start);
4322 if (debug_table_on) {
4323 display_columns(start->text_string, "[after steal] indentation.columns", indentation.columns);
4326 #if 0
4327 utilize_round_off();
4328 #endif
4329 remove_zero_percentage_column();
4334 * is_column_subset - returns TRUE if the columns described by small can be contained in
4335 * the columns in large.
4338 int html_printer::is_column_subset (struct text_defn *small, struct text_defn *large)
4340 int ns=count_columns(small);
4341 int nl=count_columns(large);
4342 int found;
4343 int i=0;
4344 int j;
4346 while (i<ns) {
4347 j=0;
4348 found = FALSE;
4349 while (j<nl) {
4350 if (is_intersection(small[i].left, small[i].right, large[j].left, large[j].right)) {
4351 found = TRUE;
4352 if (! is_subsection(small[i].left, small[i].right, large[j].left, large[j].right)) {
4353 // found column which is not a subset
4354 return( FALSE );
4357 j++;
4359 if (! found) {
4360 return( FALSE );
4362 i++;
4364 // small cannot be an empty set
4365 return( ns>0 );
4369 * right_most_column - returns the right most column position.
4372 int html_printer::right_most_column (struct text_defn *col)
4374 int i = count_columns(col);
4376 if (i>0) {
4377 return( col[i-1].right );
4378 } else {
4379 return( 0 );
4384 * large_enough_gap_for_two - returns TRUE if there exists a large enough gap
4385 * for two lines.
4388 int html_printer::large_enough_gap_for_two (struct text_defn *col)
4390 int i=0;
4391 int found=FALSE;
4392 int gap=MIN_COLUMN_FOR_TWO_LINES;
4394 if (abs(col[i].left - left_margin_indent) >= gap) {
4395 found = TRUE;
4397 while ((col[i].left != 0) && (col[i+1].left != 0)) {
4398 if (abs(col[i+1].left-col[i].right) >= gap) {
4399 found = TRUE;
4400 i++;
4401 } else {
4402 // not good enough for this table, remove it
4403 col[i].right = col[i+1].right;
4404 remove_entry_in_line(col, i+1);
4407 return( found );
4411 * is_small_table - applies some rigorous rules to test whether we should start this
4412 * table at this point.
4415 int html_printer::is_small_table (int lines, struct text_defn *last_guess,
4416 struct text_defn *words_1, struct text_defn *cols_1,
4417 struct text_defn *words_2, struct text_defn *cols_2,
4418 int *limit, int *limit_1)
4421 * firstly we check for an indented paragraph
4424 if ((lines >= 2) &&
4425 (count_columns(cols_1) == count_columns(cols_2)) && (count_columns(cols_1) == 1) &&
4426 right_indentation(cols_1) && (! right_indentation(cols_2)) &&
4427 (cols_1[0].right == right_margin_indent)) {
4428 return( FALSE );
4431 if (lines == 2) {
4433 * as we only have two lines in our table we need to examine in detail whether
4434 * we should construct a table from these two lines.
4435 * For example if the text is the start of an indented paragraph and
4436 * line1 and line2 are contineous then they should form one row in our table but
4437 * if line1 and line2 are not contineous it is safer to treat them separately.
4439 * We are prepared to reduce the table to one line
4441 if (((count_columns(cols_1) != count_columns(cols_2)) && (cols_1[0].left > cols_2[0].left)) ||
4442 (! ((is_column_subset(cols_1, cols_2)) ||
4443 (is_column_subset(cols_2, cols_1))))) {
4445 * now we must check to see whether line1 and line2 join
4447 if ((right_most_column(cols_1) == right_margin_indent) &&
4448 (cols_2[0].left == left_margin_indent)) {
4450 * looks like they join, we don't want a table at all.
4452 return( FALSE );
4455 * use single line table
4457 lines--;
4458 *limit = *limit_1;
4459 copy_line(last_guess, cols_1);
4463 if ((count_columns(last_guess)==1) && (right_indentation(last_guess))) {
4464 if (lines == 1) {
4465 *limit = *limit_1;
4467 return( TRUE );
4471 * check for large gap with single line or if multiple lines with more than one column
4474 if (lines == 1) {
4475 if (large_enough_gap(last_guess)) {
4476 *limit = *limit_1;
4477 return( TRUE );
4479 } else if (count_columns(last_guess)>1) {
4480 if (lines == 2) {
4481 return( large_enough_gap_for_two(last_guess) );
4483 return( TRUE );
4485 return( FALSE );
4490 * is_appropriate_to_start_table - returns TRUE if it is appropriate to start the table
4491 * at this point.
4494 int html_printer::is_appropriate_to_start_table (struct text_defn *cols_1,
4495 struct text_defn *cols_2,
4496 struct text_defn *last_guess)
4498 if (count_columns(last_guess) == 1) {
4499 if (debug_table_on) {
4500 display_columns("", "[is] cols_1" , cols_1);
4501 display_columns("", "[is] cols_2" , cols_2);
4502 display_columns("", "[is] last_guess", last_guess);
4505 if (! ((is_column_subset(cols_1, cols_2)) ||
4506 (is_column_subset(cols_2, cols_1)))) {
4507 return( FALSE );
4509 if ((count_columns(cols_1) == 1) &&
4510 (cols_1[0].left > left_margin_indent) && (cols_1[0].right < right_margin_indent) &&
4511 (cols_1[0].right != cols_2[0].right) &&
4512 (count_columns(last_guess) == 1)) {
4513 return( FALSE );
4516 return( TRUE );
4520 * is_a_full_width_column - returns TRUE if there exists a full width column.
4523 int html_printer::is_a_full_width_column (void)
4525 int i=0;
4527 while (i<indentation.no_of_columns) {
4528 if (((indentation.columns[i].left == get_left()) ||
4529 (indentation.columns[i].left == left_margin_indent)) &&
4530 (indentation.columns[i].right == right_margin_indent)) {
4531 return( TRUE );
4533 i++;
4535 return( FALSE );
4539 * should_defer_table - returns TRUE if we should defer this table.
4540 * This can occur if the first line seen indent
4541 * is < than future lines. In which case it
4542 * will cause future lines in this table
4543 * to be indented. The lesser of the evils
4544 * is to treat the first line by itself.
4547 int html_printer::should_defer_table (int lines, struct text_glob *start, struct text_defn *cols_1)
4549 if (lines > 2) {
4550 int i=0;
4551 int c=count_columns(cols_1);
4553 count_hits(cols_1, count_columns(cols_1), indentation.vertical_limit);
4554 rewind_text_to(start);
4555 count_right_hits(cols_1, count_columns(cols_1));
4556 rewind_text_to(start);
4557 while (i<c) {
4558 if ((cols_1[i].is_used > 1) || (cols_1[i].right_hits > 1)) {
4559 return( FALSE );
4561 i++;
4564 * first line (cols_1) is not aligned on any future column, we defer.
4566 return( TRUE );
4568 return( FALSE );
4572 * is_new_exact_right - returns TRUE if the, next_cols, has a word sitting
4573 * on the right hand margin of last_guess. But only
4574 * if no exact right word was found in last_cols.
4577 int html_printer::is_new_exact_right (struct text_defn *last_guess,
4578 struct text_defn *last_cols,
4579 struct text_defn *next_cols)
4581 int n=count_columns(last_guess)-1;
4582 return( FALSE );
4584 if ((n>=0) && (last_guess[n].left != 0) && (last_cols[n].left != 0) && (next_cols[n].left != 0)) {
4585 if ((last_cols[n].right != last_guess[n].right) &&
4586 ((next_cols[n].right == last_guess[n].right) || (next_cols[n].right == right_margin_indent))) {
4587 return( TRUE );
4590 return( FALSE );
4594 * found_use_for_table - checks whether the some words on one line directly match
4595 * the horizontal alignment of the line below.
4596 * This is rather complex as we need to detect text tables
4597 * such as .2C .IP Abstracts and indentations
4599 * Algorithm is:
4601 * read first line of text and calculate the significant
4602 * gaps between words
4603 * next next line of text and do the same
4604 * if a conflict between these lines exists and
4605 * first line is centered
4606 * then
4607 * return centered line
4608 * elsif start of a table is found
4609 * then
4610 * repeat
4611 * read next line of text and calculate significant gaps
4612 * until conflict between the gaps is found
4613 * record table
4614 * return table found
4615 * else
4616 * return no table found
4617 * fi
4620 int html_printer::found_use_for_table (text_glob *start)
4622 text_glob *t;
4623 struct text_defn all_words [MAX_WORDS_PER_LINE]; // logical OR of words on each line
4624 struct text_defn words_1 [MAX_WORDS_PER_LINE]; // actual words found on first line
4625 struct text_defn words_2 [MAX_WORDS_PER_LINE]; // actual words found on second line
4626 struct text_defn cols_1 [MAX_WORDS_PER_LINE]; // columns found on line 1
4627 struct text_defn cols_2 [MAX_WORDS_PER_LINE]; // columns found on line 2
4628 struct text_defn last_words [MAX_WORDS_PER_LINE]; // actual words found on last line
4629 struct text_defn last_cols [MAX_WORDS_PER_LINE]; // columns found so far
4630 struct text_defn next_words [MAX_WORDS_PER_LINE]; // actual words found on last line (new)
4631 struct text_defn next_cols [MAX_WORDS_PER_LINE]; // columns found on next line
4632 struct text_defn last_guess [MAX_WORDS_PER_LINE]; // columns found on last line
4633 // (logical AND of gaps (treat gaps = true))
4634 struct text_defn next_guess [MAX_WORDS_PER_LINE]; // columns found on next line
4635 // (logical AND of gaps (treat gaps = true))
4636 struct text_defn prev_guess [MAX_WORDS_PER_LINE]; // temporary copy of last_guess
4637 int i =0;
4638 int lines =1; // number of lines read
4639 int limit; // vertical limit reached in our table
4640 int limit_1; // vertical position after line 1
4642 #if 1
4643 if (strcmp(start->text_string, "This") == 0) {
4644 stop();
4646 #endif
4649 * get first set of potential columns into last_line, call this last_guess
4651 limit = collect_columns(words_1, cols_1, 0, 0, MAX_WORDS_PER_LINE);
4652 limit_1 = limit;
4653 copy_line(last_guess, cols_1);
4656 * initialize the all_words columns - if this should ever equal a complete line
4657 * with no gaps then we terminate the table.
4659 copy_line(all_words, cols_1);
4662 * and set the current limit found
4664 indentation.vertical_limit = limit;
4667 * have we reached the end of page?
4669 if (page_contents->words.is_equal_to_head() || (limit == 0)) {
4670 cols_2[0].left = 0;
4671 cols_2[0].right = 0;
4672 } else {
4674 * the answer to the previous question was no.
4675 * So we need to examine the next line
4677 limit = collect_columns(words_2, cols_2, words_1, cols_1, MAX_WORDS_PER_LINE);
4678 if (limit >= 0) {
4679 lines++;
4684 * now check to see whether the first line looks like a single centered line
4686 if (single_centered_line(cols_1, cols_2, start)) {
4687 rewind_text_to(start);
4688 write_centered_line(start);
4690 * indicate to caller than we have centered text, not found a table.
4692 indentation.no_of_columns = 0;
4693 return( TRUE );
4694 } else if (! table_on) {
4696 * user does not allow us to find a table (we are allowed to find centered lines (above))
4698 rewind_text_to(start);
4699 return( FALSE );
4703 * remove any gaps from all_words
4705 combine_line(all_words, cols_2);
4706 if (debug_table_on) {
4707 display_columns(start->text_string, "[1] all_words" , all_words);
4708 display_columns(start->text_string, "[1] cols_1" , cols_1);
4709 display_columns(start->text_string, "[1] words_1" , words_1);
4710 display_columns(start->text_string, "[1] cols_2" , cols_2);
4711 display_columns(start->text_string, "[1] words_2" , words_2);
4712 display_columns(start->text_string, "[1] last_guess", last_guess);
4716 * next_guess = last_guess AND next_cols (where gap = true)
4719 if (remove_white_using_words(prev_guess, last_guess, cols_2)) {
4721 if (remove_white_using_words(next_guess, prev_guess, all_words)) {
4724 if (debug_table_on) {
4725 display_columns(start->text_string, "[2] next_guess", next_guess);
4728 copy_line(prev_guess, cols_1);
4729 combine_line(prev_guess, cols_2);
4732 * if no sequence of words crosses a column and
4733 * both the last column and all_words are not a full solid line of text
4735 if ((! conflict_with_words(next_guess, all_words)) &&
4736 (continue_searching_column(next_guess, next_guess, all_words)) &&
4737 (is_appropriate_to_start_table(cols_1, cols_2, prev_guess)) &&
4738 (! page_contents->words.is_equal_to_head()) &&
4739 ((end_region_vpos < 0) || (limit < end_region_vpos)) &&
4740 (limit > 0)) {
4743 * subtract any columns which are bridged by a sequence of words
4746 copy_line(next_cols , cols_2);
4747 copy_line(next_words, words_2);
4749 do {
4750 copy_line(prev_guess, next_guess); // copy next_guess away so we can compare it later
4751 combine_line(last_guess, next_guess);
4753 if (debug_table_on) {
4754 t = page_contents->words.get_data();
4755 display_columns(t->text_string, "[l] last_guess", last_guess);
4757 indentation.vertical_limit = limit;
4759 copy_line(last_cols, next_cols);
4760 copy_line(last_words, next_words);
4761 if (page_contents->words.is_equal_to_head()) {
4763 * terminate the search
4765 next_cols[0].left = 0;
4766 next_cols[0].right = 0;
4767 } else {
4768 limit = collect_columns(next_words, next_cols, last_words, last_cols, MAX_WORDS_PER_LINE);
4769 lines++;
4772 combine_line(all_words, next_cols);
4773 if (debug_table_on) {
4774 display_columns(t->text_string, "[l] all_words" , all_words);
4775 display_columns(t->text_string, "[l] last_cols" , last_cols);
4776 display_columns(t->text_string, "[l] next_words", next_words);
4777 display_columns(t->text_string, "[l] next_cols" , next_cols);
4780 if (limit >= 0) {
4782 * (if limit is < 0 then the table ends anyway.)
4783 * we check to see whether we should combine close columns.
4785 can_loose_column(start, last_guess, limit);
4787 t = page_contents->words.get_data();
4788 #if 0
4789 if (strcmp(t->text_string, "heT") == 0) {
4790 stop();
4792 #endif
4794 } while ((! remove_white_using_words(next_guess, last_guess, next_cols)) &&
4795 (! conflict_with_words(next_guess, all_words)) &&
4796 (continue_searching_column(next_guess, last_guess, all_words)) &&
4797 ((is_continueous_column(prev_guess, last_cols)) || (is_exact_left(last_guess, next_cols))) &&
4798 (! is_new_exact_right(last_guess, last_cols, next_cols)) &&
4799 (! page_contents->words.is_equal_to_head()) &&
4800 (! check_lack_of_hits(next_guess, last_guess, start, limit)) &&
4801 ((end_region_vpos <= 0) || (t->minv < end_region_vpos)) &&
4802 (limit >= 0));
4803 lines--;
4806 if (limit < 0) {
4807 indentation.vertical_limit = limit;
4810 if (page_contents->words.is_equal_to_head()) {
4811 // end of page check whether we should include everything
4812 if ((! conflict_with_words(next_guess, all_words)) &&
4813 (continue_searching_column(next_guess, last_guess, all_words)) &&
4814 ((is_continueous_column(prev_guess, last_cols)) || (is_exact_left(last_guess, next_cols)))) {
4815 // end of page reached - therefore include everything
4816 page_contents->words.start_from_tail();
4817 t = page_contents->words.get_data();
4818 combine_line(last_guess, next_guess);
4819 indentation.vertical_limit = t->minv;
4821 } else {
4822 t = page_contents->words.get_data();
4823 if (((! conflict_with_words(last_guess, all_words))) &&
4824 (t->minv > end_region_vpos) && (end_region_vpos > 0)) {
4825 indentation.vertical_limit = limit;
4827 if ((end_region_vpos > 0) && (t->minv > end_region_vpos)) {
4828 indentation.vertical_limit = min(indentation.vertical_limit, end_region_vpos+1);
4829 } else if (indentation.vertical_limit < 0) {
4830 // -1 as we don't want to include section heading itself
4831 indentation.vertical_limit = -indentation.vertical_limit-1;
4835 if (debug_table_on) {
4836 display_columns(start->text_string, "[1] all_words" , all_words);
4837 display_columns(start->text_string, "[1] cols_1" , cols_1);
4838 display_columns(start->text_string, "[1] words_1" , words_1);
4839 display_columns(start->text_string, "[1] cols_2" , cols_2);
4840 display_columns(start->text_string, "[1] words_2" , words_2);
4841 display_columns(start->text_string, "[1] last_guess", last_guess);
4842 display_columns(start->text_string, "[1] next_guess", next_guess);
4844 rewind_text_to(start);
4846 i = count_columns(last_guess);
4847 if ((i>1) || (right_indentation(last_guess))) {
4849 // was (continue_searching_column(last_guess, last_guess, all_words)))) {
4850 if (should_defer_table(lines, start, cols_1)) {
4852 * yes, but let us check for a single line table
4854 lines = 1;
4855 copy_line(last_guess, cols_1);
4858 if (is_small_table(lines, last_guess, words_1, cols_1, words_2, cols_2,
4859 &indentation.vertical_limit, &limit_1)) {
4861 // copy match into permenant html_table
4863 if (indentation.columns != 0) {
4864 free(indentation.columns);
4866 if (debug_table_on) {
4867 display_columns(start->text_string, "[x] last_guess", last_guess);
4869 add_column_gaps(last_guess);
4870 if (debug_table_on) {
4871 display_columns(start->text_string, "[g] last_guess", last_guess);
4875 * +1 for the potential header_margin
4876 * +1 for null
4879 indentation.no_of_columns = count_columns(last_guess);
4880 indentation.columns = (struct text_defn *)malloc((indentation.no_of_columns+2)*sizeof(struct text_defn));
4882 i=0;
4883 while (i<=indentation.no_of_columns) {
4884 indentation.columns[i].left = last_guess[i].left;
4885 indentation.columns[i].right = last_guess[i].right;
4886 i++;
4889 if (indentation.no_of_columns>0) {
4890 assign_used_columns(start);
4891 rewind_text_to(start);
4892 calculate_percentage_width(start);
4894 if (debug_table_on) {
4895 display_columns(start->text_string, "[g] indentation.columns", indentation.columns);
4899 * clearly a single column 100% is not worth using a table.
4900 * Also we check to see whether the first line is sensibly
4901 * part of this table.
4903 if (is_a_full_width_column()) {
4904 indentation.no_of_columns = 0;
4905 free( indentation.columns );
4906 indentation.columns = 0;
4907 } else {
4908 return( TRUE );
4913 return( FALSE );
4917 * define_cell - creates a table cell using the percentage width.
4920 void html_printer::define_cell (int i)
4922 html.put_string("<td valign=\"top\" align=\"left\" width=\"");
4923 html.put_number(indentation.columns[i].percent);
4924 html.put_string("%\">\n");
4928 * column_display_word - given a left, right pair and the indentation.vertical_limit
4929 * write out html text within this region.
4932 void html_printer::column_display_word (int cell, int vert, int left, int right, int next)
4934 text_glob *g=page_contents->words.get_data();
4936 supress_sub_sup = TRUE;
4937 if (left != next) {
4938 define_cell(cell);
4939 begin_paragraph_no_height(left_alignment);
4940 while ((g != 0) && (g->minv <= vert)) {
4941 if ((left <= g->minh) && (g->minh<right)) {
4942 char *postword=html_position_text(g, left, right);
4944 if (header.written_header) {
4945 fatal("should never generate a header inside a table");
4946 } else {
4947 if (g->is_raw_command) {
4948 html.put_string((char *)g->text_string);
4949 } else {
4950 translate_to_html(g);
4952 if (postword != 0) {
4953 html.put_string(postword);
4955 issued_newline = FALSE;
4958 if (page_contents->words.is_equal_to_tail()) {
4959 g = 0;
4960 } else {
4961 page_contents->words.move_right();
4962 g=page_contents->words.get_data();
4965 end_paragraph();
4966 html.put_string("</td>\n");
4967 if (g != 0) {
4968 page_contents->words.move_left();
4969 // and correct output_vpos
4970 g=page_contents->words.get_data();
4971 output_vpos = g->minv;
4977 * total_percentages - returns the total of all the percentages in the table.
4980 int html_printer::total_percentages ()
4982 int i;
4983 int sum=0;
4985 for (i=0; i<indentation.no_of_columns; i++) {
4986 sum += indentation.columns[i].percent;
4988 return( sum );
4992 * start_table - creates a table according with parameters contained within class html_table.
4995 void html_printer::start_table (void)
4997 save_paragraph();
4998 html.put_string("\n<table width=\"");
4999 html.put_number(total_percentages());
5000 html.put_string("%\" rules=\"none\" frame=\"none\" cols=\"");
5001 html.put_number(indentation.no_of_columns);
5002 html.put_string("\" cellspacing=\"0\" cellpadding=\"0\">\n");
5006 * end_table - finishes off a table.
5009 void html_printer::end_table (void)
5011 html.put_string("</table>\n");
5012 indentation.no_of_columns = 0;
5013 restore_paragraph();
5014 supress_sub_sup = TRUE;
5018 * column_calculate_right_margin - scan through the column and find the right most margin
5021 int html_printer::column_calculate_right_margin (int left, int right)
5023 if (left == right) {
5024 return( right );
5025 } else {
5026 int rightmost =-1;
5027 int count = 0;
5028 text_glob *start = page_contents->words.get_data();
5029 text_glob *g = start;
5031 while ((g != 0) && (g->minv <= indentation.vertical_limit)) {
5032 if ((left <= g->minh) && (g->minh<right)) {
5033 if (debug_on) {
5034 fprintf(stderr, "right word = %s %d\n", g->text_string, g->maxh); fflush(stderr);
5036 if (g->maxh == rightmost) {
5037 count++;
5038 } else if (g->maxh > rightmost) {
5039 count = 1;
5040 rightmost = g->maxh;
5042 if (g->maxh > right) {
5043 if (debug_on) {
5044 fprintf(stderr, "problem as right word = %s %d [%d..%d]\n",
5045 g->text_string, right, g->minh, g->maxh); fflush(stderr);
5046 // stop();
5050 page_contents->words.move_right();
5051 if (page_contents->words.is_equal_to_head()) {
5052 g = 0;
5053 page_contents->words.start_from_tail();
5054 } else {
5055 g=page_contents->words.get_data();
5058 rewind_text_to(start);
5059 if (rightmost == -1) {
5060 return( right ); // no words in this column
5061 } else {
5062 return( rightmost );
5068 * column_calculate_left_margin - scan through the column and find the left most margin
5071 int html_printer::column_calculate_left_margin (int left, int right)
5073 if (left == right) {
5074 return( left );
5075 } else {
5076 int leftmost=right;
5077 text_glob *start = page_contents->words.get_data();
5078 text_glob *g = start;
5080 while ((g != 0) && (g->minv <= indentation.vertical_limit)) {
5081 if ((left <= g->minh) && (g->minh<right)) {
5082 leftmost = min(g->minh, leftmost);
5084 page_contents->words.move_right();
5085 if (page_contents->words.is_equal_to_head()) {
5086 g = 0;
5087 page_contents->words.start_from_tail();
5088 } else {
5089 g=page_contents->words.get_data();
5092 rewind_text_to(start);
5093 if (leftmost == right) {
5094 return( left ); // no words in this column
5095 } else {
5096 return( leftmost );
5102 * find_column_index - returns the index to the column in which glob, t, exists.
5105 int html_printer::find_column_index_in_line (text_glob *t, text_defn *line)
5107 int i=0;
5109 while ((line != 0) && ((line[i].left != 0) || (line[i].right != 0)) &&
5110 (! ((line[i].left<=t->minh) && (line[i].right>t->minh)))) {
5111 i++;
5113 return( i );
5117 * find_column_index - returns the index to the column in which glob, t, exists.
5120 int html_printer::find_column_index (text_glob *t)
5122 int i=0;
5124 while ((i<indentation.no_of_columns) &&
5125 (! ((indentation.columns[i].left<=t->minh) &&
5126 (indentation.columns[i].right>t->minh)))) {
5127 i++;
5129 return( i );
5133 * determine_row_limit - checks each row to see if there is a gap in a cell.
5134 * We return the vertical position after the empty cell
5135 * at the start of the next line.
5138 int html_printer::determine_row_limit (text_glob *start, int v)
5140 text_glob *t;
5141 int i;
5142 int vpos, last, prev;
5143 text_glob *is_gap[MAX_WORDS_PER_LINE];
5144 text_glob zero(&start->text_style, 0, 0, 0, 0, 0, 0, 0, 0);
5146 #if 1
5147 if ((v == -1) && (strcmp(start->text_string, "CASE") == 0)) {
5148 stop();
5150 #endif
5152 if (v >= indentation.vertical_limit) {
5153 return( v+1 );
5154 } else {
5156 * initially we start with all gaps in our table
5157 * after a gap we start a new row
5158 * here we set the gap array to the previous line
5161 if (v>=0) {
5162 t = page_contents->words.get_data();
5163 if (t->minv < v) {
5164 do {
5165 page_contents->words.move_right();
5166 t = page_contents->words.get_data();
5167 } while ((! page_contents->words.is_equal_to_head()) &&
5168 (t->minv <= v));
5171 if (page_contents->words.is_equal_to_head()) {
5172 t = &zero;
5173 } else {
5174 page_contents->words.move_left();
5175 t = page_contents->words.get_data();
5178 prev = t->minv;
5179 for (i=0; i<indentation.no_of_columns; i++) {
5180 is_gap[i] = t;
5183 if (page_contents->words.is_equal_to_tail()) {
5184 rewind_text_to(start);
5185 return( indentation.vertical_limit );
5186 } else {
5187 page_contents->words.move_right();
5189 t = page_contents->words.get_data();
5190 vpos = t->minv;
5192 // now check each row for a gap
5193 do {
5194 last = vpos;
5195 vpos = t->minv;
5196 if (vpos > indentation.vertical_limit) {
5197 // we have reached the end of the table, quit
5198 rewind_text_to(start);
5199 return( indentation.vertical_limit );
5202 i = find_column_index(t);
5203 if (i>=indentation.no_of_columns) {
5204 error("find_column_index has failed");
5205 stop();
5206 } else {
5207 if (! is_on_same_line(t, last)) {
5208 prev = last;
5211 if ((! is_on_same_line(is_gap[i], vpos)) && (! is_on_same_line(is_gap[i], prev)) &&
5212 (indentation.columns[i].is_used)) {
5213 // no word on previous line - must be a gap - force alignment of row
5214 rewind_text_to(start);
5215 return( prev );
5217 is_gap[i] = t;
5219 page_contents->words.move_right();
5220 t = page_contents->words.get_data();
5221 } while ((! page_contents->words.is_equal_to_head()) &&
5222 (vpos < indentation.vertical_limit) && (vpos >= last));
5223 page_contents->words.move_left();
5224 t = page_contents->words.get_data();
5225 rewind_text_to(start);
5226 return( indentation.vertical_limit );
5231 * assign_used_columns - sets the is_used field of the column array of records.
5234 void html_printer::assign_used_columns (text_glob *start)
5236 text_glob *t = start;
5237 int i;
5239 for (i=0; i<indentation.no_of_columns; i++) {
5240 indentation.columns[i].is_used = FALSE;
5243 rewind_text_to(start);
5244 if (! page_contents->words.is_empty()) {
5245 do {
5246 i = find_column_index(t);
5247 if (indentation.columns[i].left != 0) {
5248 if (debug_table_on) {
5249 fprintf(stderr, "[%s] in column %d at %d..%d limit %d\n", t->text_string,
5250 i, t->minv, t->maxv, indentation.vertical_limit); fflush(stderr);
5252 indentation.columns[i].is_used = TRUE;
5254 page_contents->words.move_right();
5255 t = page_contents->words.get_data();
5256 } while ((t->minv<indentation.vertical_limit) &&
5257 (! page_contents->words.is_equal_to_head()));
5259 if (debug_table_on) {
5260 for (i=0; i<indentation.no_of_columns; i++) {
5261 fprintf(stderr, " <left=%d right=%d is_used=%d> ",
5262 indentation.columns[i].left,
5263 indentation.columns[i].right,
5264 indentation.columns[i].is_used);
5266 fprintf(stderr, "\n");
5267 fflush(stderr);
5272 * adjust_margin_percentages - so far we have ignored the header_indent
5273 * and just considered left_margin_indent..right_margin_indent.
5274 * (We do this since we can assume 100% is total width for main text).
5275 * However as header_indent can be < left_margin_indent we need to
5276 * recalculate the real percentages in the light of the extended width.
5279 void html_printer::adjust_margin_percentages (void)
5281 if ((header_indent < left_margin_indent) && (header_indent != -1)) {
5283 * recalculation necessary
5285 int i=0;
5287 while (i<indentation.no_of_columns) {
5288 indentation.columns[i].percent = (indentation.columns[i].percent *
5289 (right_margin_indent - left_margin_indent)) /
5290 (right_margin_indent - header_indent);
5291 i++;
5293 // remove_zero_percentage_column();
5298 * foreach_column_include_text - foreach column in a table place the
5299 * appropriate html text.
5302 void html_printer::foreach_column_include_text (text_glob *start)
5304 if (indentation.no_of_columns>0) {
5305 int i;
5306 int left, right;
5307 int limit=-1;
5309 start_table();
5310 rewind_text_to(start);
5311 count_right_hits(indentation.columns, indentation.no_of_columns);
5312 rewind_text_to(start);
5314 do {
5315 limit = determine_row_limit(start, limit); // find the bottom of the next row
5316 html.put_string("<tr valign=\"top\" align=\"left\">\n");
5317 i=0;
5318 start = page_contents->words.get_data();
5319 while (i<indentation.no_of_columns) {
5320 // reset the output position to the start of column
5321 rewind_text_to(start);
5322 output_vpos = start->minv;
5323 output_hpos = indentation.columns[i].left;
5324 // and display each column until limit
5325 right = column_calculate_right_margin(indentation.columns[i].left,
5326 indentation.columns[i].right);
5327 left = column_calculate_left_margin(indentation.columns[i].left,
5328 indentation.columns[i].right);
5330 if (right>indentation.columns[i].right) {
5331 if (debug_on) {
5332 fprintf(stderr, "assert calculated right column edge is greater than column\n"); fflush(stderr);
5333 // stop();
5337 if (left<indentation.columns[i].left) {
5338 if (debug_on) {
5339 fprintf(stderr, "assert calculated left column edge is less than column\n"); fflush(stderr);
5340 // stop();
5344 if ((indentation.columns[i].right_hits == 1) &&
5345 (indentation.columns[i].right != right_margin_indent)) {
5346 indentation.wrap_margin = FALSE;
5347 if (debug_on) {
5348 fprintf(stderr, "turning auto wrap off during column %d for start word %s\n",
5349 i, start->text_string);
5350 fflush(stderr);
5351 // stop();
5353 } else {
5354 indentation.wrap_margin = TRUE;
5357 column_display_word(i, limit, left, right, indentation.columns[i].right);
5358 i++;
5361 if (page_contents->words.is_equal_to_tail()) {
5362 start = 0;
5363 } else {
5364 page_contents->words.sub_move_right();
5365 if (page_contents->words.is_empty()) {
5366 start = 0;
5367 } else {
5368 start = page_contents->words.get_data();
5372 html.put_string("</tr>\n");
5373 } while (((limit < indentation.vertical_limit) && (start != 0) &&
5374 (! page_contents->words.is_empty())) || (limit == -1));
5375 end_table();
5377 if (start == 0) {
5378 // finished page remove all words
5379 page_contents->words.start_from_head();
5380 while (! page_contents->words.is_empty()) {
5381 page_contents->words.sub_move_right();
5383 } else if (! page_contents->words.is_empty()) {
5384 page_contents->words.move_left();
5390 * write_centered_line - generates a line of centered text.
5393 void html_printer::write_centered_line (text_glob *g)
5395 int current_vpos=g->minv;
5397 move_vertical(g, center_alignment);
5399 header.written_header = FALSE;
5400 supress_sub_sup = TRUE;
5401 output_vpos = g->minv;
5402 output_hpos = g->minh;
5403 do {
5404 char *postword=html_position_text(g, left_margin_indent, right_margin_indent);
5406 if (! header.written_header) {
5407 if (g->is_raw_command) {
5408 html.put_string((char *)g->text_string);
5409 } else {
5410 translate_to_html(g);
5412 if (postword != 0) {
5413 html.put_string(postword);
5415 need_one_newline = TRUE;
5416 issued_newline = FALSE;
5418 page_contents->words.move_right();
5419 g = page_contents->words.get_data();
5420 } while ((! page_contents->words.is_equal_to_head()) && (is_on_same_line(g, current_vpos)));
5421 page_contents->words.move_left(); // so when we move right we land on the word following this centered line
5422 need_one_newline = TRUE;
5426 * is_in_middle - returns TRUE if the text defn, t, is in the middle of the page.
5429 int html_printer::is_in_middle (int left, int right)
5431 return( abs(abs(left-left_margin_indent) - abs(right_margin_indent-right)) <= CENTER_TOLERANCE );
5435 * single_centered_line - returns TRUE if first is a centered line with a different
5436 * margin to second.
5439 int html_printer::single_centered_line (text_defn *first, text_defn *second, text_glob *g)
5441 return(
5442 ((count_columns(first) == 1) && (first[0].left != left_margin_indent) &&
5443 (first[0].left != second[0].left) && is_in_middle(first->left, first->right))
5448 * check_able_to_use_center - returns TRUE if we can see a centered line.
5451 int html_printer::check_able_to_use_center (text_glob *g)
5453 if (auto_on && table_on && ((! is_on_same_line(g, output_vpos)) || issued_newline) && (! using_table_for_indent())) {
5454 // we are allowed to check for centered line
5455 // first check to see whether we might be looking at a set of columns
5456 struct text_defn last_guess[MAX_WORDS_PER_LINE];
5457 struct text_defn last_words[MAX_WORDS_PER_LINE];
5459 collect_columns(last_words, last_guess, 0, 0, MAX_WORDS_PER_LINE);
5461 rewind_text_to(g);
5462 if ((count_columns(last_guess) == 1) && (is_in_middle(last_guess[0].left, last_guess[0].right))) {
5463 write_centered_line(g);
5464 return( TRUE );
5467 return( FALSE );
5471 * check_able_to_use_table - examines forthcoming text to see whether we can
5472 * better format it by using an html transparent table.
5475 int html_printer::check_able_to_use_table (text_glob *g)
5477 if (auto_on && ((! is_on_same_line(g, output_vpos)) || issued_newline) && (! using_table_for_indent())) {
5478 // we are allowed to check for table
5480 if ((output_hpos != right_margin_indent) && (found_use_for_table(g))) {
5481 foreach_column_include_text(g);
5482 return( TRUE );
5485 return( FALSE );
5489 * move_vertical - if we are using html auto formatting then decide whether to
5490 * break the line via a <br> or a </p><p> sequence.
5493 void html_printer::move_vertical (text_glob *g, paragraph_type p)
5495 int r = font::res;
5496 int height = (g->text_style.point_size+2)*r/72; // --fixme-- we always assume VS is PS+2 (could do better)
5497 int temp_vpos;
5499 if (auto_on) {
5500 if ((more_than_line_break(output_vpos, g->minv, height)) || (p != current_paragraph->para_type)) {
5501 end_paragraph();
5502 begin_paragraph(p);
5503 } else {
5504 html_newline();
5506 } else {
5507 if (output_vpos == -1) {
5508 temp_vpos = g->minv;
5509 } else {
5510 temp_vpos = output_vpos;
5513 force_begin_paragraph();
5514 if (need_one_newline) {
5515 html_newline();
5516 temp_vpos += height;
5517 } else {
5518 need_one_newline = TRUE;
5521 while ((temp_vpos < g->minv) && (more_than_line_break(temp_vpos, g->minv, height))) {
5522 html_newline();
5523 temp_vpos += height;
5529 * emit_space - emits a space within html, it checks for the font type and
5530 * will change font depending upon, g. Courier spaces are larger
5531 * than roman so we need consistancy when changing between them.
5534 void html_printer::emit_space (text_glob *g, int force_space)
5536 if (! current_paragraph->need_paragraph) {
5537 // only generate a space if we have written a word - as html will ignore it otherwise
5538 if ((output_style != g->text_style) && (g->text_style.f != 0)) {
5539 terminate_current_font();
5541 if (force_space || (g->minh > output_hpos)) {
5542 html.put_string(" ");
5544 change_font(g, TRUE);
5549 * html_position_text - determine whether the text is subscript/superscript/normal
5550 * or a header.
5553 char *html_printer::html_position_text (text_glob *g, int left_margin, int right_margin)
5555 char *postword=0;
5557 begin_paragraph(left_alignment);
5559 if ((! header.written_header) &&
5560 (is_on_same_line(g, output_vpos) ||
5561 pretend_is_on_same_line(g, left_margin, right_margin))) {
5564 * check whether we should supress superscripts and subscripts.
5565 * I guess we might be able to do better by examining text on this line
5566 * --fixme--
5569 if ((! is_on_same_line(g, output_vpos)) && (pretend_is_on_same_line(g, left_margin, right_margin))) {
5570 supress_sub_sup = TRUE;
5572 header.written_header = FALSE;
5573 force_begin_paragraph();
5575 // check whether we need to insert white space between words on 'same' line
5576 if (pretend_is_on_same_line(g, left_margin, right_margin)) {
5577 emit_space(g, TRUE);
5580 // check whether the font was reset after generating an image
5581 if (output_style.f == 0) {
5582 change_font(g, TRUE);
5585 if (looks_like_subscript(g)) {
5587 g->text_style.point_size = output_style.point_size;
5588 g->minv = output_vpos; // this ensures that output_vpos doesn't alter
5589 // which allows multiple subscripted words
5590 move_horizontal(g, left_margin);
5591 html.put_string("<sub>");
5592 postword = "</sub>";
5593 } else if (looks_like_superscript(g)) {
5595 g->text_style.point_size = output_style.point_size;
5596 g->minv = output_vpos;
5598 move_horizontal(g, left_margin);
5599 html.put_string("<sup>");
5600 postword = "</sup>";
5601 } else {
5602 move_horizontal(g, left_margin);
5604 supress_sub_sup = FALSE;
5605 } else {
5606 // we have found a new line
5607 if (! header.written_header) {
5608 move_vertical(g, left_alignment);
5610 header.written_header = FALSE;
5612 if (processed_header(g)) {
5613 // we must not alter output_vpos as we have peeped at the next word
5614 // and set vpos to this - to ensure we do not generate a <br> after
5615 // a heading. (The html heading automatically generates a line break)
5616 output_hpos = left_margin;
5617 return( postword );
5618 } else {
5619 force_begin_paragraph();
5620 if (g->minh-left_margin != 0) {
5621 make_html_indent(g->minh-left_margin);
5623 change_font(g, TRUE);
5624 supress_sub_sup = FALSE;
5627 output_vpos = g->minv;
5628 output_hpos = g->maxh;
5629 return( postword );
5633 int html_printer::html_position_region (void)
5635 int r = font::res;
5636 int height = output_style.point_size*r/72;
5637 int temp_vpos;
5638 int is_center = FALSE;
5640 if (output_style.point_size != 0) {
5641 if (output_vpos != start_region_vpos) {
5643 // graphic starts on a different line
5644 if (output_vpos == -1) {
5645 temp_vpos = start_region_vpos;
5646 } else {
5647 temp_vpos = output_vpos;
5649 supress_sub_sup = TRUE;
5650 if (need_one_newline) {
5651 html_newline();
5652 temp_vpos += height;
5653 } else {
5654 need_one_newline = TRUE;
5657 while ((temp_vpos < start_region_vpos) &&
5658 (more_than_line_break(temp_vpos, start_region_vpos, height))) {
5659 html_newline();
5660 temp_vpos += height;
5664 if (auto_on && (is_in_middle(start_region_hpos, end_region_hpos))) {
5665 is_center = TRUE;
5666 } else {
5667 if (start_region_hpos > get_left()) {
5668 make_html_indent(start_region_hpos-get_left());
5671 output_vpos = start_region_vpos;
5672 output_hpos = start_region_hpos;
5673 return( is_center );
5677 * gs_x - translate and scale the x axis
5680 int html_printer::gs_x (int x)
5682 x += IMAGE_BOARDER_PIXELS/2;
5683 return((x-start_region_hpos)*postscript_res/font::res);
5688 * gs_y - translate and scale the y axis
5691 int html_printer::gs_y (int y)
5693 int yoffset=((int)(A4_PAGE_LENGTH*(double)font::res))-end_region_vpos;
5695 y += IMAGE_BOARDER_PIXELS/2;
5696 return( (y+yoffset)*postscript_res/font::res );
5700 void html_printer::troff_position_text (text_glob *g)
5702 change_font(g, FALSE);
5704 troff.put_string("V");
5705 troff.put_number(gs_y(g->maxv));
5706 troff.put_string("\n");
5708 troff.put_string("H");
5709 troff.put_number(gs_x(g->minh));
5710 troff.put_string("\n");
5713 void html_printer::troff_change_font (const char *fontname, int size, int font_no)
5715 troff.put_string("x font ");
5716 troff.put_number(font_no);
5717 troff.put_string(" ");
5718 troff.put_string(fontname);
5719 troff.put_string("\nf");
5720 troff.put_number(font_no);
5721 troff.put_string("\ns");
5722 troff.put_number(size*1000);
5723 troff.put_string("\n");
5727 void html_printer::set_style(const style &sty)
5729 #if 0
5730 const char *fontname = sty.f->get_name();
5731 if (fontname == 0)
5732 fatal("no internalname specified for font");
5734 change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
5735 #endif
5738 void html_printer::end_of_line()
5740 flush_sbuf();
5741 output_hpos = -1;
5744 void html_printer::html_display_word (text_glob *g)
5746 #if 0
5747 if (strcmp(g->text_string, "ot") == 0) {
5748 stop();
5750 #endif
5751 if (! check_able_to_use_table(g)) {
5752 char *postword=html_position_text(g, left_margin_indent, right_margin_indent);
5754 if (! header.written_header) {
5755 if (g->is_raw_command) {
5756 html.put_string((char *)g->text_string);
5757 } else {
5758 translate_to_html(g);
5760 if (postword != 0) {
5761 html.put_string(postword);
5763 need_one_newline = TRUE;
5764 issued_newline = FALSE;
5769 void html_printer::troff_display_word (text_glob *g)
5771 troff_position_text(g);
5772 if (g->is_raw_command) {
5773 int l=strlen((char *)g->text_string);
5774 if (l == 1) {
5775 troff.put_string("c");
5776 troff.put_string((char *)g->text_string);
5777 troff.put_string("\n");
5778 } else if (l > 1) {
5779 troff.put_string("C");
5780 troff.put_troffps_char((char *)g->text_string);
5781 troff.put_string("\n");
5783 } else {
5784 troff_position_text(g);
5785 troff.put_string("t");
5786 troff.put_translated_string((const char *)g->text_string);
5787 troff.put_string("\n");
5791 void html_printer::display_word (text_glob *g, int is_to_html)
5793 if (is_to_html) {
5794 html_display_word(g);
5795 } else if ((g->is_raw_command) && (g->is_html_command)) {
5796 // found a raw html command inside a graphic glob.
5797 // We should emit the command to the html device, but of course we
5798 // cannot place it correctly as we are dealing with troff words.
5799 // Remember output_vpos will refer to troff and not html.
5800 html.put_string((char *)g->text_string);
5801 } else {
5802 troff_display_word(g);
5807 * translate_to_html - translates a textual string into html text
5810 void html_printer::translate_to_html (text_glob *g)
5812 char buf[MAX_STRING_LENGTH];
5814 str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH,
5815 g->text_string, g->text_length, TRUE);
5816 html.put_string(buf);
5820 * html_knows_about - given a character name, troff, return TRUE
5821 * if we know how to display this character using
5822 * html unicode.
5825 int html_printer::html_knows_about (char *troff)
5827 // --fixme-- needs to have similar code as above
5828 return( FALSE );
5832 * display_fill - generates a troff format fill command
5835 void html_printer::display_fill (graphic_glob *g)
5837 troff.put_string("Df ") ;
5838 troff.put_number(g->fill);
5839 troff.put_string(" 0\n");
5843 * display_line - displays a line using troff format
5846 void html_printer::display_line (graphic_glob *g, int is_to_html)
5848 if (is_to_html) {
5849 fatal("cannot emit lines in html");
5851 if (g->code == 'l') {
5852 // straight line
5854 troff.put_string("V");
5855 troff.put_number(gs_y(g->point[0].y));
5856 troff.put_string("\n");
5858 troff.put_string("H");
5859 troff.put_number(gs_x(g->point[0].x));
5860 troff.put_string("\n");
5862 display_fill(g);
5864 troff.put_string("Dl ");
5865 troff.put_number((g->point[1].x-g->point[0].x)*postscript_res/font::res);
5866 troff.put_string(" ");
5867 troff.put_number((g->point[1].y-g->point[0].y)*postscript_res/font::res);
5868 troff.put_string("\n");
5869 // printf("line %c %d %d %d %d size %d\n", (char)g->code, g->point[0].x, g->point[0].y,
5870 // g->point[1].x, g->point[1].y, g->size);
5871 } else if ((g->code == 'c') || (g->code == 'C')) {
5872 // circle
5874 int xradius = (g->maxh - g->minh) / 2;
5875 int yradius = (g->maxv - g->minv) / 2;
5876 // center of circle or elipse
5878 troff.put_string("V");
5879 troff.put_number(gs_y(g->minv+yradius));
5880 troff.put_string("\n");
5882 troff.put_string("H");
5883 troff.put_number(gs_x(g->minh));
5884 troff.put_string("\n");
5886 display_fill(g);
5888 if (g->code == 'c') {
5889 troff.put_string("Dc ");
5890 } else {
5891 troff.put_string("DC ");
5894 troff.put_number(xradius*2*postscript_res/font::res);
5895 troff.put_string("\n");
5897 } else if ((g->code == 'e') || (g->code == 'E')) {
5898 // ellipse
5900 int xradius = (g->maxh - g->minh) / 2;
5901 int yradius = (g->maxv - g->minv) / 2;
5902 // center of elipse - this is untested
5904 troff.put_string("V");
5905 troff.put_number(gs_y(g->minv+yradius));
5906 troff.put_string("\n");
5908 troff.put_string("H");
5909 troff.put_number(gs_x(g->minh));
5910 troff.put_string("\n");
5912 display_fill(g);
5914 if (g->code == 'e') {
5915 troff.put_string("De ");
5916 } else {
5917 troff.put_string("DE ");
5920 troff.put_number(xradius*2*postscript_res/font::res);
5921 troff.put_string(" ");
5922 troff.put_number(yradius*2*postscript_res/font::res);
5923 troff.put_string("\n");
5924 } else if ((g->code == 'p') || (g->code == 'P')) {
5925 // polygon
5926 troff.put_string("V");
5927 troff.put_number(gs_y(g->yc));
5928 troff.put_string("\n");
5930 troff.put_string("H");
5931 troff.put_number(gs_x(g->xc));
5932 troff.put_string("\n");
5934 display_fill(g);
5936 if (g->code == 'p') {
5937 troff.put_string("Dp");
5938 } else {
5939 troff.put_string("DP");
5942 int i;
5943 int xc=g->xc;
5944 int yc=g->yc;
5945 for (i=0; i<g->nopoints; i++) {
5946 troff.put_string(" ");
5947 troff.put_number((g->point[i].x-xc)*postscript_res/font::res);
5948 troff.put_string(" ");
5949 troff.put_number((g->point[i].y-yc)*postscript_res/font::res);
5950 xc = g->point[i].x;
5951 yc = g->point[i].y;
5953 troff.put_string("\n");
5954 } else if (g->code == 'a') {
5955 // arc
5956 troff.put_string("V");
5957 troff.put_number(gs_y(g->yc));
5958 troff.put_string("\n");
5960 troff.put_string("H");
5961 troff.put_number(gs_x(g->xc));
5962 troff.put_string("\n");
5964 display_fill(g);
5966 troff.put_string("Da");
5968 int i;
5970 for (i=0; i<g->nopoints; i++) {
5971 troff.put_string(" ");
5972 troff.put_number(g->point[i].x*postscript_res/font::res);
5973 troff.put_string(" ");
5974 troff.put_number(g->point[i].y*postscript_res/font::res);
5976 troff.put_string("\n");
5977 } else if (g->code == '~') {
5978 // spline
5979 troff.put_string("V");
5980 troff.put_number(gs_y(g->yc));
5981 troff.put_string("\n");
5983 troff.put_string("H");
5984 troff.put_number(gs_x(g->xc));
5985 troff.put_string("\n");
5987 display_fill(g);
5989 troff.put_string("D~");
5991 int i;
5992 int xc=g->xc;
5993 int yc=g->yc;
5994 for (i=0; i<g->nopoints; i++) {
5995 troff.put_string(" ");
5996 troff.put_number((g->point[i].x-xc)*postscript_res/font::res);
5997 troff.put_string(" ");
5998 troff.put_number((g->point[i].y-yc)*postscript_res/font::res);
5999 xc = g->point[i].x;
6000 yc = g->point[i].y;
6002 troff.put_string("\n");
6008 * flush_sbuf - flushes the current sbuf into the list of glyphs.
6011 void html_printer::flush_sbuf()
6013 if (sbuf_len > 0) {
6014 int r=font::res; // resolution of the device
6015 set_style(sbuf_style);
6017 page_contents->add(&sbuf_style, sbuf, sbuf_len,
6018 sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
6019 sbuf_vpos , sbuf_end_hpos);
6021 output_hpos = sbuf_end_hpos;
6022 output_vpos = sbuf_vpos;
6023 sbuf_len = 0;
6024 sbuf_dmark_hpos = -1;
6029 void html_printer::set_line_thickness(const environment *env)
6031 line_thickness = env->size;
6032 printf("line thickness = %d\n", line_thickness);
6035 void html_printer::draw(int code, int *p, int np, const environment *env)
6037 switch (code) {
6039 case 'l':
6040 if (np == 2) {
6041 page_contents->add_line(code,
6042 env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1],
6043 env->size, fill);
6044 } else {
6045 error("2 arguments required for line");
6047 break;
6048 case 't':
6050 if (np == 0) {
6051 line_thickness = -1;
6052 } else {
6053 // troff gratuitously adds an extra 0
6054 if (np != 1 && np != 2) {
6055 error("0 or 1 argument required for thickness");
6056 break;
6058 line_thickness = p[0];
6060 break;
6063 case 'P':
6064 // fall through
6065 case 'p':
6067 if (np & 1) {
6068 error("even number of arguments required for polygon");
6069 break;
6071 if (np == 0) {
6072 error("no arguments for polygon");
6073 break;
6075 // firstly lets add our current position to polygon
6076 int oh=env->hpos;
6077 int ov=env->vpos;
6078 int i=0;
6080 while (i<np) {
6081 p[i+0] += oh;
6082 p[i+1] += ov;
6083 oh = p[i+0];
6084 ov = p[i+1];
6085 i += 2;
6087 // now store polygon in page
6088 page_contents->add_polygon(code, np, p, env->hpos, env->vpos, env->size, fill);
6090 break;
6091 case 'E':
6092 // fall through
6093 case 'e':
6094 if (np != 2) {
6095 error("2 arguments required for ellipse");
6096 break;
6098 page_contents->add_line(code,
6099 env->hpos, env->vpos-p[1]/2, env->hpos+p[0], env->vpos+p[1]/2,
6100 env->size, fill);
6102 break;
6103 case 'C':
6104 // fill circle
6106 case 'c':
6108 // troff adds an extra argument to C
6109 if (np != 1 && !(code == 'C' && np == 2)) {
6110 error("1 argument required for circle");
6111 break;
6113 page_contents->add_line(code,
6114 env->hpos, env->vpos-p[0]/2, env->hpos+p[0], env->vpos+p[0]/2,
6115 env->size, fill);
6117 break;
6118 case 'a':
6120 if (np == 4) {
6121 double c[2];
6123 if (adjust_arc_center(p, c)) {
6124 page_contents->add_arc('a', env->hpos, env->vpos, p, c, env->size, fill);
6125 } else {
6126 // a straignt line
6127 page_contents->add_line('l', env->hpos, env->vpos, p[0]+p[2], p[1]+p[3], env->size, fill);
6129 } else {
6130 error("4 arguments required for arc");
6133 break;
6134 case '~':
6136 if (np & 1) {
6137 error("even number of arguments required for spline");
6138 break;
6140 if (np == 0) {
6141 error("no arguments for spline");
6142 break;
6144 // firstly lets add our current position to spline
6145 int oh=env->hpos;
6146 int ov=env->vpos;
6147 int i=0;
6149 while (i<np) {
6150 p[i+0] += oh;
6151 p[i+1] += ov;
6152 oh = p[i+0];
6153 ov = p[i+1];
6154 i += 2;
6156 page_contents->add_spline('~', env->hpos, env->vpos, np, p, env->size, fill);
6158 break;
6159 case 'f':
6161 if (np != 1 && np != 2) {
6162 error("1 argument required for fill");
6163 break;
6165 fill = p[0];
6166 if (fill < 0 || fill > FILL_MAX) {
6167 // This means fill with the current color.
6168 fill = FILL_MAX + 1;
6170 break;
6173 default:
6174 error("unrecognised drawing command `%1'", char(code));
6175 break;
6180 void html_printer::begin_page(int n)
6182 page_number = n;
6183 html.begin_comment("Page: ").comment_arg(itoa(page_number)).end_comment();;
6184 no_of_printed_pages++;
6186 output_style.f = 0;
6187 output_space_code = 32;
6188 output_draw_point_size = -1;
6189 output_line_thickness = -1;
6190 output_hpos = -1;
6191 output_vpos = -1;
6194 void testing (text_glob *g) {}
6196 void html_printer::flush_graphic (void)
6198 graphic_glob g;
6200 graphic_level = 0;
6201 page_contents->is_in_graphic = FALSE;
6203 g.minv = -1;
6204 g.maxv = -1;
6205 calculate_region_range(&g);
6206 if (g.minv != -1) {
6207 page_contents->make_new_region(&g);
6209 move_region_to_page();
6212 void html_printer::end_page(int)
6214 flush_sbuf();
6215 flush_graphic();
6216 flush_page();
6219 font *html_printer::make_font(const char *nm)
6221 return html_font::load_html_font(nm);
6224 html_printer::~html_printer()
6226 if (fseek(tempfp, 0L, 0) < 0)
6227 fatal("fseek on temporary file failed");
6228 html.set_file(stdout);
6229 fputs("<html>\n", stdout);
6230 fputs("<head>\n", stdout);
6231 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
6232 write_title(TRUE);
6233 fputs("</head>\n", stdout);
6234 fputs("<body>\n", stdout);
6235 write_title(FALSE);
6236 header.write_headings(stdout);
6238 extern const char *Version_string;
6239 html.begin_comment("Creator : ")
6240 .comment_arg("groff ")
6241 .comment_arg("version ")
6242 .comment_arg(Version_string)
6243 .end_comment();
6246 #ifdef LONG_FOR_TIME_T
6247 long
6248 #else
6249 time_t
6250 #endif
6251 t = time(0);
6252 html.begin_comment("CreationDate: ")
6253 .comment_arg(ctime(&t))
6254 .end_comment();
6256 html.begin_comment("Total number of pages: ").comment_arg(itoa(no_of_printed_pages)).end_comment();
6257 html.end_line();
6258 html.copy_file(tempfp);
6259 fputs("</body>\n", stdout);
6260 fputs("</html>\n", stdout);
6261 fclose(tempfp);
6266 * calculate_region_range - calculates the vertical range for words and lines
6267 * within the region lists.
6270 void html_printer::calculate_region_range (graphic_glob *r)
6272 text_glob *w;
6273 graphic_glob *g;
6275 if (! page_contents->region_lines.is_empty()) {
6276 page_contents->region_lines.start_from_head();
6277 do {
6278 g = page_contents->region_lines.get_data();
6279 if ((r->minv == -1) || (g->minv < r->minv)) {
6280 r->minv = g->minv;
6282 if ((r->maxv == -1) || (g->maxv > r->maxv)) {
6283 r->maxv = g->maxv;
6285 page_contents->region_lines.move_right();
6286 } while (! page_contents->region_lines.is_equal_to_head());
6288 if (! page_contents->region_words.is_empty()) {
6289 page_contents->region_words.start_from_head();
6290 do {
6291 w = page_contents->region_words.get_data();
6293 if ((r->minv == -1) || (w->minv < r->minv)) {
6294 r->minv = w->minv;
6296 if ((r->maxv == -1) || (w->maxv > r->maxv)) {
6297 r->maxv = w->maxv;
6299 page_contents->region_words.move_right();
6300 } while (! page_contents->region_words.is_equal_to_head());
6306 * move_region_to_page - moves lines and words held in the temporary region
6307 * list to the page list.
6310 void html_printer::move_region_to_page (void)
6312 text_glob *w;
6313 graphic_glob *g;
6315 page_contents->region_lines.start_from_head();
6316 while (! page_contents->region_lines.is_empty()) {
6317 g = page_contents->region_lines.get_data(); // remove from our temporary region list
6318 page_contents->lines.add(g); // and add to the page list
6319 page_contents->region_lines.sub_move_right();
6321 page_contents->region_words.start_from_head();
6322 while (! page_contents->region_words.is_empty()) {
6323 w = page_contents->region_words.get_data(); // remove from our temporary region list
6324 page_contents->words.add(w); // and add to the page list
6325 page_contents->region_words.sub_move_right();
6330 * is_graphic_start - returns TRUE if the start of table, pic, eqn was seen.
6333 int is_graphic_start (char *s)
6335 return( (strcmp(s, "graphic-start") == 0) ||
6336 ((strcmp(s, "table-start") == 0) && (table_image_on)) );
6340 * is_graphic_end - return TRUE if the end of a table, pic, eqn was seen.
6343 int is_graphic_end (char *s)
6345 return( (strcmp(s, "graphic-end") == 0) ||
6346 ((strcmp(s, "table-end") == 0) && (table_image_on)) );
6350 * special - handle all x X requests from troff. For grohtml they allow users
6351 * to pass raw html commands, turn auto linked headings off/on and
6352 * also allow tbl, eqn & pic say what commands they have generated.
6355 void html_printer::special(char *s, const environment *env)
6357 if (s != 0) {
6358 if (is_graphic_start(s)) {
6359 graphic_level++;
6360 if (graphic_level == 1) {
6361 page_contents->is_in_graphic = TRUE; // add words and lines to temporary region lists
6363 } else if (is_graphic_end(s) && (graphic_level > 0)) {
6364 graphic_level--;
6365 if (graphic_level == 0) {
6366 flush_graphic();
6368 } else if (strncmp(s, "html:", 5) == 0) {
6369 int r=font::res; // resolution of the device
6370 char buf[MAX_STRING_LENGTH];
6371 font *f=sbuf_style.f;
6373 if (f == NULL) {
6374 int found=FALSE;
6376 f = font::load_font("TR", &found);
6378 str_translate_to_html(f, buf, MAX_STRING_LENGTH,
6379 &s[5], strlen(s)-5, FALSE);
6380 page_contents->add_html_command(&sbuf_style, buf, strlen(buf),
6382 // need to pass rest of string through to html output during flush
6384 env->vpos-env->size*r/72, env->hpos,
6385 env->vpos , env->hpos);
6386 // assume that the html command has no width, if it does then we hopefully troff
6387 // will have fudged this in a macro and requested that the formatting move right by
6388 // the appropriate width
6389 } else if (strncmp(s, "index:", 6) == 0) {
6390 cutoff_heading = atoi(&s[6]);
6395 void set_image_type (char *type)
6397 if (strcmp(type, "gif") == 0) {
6398 image_type = gif;
6399 } else if (strcmp(type, "png") == 0) {
6400 image_type = png;
6401 image_device = "png256";
6402 } else if (strncmp(type, "png", 3) == 0) {
6403 image_type = png;
6404 image_device = type;
6408 printer *make_printer()
6410 return new html_printer;
6413 static void usage();
6415 int main(int argc, char **argv)
6417 program_name = argv[0];
6418 static char stderr_buf[BUFSIZ];
6419 setbuf(stderr, stderr_buf);
6420 int c;
6421 while ((c = getopt(argc, argv, "F:atTvdgmx?I:r:")) != EOF)
6422 switch(c) {
6423 case 'v':
6425 extern const char *Version_string;
6426 fprintf(stderr, "grohtml version %s\n", Version_string);
6427 fflush(stderr);
6428 break;
6430 case 'a':
6431 auto_on = FALSE;
6432 break;
6433 case 't':
6434 table_on = FALSE;
6435 break;
6436 case 'T':
6437 table_image_on = FALSE;
6438 break;
6439 case 'F':
6440 font::command_line_font_dir(optarg);
6441 break;
6442 case 'I':
6443 // user specifying the type of images we should generate
6444 set_image_type(optarg);
6445 break;
6446 case 'r':
6447 // resolution (dots per inch for an image)
6448 image_res = atoi(optarg);
6449 break;
6450 case 'd':
6451 // debugging on
6452 debug_on = TRUE;
6453 break;
6454 case 'x':
6455 debug_table_on = TRUE;
6456 break;
6457 case 'g':
6458 // do not guess title and headings
6459 guess_on = FALSE;
6460 break;
6461 case 'm':
6462 // leave margins alone
6463 margin_on = TRUE;
6464 break;
6465 case '?':
6466 usage();
6467 break;
6468 default:
6469 assert(0);
6471 if (optind >= argc) {
6472 do_file("-");
6473 } else {
6474 for (int i = optind; i < argc; i++)
6475 do_file(argv[i]);
6477 delete pr;
6478 return 0;
6481 static void usage()
6483 fprintf(stderr, "usage: %s [-avdgmt?] [-r resolution] [-F dir] [-I imagetype] [files ...]\n",
6484 program_name);
6485 exit(1);