* src/devices/grolbp/lbp.cc (set_papersizes): Add declaration of
[s-roff.git] / src / devices / grohtml / html.cc
blob8c033ad19f564ac4d7ebd73a3e1d9482339aeed5
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 extern "C" {
39 // SunOS 4.1.3 fails to declare this in stdlib.h
40 char *mktemp(char *);
43 #include <stdio.h>
44 #include <fcntl.h>
46 #include "ordered_list.h"
48 #if !defined(TRUE)
49 # define TRUE (1==1)
50 #endif
51 #if !defined(FALSE)
52 # define FALSE (1==0)
53 #endif
55 #define MAX_TEMP_NAME 1024
56 #define MAX_STRING_LENGTH 4096
57 #define MAX_CHAR_SIZE 50 // maximum length of character name
59 #define Y_FUDGE_MARGIN +0.83
60 #define A4_PAGE_LENGTH (11.6944-Y_FUDGE_MARGIN)
61 #define DEFAULT_IMAGE_RES 80
62 #define IMAGE_BOARDER_PIXELS 10
63 #define MAX_WORDS_PER_LINE 1000 // only used for table indentation
64 #define GAP_SPACES 3 // how many spaces needed to guess a gap?
65 #define GAP_WIDTH_ONE_LINE 2 // 1/GAP_WIDTH_ONE_LINE inches required for one line table
66 #define CENTER_TOLERANCE 2 // how many pixels off center will we think a line or region is centered
67 #define MIN_COLUMN 7 // minimum column size pixels for multiple lines
68 #define MIN_COLUMN_FOR_TWO_LINES 20 // minimum column size pixels for a 2 line table
69 #define MIN_TEXT_PERCENT 5 // try and round to this percentage value for used columns
70 #define PERCENT_THRESHOLD 20 // don't bother trying to increase and width greater than this
74 * Only uncomment one of the following to determine default image type.
77 #define IMAGE_DEFAULT_PNG
78 /* #define IMAGE_DEFAULT_GIF */
81 #if defined(IMAGE_DEFAULT_GIF)
82 static enum { gif, png } image_type = gif;
83 static char *image_device = "gif";
84 #elif defined(IMAGE_DEFAULT_PNG)
85 static enum { gif, png } image_type = png;
86 static char *image_device = "png256";
87 #else
88 # error "you must define either IMAGE_DEFAULT_GIF or IMAGE_DEFAULT_PNG"
89 #endif
91 static int debug_on = FALSE;
92 static int guess_on = TRUE;
93 static int margin_on = FALSE;
94 static int auto_on = TRUE;
95 static int table_on = TRUE;
96 static int image_res = DEFAULT_IMAGE_RES;
97 static int debug_table_on = FALSE;
98 static int table_image_on = TRUE; // default is to create images for tbl
100 static int linewidth = -1;
102 #define DEFAULT_LINEWIDTH 40 /* in ems/1000 */
103 #define MAX_LINE_LENGTH 72
104 #define FILL_MAX 1000
106 void stop () {}
110 * start with a few favorites
113 static int min (int a, int b)
115 if (a < b) {
116 return( a );
117 } else {
118 return( b );
122 static int max (int a, int b)
124 if (a > b) {
125 return( a );
126 } else {
127 return( b );
132 * is_subsection - returns TRUE if a1..a2 is within b1..b2
135 static int is_subsection (int a1, int a2, int b1, int b2)
137 // easier to see whether this is not the case
138 return( !((a1 < b1) || (a1 > b2) || (a2 < b1) || (a2 > b2)) );
142 * is_intersection - returns TRUE if range a1..a2 intersects with b1..b2
145 static int is_intersection (int a1, int a2, int b1, int b2)
147 // again easier to prove NOT outside limits
148 return( ! ((a1 > b2) || (a2 < b1)) );
152 * is_digit - returns TRUE if character, ch, is a digit.
155 static int is_digit (char ch)
157 return( (ch >= '0') && (ch <= '9') );
161 * more_than_line_break - returns TRUE should v1 and v2 differ by more than
162 * a simple line break.
165 static int more_than_line_break (int v1, int v2, int size)
167 return( abs(v1-v2)>size );
171 * the class and methods for styles
174 struct style {
175 font *f;
176 int point_size;
177 int font_no;
178 int height;
179 int slant;
180 style ();
181 style (font *, int, int, int, int);
182 int operator == (const style &) const;
183 int operator != (const style &) const;
186 style::style()
187 : f(0)
191 style::style(font *p, int sz, int h, int sl, int no)
192 : f(p), point_size(sz), font_no(no), height(h), slant(sl)
196 int style::operator==(const style &s) const
198 return (f == s.f && point_size == s.point_size
199 && height == s.height && slant == s.slant);
202 int style::operator!=(const style &s) const
204 return !(*this == s);
209 * the class and methods for retaining ascii text
212 struct char_block {
213 enum { SIZE = 256 };
214 char buffer[SIZE];
215 int used;
216 char_block *next;
218 char_block();
221 char_block::char_block()
222 : used(0), next(0)
226 class char_buffer {
227 public:
228 char_buffer();
229 ~char_buffer();
230 char *add_string(char *, unsigned int);
231 private:
232 char_block *head;
233 char_block *tail;
236 char_buffer::char_buffer()
237 : head(0), tail(0)
241 char_buffer::~char_buffer()
243 while (head != 0) {
244 char_block *temp = head;
245 head = head->next;
246 delete temp;
250 char *char_buffer::add_string (char *s, unsigned int length)
252 int i=0;
253 unsigned int old_used;
255 if (tail == 0) {
256 tail = new char_block;
257 head = tail;
258 } else {
259 if (tail->used + length+1 > char_block::SIZE) {
260 tail->next = new char_block;
261 tail = tail->next;
264 // at this point we have a tail which is ready for the string.
265 if (tail->used + length+1 > char_block::SIZE) {
266 fatal("need to increase char_block::SIZE");
269 old_used = tail->used;
270 do {
271 tail->buffer[tail->used] = s[i];
272 tail->used++;
273 i++;
274 length--;
275 } while (length>0);
277 // add terminating nul character
279 tail->buffer[tail->used] = '\0';
280 tail->used++;
282 // and return start of new string
284 return( &tail->buffer[old_used] );
288 * the classes and methods for maintaining pages and text positions and graphic regions
291 class text_glob {
292 public:
293 int is_less (text_glob *a, text_glob *b);
294 text_glob (style *s, char *string, unsigned int length,
295 int min_vertical, int min_horizontal,
296 int max_vertical, int max_horizontal, int is_command, int is_html);
297 text_glob (void);
298 ~text_glob (void);
300 style text_style;
301 char *text_string;
302 unsigned int text_length;
303 int minv, maxv, minh, maxh;
304 int is_raw_command; // should the text be sent directly to the device?
305 int is_html_command; // is the raw command definitely for the html device ie not an eqn?
308 text_glob::text_glob (style *s, char *string, unsigned int length,
309 int min_vertical, int min_horizontal,
310 int max_vertical, int max_horizontal, int is_command, int is_html)
311 : text_style(*s), text_string(string), text_length(length),
312 minv(min_vertical), maxv(max_vertical), minh(min_horizontal), maxh(max_horizontal),
313 is_raw_command(is_command), is_html_command(is_html)
317 text_glob::text_glob ()
318 : text_string(0), text_length(0), minv(-1), maxv(-1), minh(-1), maxh(-1),
319 is_raw_command(FALSE), is_html_command(FALSE)
323 text_glob::~text_glob ()
327 int text_glob::is_less (text_glob *a, text_glob *b)
329 if (is_intersection(a->minv+1, a->maxv-1, b->minv+1, b->maxv-1)) {
330 return( a->minh < b->minh );
331 } else {
332 return( a->maxv < b->maxv );
336 struct xycoord {
337 int x;
338 int y;
341 class graphic_glob {
342 public:
343 int is_less (graphic_glob *a, graphic_glob *b);
344 graphic_glob (int troff_code);
345 graphic_glob (void);
346 ~graphic_glob (void);
348 int minv, maxv, minh, maxh;
349 int xc, yc;
350 int nopoints; // number of points allocated in array below
351 struct xycoord *point;
352 int size;
353 int fill;
354 int code;
357 graphic_glob::graphic_glob ()
358 : minv(-1), maxv(-1), minh(-1), maxh(-1), nopoints(0), point(0), size(0), code(0)
362 graphic_glob::~graphic_glob ()
364 if (point != 0) {
365 free(point);
369 graphic_glob::graphic_glob (int troff_code)
370 : minv(-1), maxv(-1), minh(-1), maxh(-1), nopoints(0), point(0), size(0), code(troff_code)
374 int graphic_glob::is_less (graphic_glob *a, graphic_glob *b)
376 return( (a->minv < b->minv) || ((a->minv == b->minv) && (a->minh < b->minh)) );
379 class region_glob {
380 public:
381 region_glob (void);
382 ~region_glob (void);
383 int is_less (region_glob *a, region_glob *b);
385 int minv, maxv, minh, maxh;
388 int region_glob::is_less (region_glob *a, region_glob *b)
390 return( (a->minv < b->minv) || ((a->minv == b->minv) && (a->minh < b->minh)) );
393 region_glob::region_glob (void)
394 : minv(-1), maxv(-1), minh(-1), maxh(-1)
398 region_glob::~region_glob (void)
402 class page {
403 public:
404 page (void);
405 void add (style *s, char *string, unsigned int length,
406 int min_vertical, int min_horizontal,
407 int max_vertical, int max_horizontal);
408 void add_html_command (style *s, char *string, unsigned int length,
409 int min_vertical, int min_horizontal,
410 int max_vertical, int max_horizontal);
411 void add_special_char (style *s, char *string, unsigned int length,
412 int min_vertical, int min_horizontal,
413 int max_vertical, int max_horizontal);
414 void add_line (int code, int x1, int y1, int x2, int y2, int size, int fill);
415 void add_arc (int code, int xc, int yc, int *p, double *c, int size, int fill);
416 void add_polygon (int code, int np, int *p, int oh, int ov, int size, int fill);
417 void add_spline (int code, int xc, int yc, int np, int *p, int size, int fill);
418 void calculate_region (void);
419 int is_in_region (graphic_glob *g);
420 int can_grow_region (graphic_glob *g);
421 void make_new_region (graphic_glob *g);
422 int has_line (region_glob *r);
423 int has_word (region_glob *r);
424 int no_raw_commands (int minv, int maxv);
426 // and the data
428 ordered_list <region_glob> regions; // squares of bitmapped pics,eqn,tbl's
429 ordered_list <text_glob> words; // position of words on page
430 ordered_list <graphic_glob> lines; // position of lines on page
431 char_buffer buffer; // all characters for this page
432 int is_in_graphic; // should graphics and words go below or above
433 ordered_list <text_glob> region_words; // temporary accumulation of words in a region
434 ordered_list <graphic_glob> region_lines; // (as above) and used so that we can determine
435 // the regions vertical limits
438 page::page()
439 : is_in_graphic(FALSE)
443 void page::add (style *s, char *string, unsigned int length,
444 int min_vertical, int min_horizontal,
445 int max_vertical, int max_horizontal)
447 if (length > 0) {
448 text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
449 min_vertical, min_horizontal, max_vertical, max_horizontal, FALSE, FALSE);
450 if (is_in_graphic) {
451 region_words.add(g);
452 } else {
453 words.add(g);
459 * add_html_command - it only makes sense to add html commands when we are not inside
460 * a graphical entity.
463 void page::add_html_command (style *s, char *string, unsigned int length,
464 int min_vertical, int min_horizontal,
465 int max_vertical, int max_horizontal)
467 if ((length > 0) && (! is_in_graphic)) {
468 text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
469 min_vertical, min_horizontal, max_vertical, max_horizontal, TRUE, TRUE);
470 words.add(g);
475 * add_special_char - it only makes sense to add special characters when we are inside
476 * a graphical entity.
479 void page::add_special_char (style *s, char *string, unsigned int length,
480 int min_vertical, int min_horizontal,
481 int max_vertical, int max_horizontal)
483 if ((length > 0) && (is_in_graphic)) {
484 text_glob *g=new text_glob(s, buffer.add_string(string, length), length,
485 min_vertical, min_horizontal, max_vertical, max_horizontal, TRUE, FALSE);
486 region_words.add(g);
490 void page::add_line (int code, int x1, int y1, int x2, int y2, int size, int fill)
492 graphic_glob *g = new graphic_glob(code);
494 g->minh = min(x1, x2);
495 g->maxh = max(x1, x2);
496 g->minv = min(y1, y2);
497 g->maxv = max(y1, y2);
498 g->point = (struct xycoord *)malloc(sizeof(xycoord)*2);
499 g->nopoints = 2;
500 g->point[0].x = x1 ;
501 g->point[0].y = y1 ;
502 g->point[1].x = x2 ;
503 g->point[1].y = y2 ;
504 g->xc = 0;
505 g->yc = 0;
506 g->size = size;
507 g->fill = fill;
509 if (is_in_graphic) {
510 region_lines.add(g);
511 } else {
512 lines.add(g);
517 * assign_min_max_for_arc - works out the smallest box that will encompass an
518 * arc defined by: origin: g->xc, g->xc
519 * and vector (p[0], p[1]) and (p[2], p[3])
522 void assign_min_max_for_arc (graphic_glob *g, int *p, double *c)
524 int radius = (int) sqrt(c[0]*c[0]+c[1]*c[1]);
525 int xv1 = p[0];
526 int yv1 = p[1];
527 int xv2 = p[2];
528 int yv2 = p[3];
529 int x1 = g->xc+xv1;
530 int y1 = g->yc+yv1;
531 int x2 = g->xc+xv1+xv2;
532 int y2 = g->yc+yv1+yv2;
534 // firstly lets use the 'circle' limitation
535 g->minh = x1-radius;
536 g->maxh = x1+radius;
537 g->minv = y1-radius;
538 g->maxv = y1+radius;
540 // incidentally I'm sure there is a better way to do this, but I don't know it
541 // please can someone let me know or "improve" this function
543 // now see which min/max can be reduced and increased for the limits of the arc
546 // Q2 | Q1
547 // -----+-----
548 // Q3 | Q4
552 if ((xv1>=0) && (yv1>=0)) {
553 // first vector in Q3
554 if ((xv2>=0) && (yv2>=0)) {
555 // second in Q1
556 g->maxh = x2;
557 g->minv = y1;
558 } else if ((xv2<0) && (yv2>=0)) {
559 // second in Q2
560 g->maxh = x2;
561 g->minv = y1;
562 } else if ((xv2>=0) && (yv2<0)) {
563 // second in Q4
564 g->minv = min(y1, y2);
565 } else if ((xv2<0) && (yv2<0)) {
566 // second in Q3
567 if (x1>=x2) {
568 g->minh = x2;
569 g->maxh = x1;
570 g->minv = min(y1, y2);
571 g->maxv = max(y1, y2);
572 } else {
573 // xv2, yv2 could all be zero?
576 } else if ((xv1>=0) && (yv1<0)) {
577 // first vector in Q2
578 if ((xv2>=0) && (yv2>=0)) {
579 // second in Q1
580 g->maxh = max(x1, x2);
581 g->minh = min(x1, x2);
582 g->minv = y1;
583 } else if ((xv2<0) && (yv2>=0)) {
584 // second in Q2
585 if (x1<x2) {
586 g->maxh = x2;
587 g->minh = x1;
588 g->minv = min(y1, y2);
589 g->maxv = max(y1, y2);
590 } else {
591 // otherwise almost full circle anyway
593 } else if ((xv2>=0) && (yv2<0)) {
594 // second in Q4
595 g->minv = y2;
596 g->minh = x1;
597 } else if ((xv2<0) && (yv2<0)) {
598 // second in Q3
599 g->minh = min(x1, x2);
601 } else if ((xv1<0) && (yv1<0)) {
602 // first vector in Q1
603 if ((xv2>=0) && (yv2>=0)) {
604 // second in Q1
605 if (x1<x2) {
606 g->minh = x1;
607 g->maxh = x2;
608 g->minv = min(y1, y2);
609 g->maxv = max(y1, y2);
610 } else {
611 // nearly full circle
613 } else if ((xv2<0) && (yv2>=0)) {
614 // second in Q2
615 g->maxv = max(y1, y2);
616 } else if ((xv2>=0) && (yv2<0)) {
617 // second in Q4
618 g->minv = min(y1, y2);
619 g->maxv = max(y1, y2);
620 g->minh = min(x1, x2);
621 } else if ((xv2<0) && (yv2<0)) {
622 // second in Q3
623 g->minh = x2;
624 g->maxv = y1;
626 } else if ((xv1<0) && (yv1>=0)) {
627 // first vector in Q4
628 if ((xv2>=0) && (yv2>=0)) {
629 // second in Q1
630 g->maxh = max(x1, x2);
631 } else if ((xv2<0) && (yv2>=0)) {
632 // second in Q2
633 g->maxv = max(y1, y2);
634 g->maxh = max(x1, x2);
635 } else if ((xv2>=0) && (yv2<0)) {
636 // second in Q4
637 if (x1>=x2) {
638 g->minv = min(y1, y2);
639 g->maxv = max(y1, y2);
640 g->minh = min(x1, x2);
641 g->maxh = max(x2, x2);
642 } else {
643 // nearly full circle
645 } else if ((xv2<0) && (yv2<0)) {
646 // second in Q3
647 g->maxv = max(y1, y2);
648 g->minh = min(x1, x2);
649 g->maxh = max(x1, x2);
652 // this should *never* happen but if it does it means a case above is wrong..
654 // this code is only present for safety sake
655 if (g->maxh < g->minh) {
656 if (debug_on) {
657 fprintf(stderr, "assert failed minh > maxh\n"); fflush(stderr);
658 // stop();
660 g->maxh = g->minh;
662 if (g->maxv < g->minv) {
663 if (debug_on) {
664 fprintf(stderr, "assert failed minv > maxv\n"); fflush(stderr);
665 // stop();
667 g->maxv = g->minv;
671 void page::add_arc (int code, int xc, int yc, int *p, double *c, int size, int fill)
673 graphic_glob *g = new graphic_glob(code);
675 g->point = (struct xycoord *)malloc(sizeof(xycoord)*2);
676 g->nopoints = 2;
677 g->point[0].x = p[0] ;
678 g->point[0].y = p[1] ;
679 g->point[1].x = p[2] ;
680 g->point[1].y = p[3] ;
681 g->xc = xc;
682 g->yc = yc;
683 g->size = size;
684 g->fill = fill;
686 assign_min_max_for_arc(g, p, c);
688 if (is_in_graphic) {
689 region_lines.add(g);
690 } else {
691 lines.add(g);
696 void page::add_polygon (int code, int np, int *p, int oh, int ov, int size, int fill)
698 graphic_glob *g = new graphic_glob(code);
699 int j = 0;
700 int i;
702 g->point = (struct xycoord *)malloc(sizeof(xycoord)*np/2);
703 g->nopoints = np/2;
705 for (i=0; i<g->nopoints; i++) {
706 g->point[i].x = p[j];
707 j++;
708 g->point[i].y = p[j];
709 j++;
711 // now calculate min/max
712 g->minh = g->point[0].x;
713 g->minv = g->point[0].y;
714 g->maxh = g->point[0].x;
715 g->maxv = g->point[0].y;
716 for (i=1; i<g->nopoints; i++) {
717 g->minh = min(g->minh, g->point[i].x);
718 g->minv = min(g->minv, g->point[i].y);
719 g->maxh = max(g->maxh, g->point[i].x);
720 g->maxv = max(g->maxv, g->point[i].y);
722 g->size = size;
723 g->xc = oh;
724 g->yc = ov;
725 g->fill = fill;
727 if (is_in_graphic) {
728 region_lines.add(g);
729 } else {
730 lines.add(g);
734 void page::add_spline (int code, int xc, int yc, int np, int *p, int size, int fill)
736 graphic_glob *g = new graphic_glob(code);
737 int j = 0;
738 int i;
740 g->point = (struct xycoord *)malloc(sizeof(xycoord)*np/2);
741 g->nopoints = np/2;
743 for (i=0; i<g->nopoints; i++) {
744 g->point[i].x = p[j];
745 j++;
746 g->point[i].y = p[j];
747 j++;
749 // now calculate min/max
750 g->minh = min(g->point[0].x, g->point[0].x/2);
751 g->minv = min(g->point[0].y, g->point[0].y/2);
752 g->maxh = max(g->point[0].x, g->point[0].x/2);
753 g->maxv = max(g->point[0].y, g->point[0].y/2);
755 /* tnum/tden should be between 0 and 1; the closer it is to 1
756 the tighter the curve will be to the guiding lines; 2/3
757 is the standard value */
758 const int tnum = 2;
759 const int tden = 3;
761 for (i=1; i<g->nopoints-1; i++) {
762 g->minh = min(g->minh, g->point[i].x*tnum/(2*tden));
763 g->minv = min(g->minv, g->point[i].y*tnum/(2*tden));
764 g->maxh = max(g->maxh, g->point[i].x*tnum/(2*tden));
765 g->maxv = max(g->maxv, g->point[i].y*tnum/(2*tden));
767 g->minh = min(g->minh, g->point[i].x/2+(g->point[i+1].x*(tden-tden))/(2*tden));
768 g->minv = min(g->minv, g->point[i].y/2+(g->point[i+1].y*(tden-tden))/(2*tden));
769 g->maxh = max(g->maxh, g->point[i].x/2+(g->point[i+1].x*(tden-tden))/(2*tden));
770 g->maxv = max(g->maxv, g->point[i].y/2+(g->point[i+1].y*(tden-tden))/(2*tden));
772 g->minh = min(g->minh, (g->point[i].x-g->point[i].x/2) + g->point[i+1].x/2);
773 g->minv = min(g->minv, (g->point[i].y-g->point[i].y/2) + g->point[i+1].y/2);
774 g->maxh = max(g->maxh, (g->point[i].x-g->point[i].x/2) + g->point[i+1].x/2);
775 g->maxv = max(g->maxv, (g->point[i].y-g->point[i].y/2) + g->point[i+1].y/2);
777 i = g->nopoints-1;
779 g->minh = min(g->minh, (g->point[i].x-g->point[i].x/2)) + xc;
780 g->minv = min(g->minv, (g->point[i].y-g->point[i].y/2)) + yc;
781 g->maxh = max(g->maxh, (g->point[i].x-g->point[i].x/2)) + xc;
782 g->maxv = max(g->maxv, (g->point[i].y-g->point[i].y/2)) + yc;
784 g->size = size;
785 g->xc = xc;
786 g->yc = yc;
787 g->fill = fill;
789 if (is_in_graphic) {
790 region_lines.add(g);
791 } else {
792 lines.add(g);
796 class html_font : public font {
797 html_font(const char *);
798 public:
799 int encoding_index;
800 char *encoding;
801 char *reencoded_name;
802 ~html_font();
803 static html_font *load_html_font(const char *);
806 html_font *html_font::load_html_font(const char *s)
808 html_font *f = new html_font(s);
809 if (!f->load()) {
810 delete f;
811 return 0;
813 return f;
816 html_font::html_font(const char *nm)
817 : font(nm)
821 html_font::~html_font()
826 * a simple class to contain the header to this document
829 class title_desc {
830 public:
831 title_desc ();
832 ~title_desc ();
834 int has_been_written;
835 int has_been_found;
836 char text[MAX_STRING_LENGTH];
840 title_desc::title_desc ()
841 : has_been_written(FALSE), has_been_found(FALSE)
845 title_desc::~title_desc ()
849 class header_desc {
850 public:
851 header_desc ();
852 ~header_desc ();
854 int no_of_headings; // how many headings have we found?
855 char_buffer headings; // all the headings used in the document
856 ordered_list <text_glob> headers;
857 int header_level; // current header level
858 int written_header; // have we written the header yet?
859 char header_buffer[MAX_STRING_LENGTH]; // current header text
861 void write_headings (FILE *f);
864 header_desc::header_desc ()
865 : no_of_headings(0), header_level(2), written_header(0)
869 header_desc::~header_desc ()
874 * paragraph_type - alignment for a new paragraph
877 typedef enum { left_alignment, center_alignment } paragraph_type;
880 * text_defn - defines the limit of text, initially these are stored in the
881 * column array as words. Later we examine the white space between
882 * the words in successive lines to find out whether we can detect
883 * distinct columns. The columns are generated via html tables.
886 struct text_defn {
887 int left; // the start of a word or text
888 int right; // the end of the text and beginning of white space
889 int is_used; // will this this column be used for words or space
890 int right_hits; // count of the number of words touching right position
891 int percent; // what percentage width should we use for this cell?
895 * introduce a paragraph class so that we can nest paragraphs
896 * from plain html text and html tables.
899 class html_paragraph {
900 public:
901 html_paragraph (int in, int need, paragraph_type type, html_paragraph *prev);
902 ~html_paragraph ();
904 int in_paragraph;
905 int need_paragraph;
906 paragraph_type para_type;
907 html_paragraph *previous;
911 * html_paragraph - constructor, fill in the public fields.
914 html_paragraph::html_paragraph (int in, int need, paragraph_type type, html_paragraph *prev)
915 : in_paragraph(in), need_paragraph(need),
916 para_type(type), previous(prev)
921 * html_paragraph - deconstructor
924 html_paragraph::~html_paragraph ()
929 * note that html_tables are currently only used to provide a better
930 * indentation mechanism for html text (in particular it allows grohtml
931 * to render .IP and .2C together with autoformatting).
934 class html_table {
935 public:
936 html_table ();
937 ~html_table ();
939 int no_of_columns; // how many columns are we using?
940 struct text_defn *columns; // left and right margins for each column
941 int vertical_limit; // the limit of the table
942 int wrap_margin; // is the current rightmost margin able to wrap words?
945 html_table::html_table ()
946 : no_of_columns(0), columns(0), vertical_limit(0), wrap_margin(0)
950 html_table::~html_table ()
954 class html_printer : public printer {
955 FILE *tempfp;
956 simple_output html;
957 simple_output troff;
958 int res;
959 int postscript_res;
960 int space_char_index;
961 int no_of_printed_pages;
962 int paper_length;
963 enum { SBUF_SIZE = 8192 };
964 char sbuf[SBUF_SIZE];
965 int sbuf_len;
966 int sbuf_start_hpos;
967 int sbuf_vpos;
968 int sbuf_end_hpos;
969 int sbuf_kern;
970 style sbuf_style;
971 int sbuf_dmark_hpos;
972 style output_style;
973 int output_hpos;
974 int output_vpos;
975 int output_draw_point_size;
976 int line_thickness;
977 int output_line_thickness;
978 int fill;
979 unsigned char output_space_code;
980 string defs;
981 char *inside_font_style;
982 int page_number;
983 title_desc title;
984 header_desc header;
985 int header_indent;
986 page *page_contents;
987 html_table indentation;
988 int left_margin_indent;
989 int right_margin_indent;
990 int need_one_newline;
991 int issued_newline;
992 html_paragraph *current_paragraph;
993 char image_name[MAX_STRING_LENGTH];
994 int image_number;
995 int graphic_level;
996 int supress_sub_sup;
998 int start_region_vpos;
999 int start_region_hpos;
1000 int end_region_vpos;
1001 int end_region_hpos;
1002 int cutoff_heading;
1004 struct graphic_glob *start_graphic;
1005 struct text_glob *start_text;
1007 void flush_sbuf ();
1008 void set_style (const style &);
1009 void set_space_code (unsigned char c);
1010 void do_exec (char *, const environment *);
1011 void do_import (char *, const environment *);
1012 void do_def (char *, const environment *);
1013 void do_mdef (char *, const environment *);
1014 void do_file (char *, const environment *);
1015 void set_line_thickness (const environment *);
1016 void change_font (text_glob *g, int is_to_html);
1017 void terminate_current_font (void);
1018 void flush_font (void);
1019 void flush_page (void);
1020 void add_char_to_sbuf (unsigned char code);
1021 void add_to_sbuf (char code, const char *name);
1022 void display_word (text_glob *g, int is_to_html);
1023 void html_display_word (text_glob *g);
1024 void troff_display_word (text_glob *g);
1025 void display_line (graphic_glob *g, int is_to_html);
1026 void display_fill (graphic_glob *g);
1027 void calculate_margin (void);
1028 void traverse_page_regions (void);
1029 void dump_page (void);
1030 int is_within_region (graphic_glob *g);
1031 int is_within_region (text_glob *t);
1032 int is_less (graphic_glob *g, text_glob *t);
1033 void display_globs (int is_to_html);
1034 void move_horizontal (text_glob *g, int left_margin);
1035 void move_vertical (text_glob *g, paragraph_type p);
1036 void write_html_font_face (const char *fontname, const char *left, const char *right);
1037 void write_html_font_type (const char *fontname, const char *left, const char *right);
1038 void html_change_font (text_glob *g, const char *fontname, int size);
1039 char *html_position_text (text_glob *g, int left_margin, int right_margin);
1040 int html_position_region (void);
1041 void troff_change_font (const char *fontname, int size, int font_no);
1042 void troff_position_text (text_glob *g);
1043 int pretend_is_on_same_line (text_glob *g, int left_margin, int right_margin);
1044 int is_on_same_line (text_glob *g, int vpos);
1045 int looks_like_subscript (text_glob *g);
1046 int looks_like_superscript (text_glob *g);
1047 int looks_like_smaller_font (text_glob *g);
1048 int looks_like_larger_font (text_glob *g);
1049 void begin_paragraph (paragraph_type p);
1050 void begin_paragraph_no_height (paragraph_type p);
1051 void force_begin_paragraph (void);
1052 void end_paragraph (void);
1053 void save_paragraph (void);
1054 void restore_paragraph (void);
1055 void html_newline (void);
1056 void convert_to_image (char *troff_src, char *image_name);
1057 void write_title (int in_head);
1058 void find_title (void);
1059 int is_bold (text_glob *g);
1060 void write_header (text_glob *g);
1061 void determine_header_level (void);
1062 void build_header (text_glob *g);
1063 void make_html_indent (int indent);
1064 int is_whole_line_bold (text_glob *g);
1065 int is_a_header (text_glob *g);
1066 int processed_header (text_glob *g);
1067 void make_new_image_name (void);
1068 void calculate_region_margins (region_glob *r);
1069 void remove_redundant_regions (void);
1070 void remove_duplicate_regions (void);
1071 void move_region_to_page (void);
1072 void calculate_region_range (graphic_glob *r);
1073 void flush_graphic (void);
1074 void write_string (graphic_glob *g, int is_to_html);
1075 void prologue (void);
1076 int gs_x (int x);
1077 int gs_y (int y);
1078 void display_regions (void);
1079 int check_able_to_use_table (text_glob *g);
1080 int using_table_for_indent (void);
1081 int collect_columns (struct text_defn *next_words, struct text_defn *next_cols,
1082 struct text_defn *last_words, struct text_defn *last_cols,
1083 int max_words);
1084 void include_into_list (struct text_defn *line, struct text_defn *item);
1085 int is_in_column (struct text_defn *line, struct text_defn *item, int max_words);
1086 int is_column_match (struct text_defn *match, struct text_defn *line1,
1087 struct text_defn *line2, int max_words);
1088 int count_columns (struct text_defn *line);
1089 void rewind_text_to (text_glob *g);
1090 int found_use_for_table (text_glob *start);
1091 void column_display_word (int cell, int vert, int left, int right, int next);
1092 void start_table (void);
1093 void end_table (void);
1094 void foreach_column_include_text (text_glob *start);
1095 void define_cell (int i);
1096 int column_calculate_left_margin (int left, int right);
1097 int column_calculate_right_margin (int left, int right);
1098 void display_columns (const char *word, const char *name, text_defn *line);
1099 void calculate_right (struct text_defn *line, int max_words);
1100 void determine_right_most_column (struct text_defn *line, int max_words);
1101 int remove_white_using_words (struct text_defn *next_guess, struct text_defn *last_guess, struct text_defn *next_line);
1102 void copy_line (struct text_defn *dest, struct text_defn *src);
1103 void combine_line (struct text_defn *dest, struct text_defn *src);
1104 int conflict_with_words (struct text_defn *column_guess, struct text_defn *words);
1105 void remove_entry_in_line (struct text_defn *line, int j);
1106 void remove_redundant_columns (struct text_defn *line);
1107 void add_column_gaps (struct text_defn *line);
1108 int continue_searching_column (text_defn *next_col, text_defn *last_col, text_defn *all_words);
1109 void add_right_full_width (struct text_defn *line, int mingap);
1110 int is_continueous_column (text_defn *last_col, text_defn *next_line);
1111 int is_exact_left (text_defn *last_col, text_defn *next_line);
1112 int find_column_index_in_line (text_glob *t, text_defn *line);
1113 void emit_space (text_glob *g, int force_space);
1114 int is_in_middle (int left, int right);
1115 int check_able_to_use_center (text_glob *g);
1116 void write_centered_line (text_glob *g);
1117 int single_centered_line (text_defn *first, text_defn *second, text_glob *g);
1118 int determine_row_limit (text_glob *start, int v);
1119 void assign_used_columns (text_glob *start);
1120 int find_column_index (text_glob *t);
1121 int large_enough_gap (text_defn *last_col);
1122 int is_worth_column (int left, int right);
1123 int is_subset_of_columns (text_defn *a, text_defn *b);
1124 void count_hits (text_defn *col, int no_of_columns, int limit);
1125 void count_right_hits (text_defn *col, int no_of_columns);
1126 int calculate_min_gap (text_glob *g);
1127 int right_indentation (struct text_defn *last_guess);
1128 void calculate_percentage_width (text_glob *start);
1129 int able_to_steal_width (void);
1130 int need_to_steal_width (void);
1131 int can_distribute_fairly (void);
1132 void utilize_round_off (void);
1133 int will_wrap_text (int i, text_glob *start);
1134 int next_line_on_left_column (int i, text_glob *start);
1135 void remove_table_column (int i);
1136 void remove_unnecessary_unused (text_glob *start);
1137 int is_small_table (int lines, struct text_defn *last_guess,
1138 struct text_defn *words_1, struct text_defn *cols_1,
1139 struct text_defn *words_2, struct text_defn *cols_2,
1140 int *limit, int *limit_1);
1141 int is_column_subset (struct text_defn *cols_1, struct text_defn *cols_2);
1142 int is_appropriate_to_start_table (struct text_defn *cols_1, struct text_defn *cols_2,
1143 struct text_defn *last_guess);
1144 int is_a_full_width_column (void);
1145 int right_most_column (struct text_defn *col);
1146 int large_enough_gap_for_two (struct text_defn *col);
1147 void remove_zero_percentage_column (void);
1148 void translate_to_html (text_glob *g);
1149 int html_knows_about (char *troff);
1150 void determine_diacritical_mark (const char *name, const environment *env);
1151 int sbuf_continuation (unsigned char code, const char *name, const environment *env, int w);
1152 char *remove_last_char_from_sbuf ();
1153 const char *check_diacritical_combination (unsigned char code, const char *name);
1154 int seen_backwards_escape (char *s, int l);
1155 int should_defer_table (int lines, struct text_glob *start, struct text_defn *cols_1);
1156 int is_new_exact_right (struct text_defn *last_guess, struct text_defn *last_cols, struct text_defn *next_cols);
1157 void issue_left_paragraph (void);
1158 void adjust_margin_percentages (void);
1159 int total_percentages (void);
1160 int get_left (void);
1161 void can_loose_column (text_glob *start, struct text_defn *last_guess, int limit);
1162 int check_lack_of_hits (struct text_defn *next_guess, struct text_defn *last_guess, text_glob *start, int limit);
1163 int is_in_table (void);
1165 // ADD HERE
1167 public:
1168 html_printer();
1169 ~html_printer();
1170 void set_char(int i, font *f, const environment *env, int w, const char *name);
1171 void draw(int code, int *p, int np, const environment *env);
1172 void begin_page(int);
1173 void end_page(int);
1174 void special(char *arg, const environment *env);
1175 font *make_font(const char *);
1176 void end_of_line();
1179 html_printer::html_printer()
1180 : html(0, MAX_LINE_LENGTH),
1181 troff(0, MAX_LINE_LENGTH),
1182 no_of_printed_pages(0),
1183 sbuf_len(0),
1184 sbuf_dmark_hpos(-1),
1185 output_hpos(-1),
1186 output_vpos(-1),
1187 line_thickness(-1),
1188 fill(FILL_MAX + 1),
1189 inside_font_style(0),
1190 page_number(0),
1191 header_indent(-1),
1192 left_margin_indent(0),
1193 right_margin_indent(0),
1194 need_one_newline(0),
1195 issued_newline(0),
1196 image_number(0),
1197 graphic_level(0),
1198 supress_sub_sup(TRUE),
1199 start_region_vpos(0),
1200 start_region_hpos(0),
1201 end_region_vpos(0),
1202 end_region_hpos(0),
1203 cutoff_heading(100)
1205 tempfp = xtmpfile();
1206 html.set_file(tempfp);
1207 if (linewidth < 0)
1208 linewidth = DEFAULT_LINEWIDTH;
1209 if (font::hor != 1)
1210 fatal("horizontal resolution must be 1");
1211 if (font::vert != 1)
1212 fatal("vertical resolution must be 1");
1213 #if 0
1214 // should be sorted html..
1215 if (font::res % (font::sizescale*72) != 0)
1216 fatal("res must be a multiple of 72*sizescale");
1217 #endif
1218 int r = font::res;
1219 int point = 0;
1220 while (r % 10 == 0) {
1221 r /= 10;
1222 point++;
1224 res = r;
1225 html.set_fixed_point(point);
1226 space_char_index = font::name_to_index("space");
1227 paper_length = font::paperlength;
1228 if (paper_length == 0)
1229 paper_length = 11*font::res;
1230 page_contents = new page;
1232 postscript_res = 72000;
1233 current_paragraph = new html_paragraph(FALSE, FALSE, left_alignment, 0);
1237 * add_char_to_sbuf - adds a single character to the sbuf.
1240 void html_printer::add_char_to_sbuf (unsigned char code)
1242 if (sbuf_len < SBUF_SIZE) {
1243 sbuf[sbuf_len] = code;
1244 sbuf_len++;
1245 } else {
1246 fatal("need to increase SBUF_SIZE");
1251 * add_to_sbuf - adds character code or name to the sbuf.
1252 * It escapes \ with \\
1253 * We need to preserve the name of characters if they exist
1254 * because we may need to send this character to two different
1255 * devices: html and postscript.
1258 void html_printer::add_to_sbuf (char code, const char *name)
1260 if (name == 0) {
1261 if (code == '\\') {
1262 add_char_to_sbuf('\\');
1264 add_char_to_sbuf(code);
1265 } else {
1266 int l=strlen(name);
1267 int i=0;
1269 add_char_to_sbuf('\\');
1270 add_char_to_sbuf('(');
1271 while (i<l) {
1272 if (name[i] == '\\') {
1273 add_char_to_sbuf('\\');
1275 add_char_to_sbuf(name[i]);
1276 i++;
1278 add_char_to_sbuf('\\');
1279 add_char_to_sbuf(')');
1283 int html_printer::sbuf_continuation (unsigned char code, const char *name,
1284 const environment *env, int w)
1286 if ((sbuf_end_hpos == env->hpos) || (sbuf_dmark_hpos == env->hpos)) {
1287 name = check_diacritical_combination(code, name);
1288 add_to_sbuf(code, name);
1289 determine_diacritical_mark(name, env);
1290 sbuf_end_hpos += w + sbuf_kern;
1291 return( TRUE );
1292 } else {
1293 if ((sbuf_len < SBUF_SIZE-1) && (env->hpos >= sbuf_end_hpos) &&
1294 ((sbuf_kern == 0) || (sbuf_end_hpos - sbuf_kern != env->hpos))) {
1296 * lets see whether a space is needed or not
1298 int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size);
1300 if (env->hpos-sbuf_end_hpos < space_width) {
1301 name = check_diacritical_combination(code, name);
1302 add_to_sbuf(code, name);
1303 determine_diacritical_mark(name, env);
1304 sbuf_end_hpos = env->hpos + w;
1305 return( TRUE );
1307 } else if ((sbuf_len > 0) && (sbuf_dmark_hpos)) {
1309 * check whether the diacritical mark is on the same character
1311 int space_width = sbuf_style.f->get_space_width(sbuf_style.point_size);
1313 if (abs(sbuf_dmark_hpos-env->hpos) < space_width) {
1314 name = check_diacritical_combination(code, name);
1315 add_to_sbuf(code, name);
1316 determine_diacritical_mark(name, env);
1317 sbuf_end_hpos = env->hpos + w;
1318 return( TRUE );
1322 return( FALSE );
1326 * seen_backwards_escape - returns TRUE if we can see a escape at position i..l in s
1329 int html_printer::seen_backwards_escape (char *s, int l)
1332 * this is tricky so it is broken into components for clarity
1333 * (we let the compiler put in all back into a complex expression)
1335 if ((l>0) && (sbuf[l] == '(') && (sbuf[l-1] == '\\')) {
1337 * ok seen '\(' but we must now check for '\\('
1339 if ((l>1) && (sbuf[l-2] == '\\')) {
1341 * escaped the escape
1343 return( FALSE );
1344 } else {
1345 return( TRUE );
1347 } else {
1348 return( FALSE );
1353 * reverse - return reversed string.
1356 char *reverse (char *s)
1358 int i=0;
1359 int j=strlen(s)-1;
1360 char t;
1362 while (i<j) {
1363 t = s[i];
1364 s[i] = s[j];
1365 s[j] = t;
1366 i++;
1367 j--;
1369 return( s );
1373 * remove_last_char_from_sbuf - removes the last character from sbuf.
1376 char *html_printer::remove_last_char_from_sbuf ()
1378 int l=sbuf_len;
1379 static char last[MAX_STRING_LENGTH];
1381 if (l>0) {
1382 l--;
1383 if ((sbuf[l] == ')') && (l>0) && (sbuf[l-1] == '\\')) {
1385 * found terminating escape
1387 int i=0;
1389 l -= 2;
1390 while ((l>0) && (! seen_backwards_escape(sbuf, l))) {
1391 if (sbuf[l] == '\\') {
1392 if (sbuf[l-1] == '\\') {
1393 last[i] = sbuf[l];
1394 i++;
1395 l--;
1397 } else {
1398 last[i] = sbuf[l];
1399 i++;
1401 l--;
1403 last[i] = (char)0;
1404 sbuf_len = l;
1405 if (seen_backwards_escape(sbuf, l)) {
1406 sbuf_len--;
1408 return( reverse(last) );
1409 } else {
1410 if ((sbuf[l] == '\\') && (l>0) && (sbuf[l-1] == '\\')) {
1411 l -= 2;
1412 sbuf_len = l;
1413 return( "\\" );
1414 } else {
1415 sbuf_len--;
1416 last[0] = sbuf[sbuf_len];
1417 last[1] = (char)0;
1418 return( last );
1421 } else {
1422 return( NULL );
1427 * check_diacriticial_combination - checks to see whether the character code
1428 * if combined with the previous diacriticial mark
1429 * forms a new character.
1432 const char *html_printer::check_diacritical_combination (unsigned char code, const char *name)
1434 static char troff_char[2];
1436 if ((name == 0) && (sbuf_dmark_hpos >= 0)) {
1437 // last character was a diacritical mark
1438 char *last = remove_last_char_from_sbuf();
1440 int i=0;
1441 int j;
1443 while (diacritical_table[i].mark != NULL) {
1444 if (strcmp(diacritical_table[i].mark, last) == 0) {
1445 j=0;
1446 while ((diacritical_table[i].second_troff_char[j] != (char)0) &&
1447 (diacritical_table[i].second_troff_char[j] != code)) {
1448 j++;
1450 if (diacritical_table[i].second_troff_char[j] == code) {
1451 troff_char[0] = diacritical_table[i].translation;
1452 troff_char[1] = code;
1453 troff_char[2] = (char)0;
1454 return( troff_char );
1457 i++;
1459 add_to_sbuf(last[0], last);
1461 return( name );
1465 * determine_diacritical_mark - if name is a diacriticial mark the record the position.
1466 * --fixme-- is there a better way of doing this
1467 * this must be done in troff somewhere.
1470 void html_printer::determine_diacritical_mark (const char *name, const environment *env)
1472 if (name != 0) {
1473 int i=0;
1475 while (diacritical_table[i].mark != NULL) {
1476 if (strcmp(name, diacritical_table[i].mark) == 0) {
1477 sbuf_dmark_hpos = env->hpos;
1478 return;
1480 i++;
1483 sbuf_dmark_hpos = -1;
1487 * set_char - adds a character into the sbuf if it is a continuation with the previous
1488 * word otherwise flush the current sbuf and add character anew.
1491 void html_printer::set_char(int i, font *f, const environment *env, int w, const char *name)
1493 unsigned char code = f->get_code(i);
1495 #if 0
1496 if (code == ' ') {
1497 stop();
1499 #endif
1500 style sty(f, env->size, env->height, env->slant, env->fontno);
1501 if (sty.slant != 0) {
1502 if (sty.slant > 80 || sty.slant < -80) {
1503 error("silly slant `%1' degrees", sty.slant);
1504 sty.slant = 0;
1507 if ((name != 0) && (page_contents->is_in_graphic)) {
1508 flush_sbuf();
1509 int r=font::res; // resolution of the device
1510 page_contents->add_special_char(&sty, (char *)name, strlen(name),
1511 env->vpos-sty.point_size*r/72, env->hpos,
1512 env->vpos , env->hpos+w);
1513 sbuf_end_hpos = env->hpos + w;
1514 sbuf_start_hpos = env->hpos;
1515 sbuf_vpos = env->vpos;
1516 sbuf_style = sty;
1517 sbuf_kern = 0;
1518 } else {
1519 if ((sbuf_len > 0) && (sbuf_len < SBUF_SIZE) && (sty == sbuf_style) &&
1520 (sbuf_vpos == env->vpos) && (sbuf_continuation(code, name, env, w))) {
1521 return;
1522 } else {
1523 flush_sbuf();
1524 sbuf_len = 0;
1525 add_to_sbuf(code, name);
1526 determine_diacritical_mark(name, env);
1527 sbuf_end_hpos = env->hpos + w;
1528 sbuf_start_hpos = env->hpos;
1529 sbuf_vpos = env->vpos;
1530 sbuf_style = sty;
1531 sbuf_kern = 0;
1537 * make_new_image_name - creates a new file name ready for a image file.
1540 void html_printer::make_new_image_name (void)
1542 image_number++;
1544 if ((current_filename == 0) ||
1545 (strcmp(current_filename, "<standard input>") == 0) ||
1546 (strcmp(current_filename, "-") == 0) ||
1547 (strchr(current_filename, '/') != 0)) {
1548 sprintf(image_name, "grohtml-%d-%ld", image_number, (long)getpid());
1549 } else {
1550 sprintf(image_name, "%s-%d-%ld", current_filename, image_number, (long)getpid());
1555 * write_title - writes the title to this document
1558 void html_printer::write_title (int in_head)
1560 if (title.has_been_found) {
1561 if (in_head) {
1562 html.put_string("<title>");
1563 html.put_string(title.text);
1564 html.put_string("</title>\n");
1565 } else {
1566 title.has_been_written = TRUE;
1567 html.put_string("<h1 align=center>");
1568 html.put_string(title.text);
1569 html.put_string("</h1>\n");
1575 * get_html_translation - given the position of the character and its name
1576 * return the device encoding for such character.
1579 char *get_html_translation (font *f, char *name)
1581 int index;
1583 if ((f == 0) || (name == 0) || (strcmp(name, "") == 0)) {
1584 return( NULL );
1585 } else {
1586 index = f->name_to_index(name);
1587 if (index == 0) {
1588 error("character `%s' not found", name);
1589 return( NULL );
1590 } else {
1591 return( (char *)f->get_special_device_encoding(index) );
1597 * str_translate_to_html - converts a string, str, into html text. It places
1598 * the output input buffer, buf. It truncates string, str, if
1599 * there is not enough space in buf.
1600 * It looks up the html character encoding of single characters
1601 * if, and_single, is TRUE. Characters such as < > & etc.
1604 void str_translate_to_html (font *f, char *buf, int buflen, char *str, int len, int and_single)
1606 int l;
1607 char *translation;
1608 int e;
1609 char escaped_char[MAX_STRING_LENGTH];
1610 int i=0;
1611 int b=0;
1612 int t=0;
1614 #if 0
1615 if (strcmp(str, "\\(\\\\-\\)") == 0) {
1616 stop();
1618 #endif
1619 while (str[i] != (char)0) {
1620 if ((str[i]=='\\') && (i+1<len)) {
1621 i++; // skip the leading backslash
1622 if (str[i] == '(') {
1623 // start of escape
1624 i++;
1625 e = 0;
1626 while ((str[i] != (char)0) &&
1627 (! ((str[i] == '\\') && (i+1<len) && (str[i+1] == ')')))) {
1628 if (str[i] == '\\') {
1629 i++;
1631 escaped_char[e] = str[i];
1632 e++;
1633 i++;
1635 if ((str[i] == '\\') && (i+1<len) && (str[i+1] == ')')) {
1636 i += 2;
1638 escaped_char[e] = (char)0;
1639 if (e > 0) {
1640 translation = get_html_translation(f, escaped_char);
1641 if (translation) {
1642 l = strlen(translation);
1643 t = max(0, min(l, buflen-b));
1644 strncpy(&buf[b], translation, t);
1645 b += t;
1646 } else {
1647 int index=f->name_to_index(escaped_char);
1649 if (index != 0) {
1650 buf[b] = f->get_code(index);
1651 b++;
1656 } else {
1657 if (and_single) {
1658 char name[2];
1660 name[0] = str[i];
1661 name[1] = (char)0;
1662 translation = get_html_translation(f, name);
1663 if (translation) {
1664 l = strlen(translation);
1665 t = max(0, min(l, buflen-b));
1666 strncpy(&buf[b], translation, t);
1667 b += t;
1668 } else {
1669 if (b<buflen) {
1670 buf[b] = str[i];
1671 b++;
1674 } else {
1676 * do not attempt to encode single characters
1678 if (b<buflen) {
1679 buf[b] = str[i];
1680 b++;
1683 i++;
1686 buf[min(b, buflen)] = (char)0;
1690 * find_title - finds a title to this document, if it exists.
1693 void html_printer::find_title (void)
1695 text_glob *t;
1696 int r=font::res;
1697 int removed_from_head;
1698 char buf[MAX_STRING_LENGTH];
1700 if ((page_number == 1) && (guess_on)) {
1701 if (! page_contents->words.is_empty()) {
1703 int end_title_hpos = 0;
1704 int start_title_vpos = 0;
1705 int found_title_start = FALSE;
1706 int height = 0;
1707 int start_region =-1;
1709 if (! page_contents->regions.is_empty()) {
1710 region_glob *r;
1712 page_contents->regions.start_from_head();
1713 r = page_contents->regions.get_data();
1714 if (r->minv > 0) {
1715 start_region = r->minv;
1719 page_contents->words.start_from_head();
1720 do {
1721 t = page_contents->words.get_data();
1722 removed_from_head = FALSE;
1723 if ((found_title_start) && (start_region != -1) && (t->maxv >= start_region)) {
1725 * we have just encountered the first graphic region so
1726 * we stop looking for a title.
1728 title.has_been_found = TRUE;
1729 return;
1730 } else if (t->is_raw_command) {
1731 // skip raw commands
1732 page_contents->words.move_right(); // move onto next word
1733 } else if ((!found_title_start) && (t->minh > left_margin_indent) &&
1734 ((start_region == -1) || (t->maxv < start_region))) {
1735 start_title_vpos = t->minv;
1736 end_title_hpos = t->minh;
1737 str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE);
1738 strcpy((char *)title.text, buf);
1739 height = t->text_style.point_size*r/72;
1740 found_title_start = TRUE;
1741 page_contents->words.sub_move_right();
1742 removed_from_head = ((!page_contents->words.is_empty()) &&
1743 (page_contents->words.is_equal_to_head()));
1744 } else if (found_title_start) {
1745 if ((t->minv == start_title_vpos) ||
1746 ((!more_than_line_break(start_title_vpos, t->minv, (height*3)/2)) &&
1747 (t->minh > left_margin_indent)) ||
1748 (is_bold(t) && (t->minh > left_margin_indent))) {
1749 start_title_vpos = min(t->minv, start_title_vpos);
1750 end_title_hpos = max(t->maxh, end_title_hpos);
1751 strcat(title.text, " ");
1752 str_translate_to_html(t->text_style.f, buf, MAX_STRING_LENGTH, t->text_string, t->text_length, TRUE);
1753 strcat(title.text, buf);
1754 page_contents->words.sub_move_right();
1755 removed_from_head = ((!page_contents->words.is_empty()) &&
1756 (page_contents->words.is_equal_to_head()));
1757 } else {
1758 // end of title
1759 title.has_been_found = TRUE;
1760 return;
1762 } else if (t->minh <= left_margin_indent) {
1763 // no margin exists
1764 return;
1765 } else {
1766 // move onto next word
1767 page_contents->words.move_right();
1769 } while ((! page_contents->words.is_equal_to_head()) || (removed_from_head));
1775 * html_newline - generates a newline <br>
1778 void html_printer::html_newline (void)
1780 int r = font::res;
1781 int height = output_style.point_size*r/72;
1783 if (current_paragraph->in_paragraph) {
1784 // safe to generate a pretty newline
1785 html.put_string("<br>\n");
1786 } else {
1787 html.put_string("<br>");
1789 output_vpos += height;
1790 issued_newline = TRUE;
1794 * issue_left_paragraph - emits a left paragraph together with appropriate
1795 * margin if header_indent is < left_margin_indent.
1798 void html_printer::issue_left_paragraph (void)
1800 if ((header_indent < left_margin_indent) && (! using_table_for_indent())) {
1801 html.put_string("<p style=\"margin-left: ");
1802 html.put_number(((left_margin_indent-header_indent)*100)/(right_margin_indent-header_indent));
1803 html.put_string("%\">");
1804 } else {
1805 html.put_string("<p>");
1810 * force_begin_paragraph - force the begin_paragraph to be emitted.
1813 void html_printer::force_begin_paragraph (void)
1815 if (current_paragraph->in_paragraph && current_paragraph->need_paragraph) {
1816 switch (current_paragraph->para_type) {
1818 case left_alignment: issue_left_paragraph();
1819 break;
1820 case center_alignment: html.put_string("<p align=center>");
1821 break;
1822 default: fatal("unknown paragraph alignment type");
1824 current_paragraph->need_paragraph = FALSE;
1829 * begin_paragraph - starts a new paragraph. It does nothing if a paragraph
1830 * has already been started.
1833 void html_printer::begin_paragraph (paragraph_type p)
1835 if (! current_paragraph->in_paragraph) {
1836 int r = font::res;
1837 int height = output_style.point_size*r/72;
1839 if (output_vpos >=0) {
1840 // we leave it alone if it is set to the top of page
1841 output_vpos += height;
1843 current_paragraph->need_paragraph = TRUE; // delay the <p> just in case we don't actually emit text
1844 current_paragraph->in_paragraph = TRUE;
1845 current_paragraph->para_type = p;
1846 issued_newline = TRUE;
1852 * begin_paragraph_no_height - starts a new paragraph. It does nothing if a paragraph
1853 * has already been started. Note it does not alter output_vpos.
1856 void html_printer::begin_paragraph_no_height (paragraph_type p)
1858 if (! current_paragraph->in_paragraph) {
1859 current_paragraph->need_paragraph = TRUE; // delay the <p> just in case we don't actually emit text
1860 current_paragraph->in_paragraph = TRUE;
1861 current_paragraph->para_type = p;
1862 issued_newline = TRUE;
1867 * end_paragraph - end the current paragraph. It does nothing if a paragraph
1868 * has not been started.
1871 void html_printer::end_paragraph (void)
1873 if (current_paragraph->in_paragraph) {
1874 // check whether we have generated any text inbetween the potential paragraph begin end
1875 if (! current_paragraph->need_paragraph) {
1876 int r = font::res;
1877 int height = output_style.point_size*r/72;
1879 output_vpos += height;
1880 terminate_current_font();
1881 html.put_string("</p>\n");
1882 } else {
1883 terminate_current_font();
1885 current_paragraph->para_type = left_alignment;
1886 current_paragraph->in_paragraph = FALSE;
1891 * save_paragraph - saves the current paragraph state and
1892 * creates new paragraph state.
1895 void html_printer::save_paragraph (void)
1897 if (current_paragraph == 0) {
1898 fatal("current_paragraph is NULL");
1900 current_paragraph = new html_paragraph(current_paragraph->in_paragraph,
1901 current_paragraph->need_paragraph,
1902 current_paragraph->para_type,
1903 current_paragraph);
1904 terminate_current_font();
1908 * restore_paragraph - restores the previous paragraph state.
1911 void html_printer::restore_paragraph (void)
1913 html_paragraph *old = current_paragraph;
1915 current_paragraph = current_paragraph->previous;
1916 free(old);
1920 * calculate_margin - runs through the words and graphics globs
1921 * and finds the start of the left most margin.
1924 void html_printer::calculate_margin (void)
1926 text_glob *w;
1927 graphic_glob *g;
1929 // remove margin
1931 right_margin_indent = 0;
1933 if (! page_contents->words.is_empty()) {
1935 // firstly check the words to determine the right margin
1937 page_contents->words.start_from_head();
1938 do {
1939 w = page_contents->words.get_data();
1940 if ((w->maxh >= 0) && (w->maxh > right_margin_indent)) {
1941 right_margin_indent = w->maxh;
1942 #if 0
1943 if (right_margin_indent == 758) stop();
1944 #endif
1946 page_contents->words.move_right();
1947 } while (! page_contents->words.is_equal_to_head());
1950 * only examine graphics if no words present
1952 if (! page_contents->lines.is_empty()) {
1953 // now check for diagrams for right margin
1954 page_contents->lines.start_from_head();
1955 do {
1956 g = page_contents->lines.get_data();
1957 if ((g->maxh >= 0) && (g->maxh > right_margin_indent)) {
1958 right_margin_indent = g->maxh;
1959 #if 0
1960 if (right_margin_indent == 950) stop();
1961 #endif
1963 page_contents->lines.move_right();
1964 } while (! page_contents->lines.is_equal_to_head());
1969 * now we know the right margin lets do the same to find left margin
1972 if (header_indent == -1) {
1973 header_indent = right_margin_indent;
1975 left_margin_indent = right_margin_indent;
1977 if (! page_contents->words.is_empty()) {
1978 do {
1979 w = page_contents->words.get_data();
1980 if ((w->minh >= 0) && (w->minh < left_margin_indent)) {
1981 if (! is_a_header(w) && (! w->is_raw_command)) {
1982 left_margin_indent = w->minh;
1985 page_contents->words.move_right();
1986 } while (! page_contents->words.is_equal_to_head());
1990 * only examine graphic for margins if text yields nothing
1993 if (! page_contents->lines.is_empty()) {
1994 // now check for diagrams
1995 page_contents->lines.start_from_head();
1996 do {
1997 g = page_contents->lines.get_data();
1998 if ((g->minh >= 0) && (g->minh < left_margin_indent)) {
1999 left_margin_indent = g->minh;
2001 page_contents->lines.move_right();
2002 } while (! page_contents->lines.is_equal_to_head());
2008 * calculate_region - runs through the graphics globs and text globs
2009 * and ensures that all graphic routines
2010 * are defined by the region lists.
2011 * This then allows us to easily
2012 * determine the range of vertical and
2013 * horizontal boundaries for pictures,
2014 * tbl's and eqn's.
2018 void page::calculate_region (void)
2020 graphic_glob *g;
2022 if (! lines.is_empty()) {
2023 lines.start_from_head();
2024 do {
2025 g = lines.get_data();
2026 if (! is_in_region(g)) {
2027 if (! can_grow_region(g)) {
2028 make_new_region(g);
2031 lines.move_right();
2032 } while (! lines.is_equal_to_head());
2037 * remove_redundant_regions - runs through the regions and ensures that
2038 * all are needed. This is required as
2039 * a picture may be empty, or EQ EN pair
2040 * maybe empty.
2043 void html_printer::remove_redundant_regions (void)
2045 region_glob *r;
2047 // firstly run through the region making sure that all are needed
2048 // ie all contain a line or word
2049 if (! page_contents->regions.is_empty()) {
2050 page_contents->regions.start_from_tail();
2051 do {
2052 r = page_contents->regions.get_data();
2053 calculate_region_margins(r);
2054 if (page_contents->has_line(r) || page_contents->has_word(r)) {
2055 page_contents->regions.move_right();
2056 } else {
2057 page_contents->regions.sub_move_right();
2059 } while ((! page_contents->regions.is_empty()) &&
2060 (! page_contents->regions.is_equal_to_tail()));
2064 void html_printer::display_regions (void)
2066 if (debug_table_on) {
2067 region_glob *r;
2069 fprintf(stderr, "==========s t a r t===========\n");
2070 if (! page_contents->regions.is_empty()) {
2071 page_contents->regions.start_from_head();
2072 do {
2073 r = page_contents->regions.get_data();
2074 fprintf(stderr, "region minv %d maxv %d\n", r->minv, r->maxv);
2075 page_contents->regions.move_right();
2076 } while (! page_contents->regions.is_equal_to_head());
2078 fprintf(stderr, "============e n d=============\n");
2079 fflush(stderr);
2084 * remove_duplicate_regions - runs through the regions and ensures that
2085 * no duplicates exist.
2088 void html_printer::remove_duplicate_regions (void)
2090 region_glob *r;
2091 region_glob *l=0;
2093 if (! page_contents->regions.is_empty()) {
2094 page_contents->regions.start_from_head();
2095 l = page_contents->regions.get_data();
2096 page_contents->regions.move_right();
2097 r = page_contents->regions.get_data();
2098 if (l != r) {
2099 do {
2100 r = page_contents->regions.get_data();
2101 // we have a legit region so we check for an intersection
2102 if (is_intersection(r->minv, r->minv, l->minv, l->maxv) &&
2103 is_intersection(r->minh, r->maxh, l->minh, l->maxh)) {
2104 l->minv = min(r->minv, l->minv);
2105 l->maxv = max(r->maxv, l->maxv);
2106 l->minh = min(r->minh, l->minh);
2107 l->maxh = max(r->maxh, l->maxh);
2108 calculate_region_margins(l);
2109 page_contents->regions.sub_move_right();
2110 } else {
2111 l = r;
2112 page_contents->regions.move_right();
2114 } while ((! page_contents->regions.is_empty()) &&
2115 (! page_contents->regions.is_equal_to_head()));
2120 int page::has_line (region_glob *r)
2122 graphic_glob *g;
2124 if (! lines.is_empty()) {
2125 lines.start_from_head();
2126 do {
2127 g = lines.get_data();
2128 if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) &&
2129 is_subsection(g->minh, g->maxh, r->minh, r->maxh)) {
2130 return( TRUE );
2132 lines.move_right();
2133 } while (! lines.is_equal_to_head());
2135 return( FALSE );
2139 int page::has_word (region_glob *r)
2141 text_glob *g;
2143 if (! words.is_empty()) {
2144 words.start_from_head();
2145 do {
2146 g = words.get_data();
2147 if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) &&
2148 is_subsection(g->minh, g->maxh, r->minh, r->maxh)) {
2149 return( TRUE );
2151 words.move_right();
2152 } while (! words.is_equal_to_head());
2154 return( FALSE );
2158 void html_printer::calculate_region_margins (region_glob *r)
2160 text_glob *w;
2161 graphic_glob *g;
2163 r->minh = right_margin_indent;
2164 r->maxh = left_margin_indent;
2166 if (! page_contents->lines.is_empty()) {
2167 page_contents->lines.start_from_head();
2168 do {
2169 g = page_contents->lines.get_data();
2170 if (is_subsection(g->minv, g->maxv, r->minv, r->maxv)) {
2171 r->minh = min(r->minh, g->minh);
2172 r->maxh = max(r->maxh, g->maxh);
2174 page_contents->lines.move_right();
2175 } while (! page_contents->lines.is_equal_to_head());
2177 if (! page_contents->words.is_empty()) {
2178 page_contents->words.start_from_head();
2179 do {
2180 w = page_contents->words.get_data();
2181 if (is_subsection(w->minv, w->maxv, r->minv, r->maxv)) {
2182 r->minh = min(r->minh, w->minh);
2183 r->maxh = max(r->maxh, w->maxh);
2185 page_contents->words.move_right();
2186 } while (! page_contents->words.is_equal_to_head());
2191 int page::is_in_region (graphic_glob *g)
2193 region_glob *r;
2195 if (! regions.is_empty()) {
2196 regions.start_from_head();
2197 do {
2198 r = regions.get_data();
2199 if (is_subsection(g->minv, g->maxv, r->minv, r->maxv) &&
2200 is_subsection(g->minh, g->maxh, r->minh, r->maxh)) {
2201 return( TRUE );
2203 regions.move_right();
2204 } while (! regions.is_equal_to_head());
2206 return( FALSE );
2211 * no_raw_commands - returns TRUE if no html raw commands exist between
2212 * minv and maxv.
2215 int page::no_raw_commands (int minv, int maxv)
2217 text_glob *g;
2219 if (! words.is_empty()) {
2220 words.start_from_head();
2221 do {
2222 g = words.get_data();
2223 if ((g->is_raw_command) && (g->is_html_command) &&
2224 (is_intersection(g->minv, g->maxv, minv, maxv))) {
2225 return( FALSE );
2227 words.move_right();
2228 } while (! words.is_equal_to_head());
2230 return( TRUE );
2234 * can_grow_region - returns TRUE if a region exists which can be extended
2235 * to include graphic_glob *g. The region is extended.
2238 int page::can_grow_region (graphic_glob *g)
2240 region_glob *r;
2241 int quarter_inch=font::res/4;
2243 if (! regions.is_empty()) {
2244 regions.start_from_head();
2245 do {
2246 r = regions.get_data();
2247 // must prevent grohtml from growing a region through a html raw command
2248 if (is_intersection(g->minv, g->maxv, r->minv, r->maxv+quarter_inch) &&
2249 (no_raw_commands(r->minv, r->maxv+quarter_inch))) {
2250 #if defined(DEBUGGING)
2251 stop();
2252 printf("r minh=%d minv=%d maxh=%d maxv=%d\n",
2253 r->minh, r->minv, r->maxh, r->maxv);
2254 printf("g minh=%d minv=%d maxh=%d maxv=%d\n",
2255 g->minh, g->minv, g->maxh, g->maxv);
2256 #endif
2257 r->minv = min(r->minv, g->minv);
2258 r->maxv = max(r->maxv, g->maxv);
2259 r->minh = min(r->minh, g->minh);
2260 r->maxh = max(r->maxh, g->maxh);
2261 #if defined(DEBUGGING)
2262 printf(" r minh=%d minv=%d maxh=%d maxv=%d\n",
2263 r->minh, r->minv, r->maxh, r->maxv);
2264 #endif
2265 return( TRUE );
2267 regions.move_right();
2268 } while (! regions.is_equal_to_head());
2270 return( FALSE );
2275 * make_new_region - creates a new region to contain, g.
2278 void page::make_new_region (graphic_glob *g)
2280 region_glob *r=new region_glob;
2282 r->minv = g->minv;
2283 r->maxv = g->maxv;
2284 r->minh = g->minh;
2285 r->maxv = g->maxv;
2286 regions.add(r);
2290 void html_printer::dump_page(void)
2292 text_glob *g;
2294 printf("\n\ndebugging start\n");
2295 page_contents->words.start_from_head();
2296 do {
2297 g = page_contents->words.get_data();
2298 printf("%s ", g->text_string);
2299 page_contents->words.move_right();
2300 } while (! page_contents->words.is_equal_to_head());
2301 printf("\ndebugging end\n\n");
2306 * traverse_page_regions - runs through the regions in current_page
2307 * and generate html for text, and troff output
2308 * for all graphics.
2311 void html_printer::traverse_page_regions (void)
2313 region_glob *r;
2315 start_region_vpos = 0;
2316 start_region_hpos = 0;
2317 end_region_vpos = -1;
2318 end_region_hpos = -1;
2320 if (! page_contents->regions.is_empty()) {
2321 page_contents->regions.start_from_head();
2322 do {
2323 r = page_contents->regions.get_data();
2324 if (r->minv > 0) {
2325 end_region_vpos = r->minv-1;
2326 } else {
2327 end_region_vpos = 0;
2329 end_region_hpos = -1;
2330 display_globs(TRUE);
2331 calculate_region_margins(r);
2332 start_region_vpos = end_region_vpos;
2333 end_region_vpos = r->maxv;
2334 start_region_hpos = r->minh;
2335 end_region_hpos = r->maxh;
2336 display_globs(FALSE);
2337 start_region_vpos = end_region_vpos+1;
2338 start_region_hpos = 0;
2339 page_contents->regions.move_right();
2340 } while (! page_contents->regions.is_equal_to_head());
2341 start_region_vpos = end_region_vpos+1;
2342 start_region_hpos = 0;
2343 end_region_vpos = -1;
2344 end_region_hpos = -1;
2346 display_globs(TRUE);
2349 int html_printer::is_within_region (text_glob *t)
2351 int he, ve, hs;
2353 if (start_region_hpos == -1) {
2354 hs = t->minh;
2355 } else {
2356 hs = start_region_hpos;
2358 if (end_region_vpos == -1) {
2359 ve = t->maxv;
2360 } else {
2361 ve = end_region_vpos;
2363 if (end_region_hpos == -1) {
2364 he = t->maxh;
2365 } else {
2366 he = end_region_hpos;
2368 return( is_subsection(t->minv, t->maxv, start_region_vpos, ve) &&
2369 is_subsection(t->minh, t->maxh, hs, he) );
2372 int html_printer::is_within_region (graphic_glob *g)
2374 int he, ve, hs;
2376 if (start_region_hpos == -1) {
2377 hs = g->minh;
2378 } else {
2379 hs = start_region_hpos;
2381 if (end_region_vpos == -1) {
2382 ve = g->maxv;
2383 } else {
2384 ve = end_region_vpos;
2386 if (end_region_hpos == -1) {
2387 he = g->maxh;
2388 } else {
2389 he = end_region_hpos;
2391 return( is_subsection(g->minv, g->maxv, start_region_vpos, ve) &&
2392 is_subsection(g->minh, g->maxh, hs, he) );
2395 int html_printer::is_less (graphic_glob *g, text_glob *t)
2397 return( (g->minv < t->minv) || ((g->minv == t->minv) && (g->minh < t->minh)) );
2400 void html_printer::convert_to_image (char *troff_src, char *image_name)
2402 char buffer[1024];
2403 char *ps_src = mktemp(xtmptemplate("-ps-"));
2405 sprintf(buffer, "grops %s > %s\n", troff_src, ps_src);
2406 if (debug_on) {
2407 fprintf(stderr, buffer);
2409 system(buffer);
2411 if (image_type == gif) {
2412 sprintf(buffer,
2413 "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",
2414 image_res,
2415 (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2416 (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2417 ps_src, image_name);
2418 } else {
2419 sprintf(buffer,
2420 "echo showpage | gs -q -dSAFER -sDEVICE=%s -r%d -g%dx%d -sOutputFile=- %s - 2> /dev/null > %s.png \n",
2421 image_device,
2422 image_res,
2423 (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2424 (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2425 ps_src, image_name);
2426 #if 0
2427 sprintf(buffer,
2428 "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",
2429 /* image_device, */
2430 image_res,
2431 (end_region_hpos-start_region_hpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2432 (end_region_vpos-start_region_vpos)*image_res/font::res+IMAGE_BOARDER_PIXELS,
2433 name, name, name, image_name);
2434 #endif
2436 if (debug_on) {
2437 fprintf(stderr, "%s", buffer);
2439 system(buffer);
2440 unlink(ps_src);
2441 unlink(troff_src);
2444 void html_printer::prologue (void)
2446 troff.put_string("x T ps\nx res ");
2447 troff.put_number(postscript_res);
2448 troff.put_string(" 1 1\nx init\np1\n");
2451 void html_printer::display_globs (int is_to_html)
2453 text_glob *t=0;
2454 graphic_glob *g=0;
2455 FILE *f=0;
2456 char *troff_src;
2457 int something=FALSE;
2458 int is_center=FALSE;
2460 end_paragraph();
2462 if (! is_to_html) {
2463 is_center = html_position_region();
2464 make_new_image_name();
2465 f = xtmpfile(&troff_src, "-troff-", FALSE);
2466 troff.set_file(f);
2467 prologue();
2468 output_style.f = 0;
2470 if (! page_contents->words.is_empty()) {
2471 page_contents->words.start_from_head();
2472 t = page_contents->words.get_data();
2475 if (! page_contents->lines.is_empty()) {
2476 page_contents->lines.start_from_head();
2477 g = page_contents->lines.get_data();
2480 do {
2481 #if 0
2482 if ((t != 0) && (strcmp(t->text_string, "(1.a)") == 0)) {
2483 stop();
2485 #endif
2486 if ((t == 0) && (g != 0)) {
2487 if (is_within_region(g)) {
2488 something = TRUE;
2489 display_line(g, is_to_html);
2491 if (page_contents->lines.is_empty() || page_contents->lines.is_equal_to_tail()) {
2492 g = 0;
2493 } else {
2494 g = page_contents->lines.move_right_get_data();
2496 } else if ((g == 0) && (t != 0)) {
2497 if (is_within_region(t)) {
2498 display_word(t, is_to_html);
2499 something = TRUE;
2501 if (page_contents->words.is_empty() || page_contents->words.is_equal_to_tail()) {
2502 t = 0;
2503 } else {
2504 t = page_contents->words.move_right_get_data();
2506 } else {
2507 if ((g == 0) || (t == 0)) {
2508 // hmm nothing to print out...
2509 } else if (is_less(g, t)) {
2510 if (is_within_region(g)) {
2511 display_line(g, is_to_html);
2512 something = TRUE;
2514 if (page_contents->lines.is_empty() || page_contents->lines.is_equal_to_tail()) {
2515 g = 0;
2516 } else {
2517 g = page_contents->lines.move_right_get_data();
2519 } else {
2520 if (is_within_region(t)) {
2521 display_word(t, is_to_html);
2522 something = TRUE;
2524 if (page_contents->words.is_empty() || page_contents->words.is_equal_to_tail()) {
2525 t = 0;
2526 } else {
2527 t = page_contents->words.move_right_get_data();
2531 } while ((t != 0) || (g != 0));
2533 if ((! is_to_html) && (f != 0)) {
2534 fclose(troff.get_file());
2535 if (something) {
2536 convert_to_image(troff_src, image_name);
2538 if (is_center) {
2539 end_paragraph();
2540 begin_paragraph(center_alignment);
2541 force_begin_paragraph();
2543 html.put_string("<img src=\"");
2544 html.put_string(image_name);
2545 if (image_type == gif) {
2546 html.put_string(".gif\"");
2547 } else {
2548 html.put_string(".png\"");
2550 html.put_string(">\n");
2551 html_newline();
2552 if (is_center) {
2553 end_paragraph();
2556 output_vpos = end_region_vpos;
2557 output_hpos = 0;
2558 need_one_newline = FALSE;
2559 output_style.f = 0;
2560 end_paragraph();
2565 void html_printer::flush_page (void)
2567 calculate_margin();
2568 output_vpos = -1;
2569 output_hpos = get_left();
2570 supress_sub_sup = TRUE;
2571 #if 0
2572 dump_page();
2573 #endif
2574 html.begin_comment("left margin: ").comment_arg(i_to_a(left_margin_indent)).end_comment();;
2575 html.begin_comment("right margin: ").comment_arg(i_to_a(right_margin_indent)).end_comment();;
2576 remove_redundant_regions();
2577 page_contents->calculate_region();
2578 remove_duplicate_regions();
2579 find_title();
2580 supress_sub_sup = TRUE;
2581 traverse_page_regions();
2582 terminate_current_font();
2583 if (need_one_newline) {
2584 html_newline();
2586 end_paragraph();
2588 // move onto a new page
2589 delete page_contents;
2590 page_contents = new page;
2593 static int convertSizeToHTML (int size)
2595 if (size < 6) {
2596 return( 0 );
2597 } else if (size < 8) {
2598 return( 1 );
2599 } else if (size < 10) {
2600 return( 2 );
2601 } else if (size < 12) {
2602 return( 3 );
2603 } else if (size < 14) {
2604 return( 4 );
2605 } else if (size < 16) {
2606 return( 5 );
2607 } else if (size < 18) {
2608 return( 6 );
2609 } else {
2610 return( 7 );
2615 void html_printer::write_html_font_face (const char *fontname, const char *left, const char *right)
2617 switch (fontname[0]) {
2619 case 'C': html.put_string(left) ; html.put_string("tt"); html.put_string(right);
2620 break;
2621 case 'H': break;
2622 case 'T': break;
2623 default: break;
2628 void html_printer::write_html_font_type (const char *fontname, const char *left, const char *right)
2630 if (strcmp(&fontname[1], "B") == 0) {
2631 html.put_string(left) ; html.put_string("B"); html.put_string(right);
2632 } else if (strcmp(&fontname[1], "I") == 0) {
2633 html.put_string(left) ; html.put_string("I"); html.put_string(right);
2634 } else if (strcmp(&fontname[1], "BI") == 0) {
2635 html.put_string(left) ; html.put_string("EM"); html.put_string(right);
2640 void html_printer::html_change_font (text_glob *g, const char *fontname, int size)
2642 char buffer[1024];
2644 if (output_style.f != 0) {
2645 const char *oldfontname = output_style.f->get_name();
2647 // firstly terminate the current font face and type
2648 if ((oldfontname != 0) && (oldfontname != fontname)) {
2649 write_html_font_face(oldfontname, "</", ">");
2650 write_html_font_type(oldfontname, "</", ">");
2654 if ((output_style.point_size != size) && (output_style.point_size != 0)) {
2655 // shutdown the previous font size
2656 html.put_string("</font>");
2659 if ((output_style.point_size != size) && (size != 0)) {
2660 // now emit the size if it has changed
2661 sprintf(buffer, "<font size=%d>", convertSizeToHTML(size));
2662 html.put_string(buffer);
2663 output_style.point_size = size; // and remember the size
2665 output_style.f = 0; // no style at present
2666 output_style.point_size = size; // remember current font size
2668 if (fontname != 0) {
2669 if (! g->is_raw_command) {
2670 // now emit the new font
2671 write_html_font_face(fontname, "<", ">");
2673 // now emit the new font type
2674 write_html_font_type(fontname, "<", ">");
2676 output_style = g->text_style; // remember style for next time
2682 void html_printer::change_font (text_glob *g, int is_to_html)
2684 if (is_to_html) {
2685 if (output_style != g->text_style) {
2686 const char *fontname=0;
2687 int size=0;
2689 if (g->text_style.f != 0) {
2690 fontname = g->text_style.f->get_name();
2691 size = (font::res/(72*font::sizescale))*g->text_style.point_size;
2693 html_change_font(g, fontname, size);
2695 } else {
2696 // is to troff
2697 if (output_style != g->text_style) {
2698 if (g->text_style.f != 0) {
2699 const char *fontname = g->text_style.f->get_name();
2700 int size = (font::res/(72*font::sizescale))*g->text_style.point_size;
2702 if (fontname == 0) {
2703 fatal("no internalname specified for font");
2706 troff_change_font(fontname, size, g->text_style.font_no);
2707 output_style = g->text_style; // remember style for next time
2714 * is_bold - returns TRUE if the text inside, g, is using a bold face.
2715 * It returns FALSE is g contains a raw html command, even if this uses
2716 * a bold font.
2719 int html_printer::is_bold (text_glob *g)
2721 if (g->text_style.f == 0) {
2722 // unknown font
2723 return( FALSE );
2724 } else if (g->is_raw_command) {
2725 return( FALSE );
2726 } else {
2727 const char *fontname = g->text_style.f->get_name();
2729 if (strlen(fontname) >= 2) {
2730 return( fontname[1] == 'B' );
2731 } else {
2732 return( FALSE );
2737 void html_printer::terminate_current_font (void)
2739 text_glob g;
2741 // we create a dummy glob just so we can tell html_change_font not to start up
2742 // a new font
2743 g.is_raw_command = TRUE;
2744 html_change_font(&g, 0, 0);
2747 void html_printer::write_header (text_glob *g)
2749 if (strlen(header.header_buffer) > 0) {
2750 if (header.header_level > 7) {
2751 header.header_level = 7;
2754 if (cutoff_heading+2 > header.header_level) {
2755 // firstly we must terminate any font and type faces
2756 terminate_current_font();
2757 end_paragraph();
2759 // secondly we generate a tag
2760 html.put_string("<a name=\"");
2761 html.put_string(header.header_buffer);
2762 html.put_string("\"></a>");
2763 // now we save the header so we can issue a list of link
2764 style st;
2766 header.no_of_headings++;
2768 text_glob *h=new text_glob(&st,
2769 header.headings.add_string(header.header_buffer, strlen(header.header_buffer)),
2770 strlen(header.header_buffer),
2771 header.no_of_headings, header.header_level,
2772 header.no_of_headings, header.header_level,
2773 FALSE, FALSE);
2774 header.headers.add(h); // and add this header to the header list
2775 } else {
2776 terminate_current_font();
2777 end_paragraph();
2780 // we adjust the margin if necessary
2782 if (g->minh < left_margin_indent) {
2783 header_indent = g->minh;
2786 // and now we issue the real header
2787 html.put_string("<h");
2788 html.put_number(header.header_level);
2789 html.put_string(">");
2790 html.put_string(header.header_buffer);
2791 html.put_string("</h");
2792 html.put_number(header.header_level);
2793 html.put_string(">");
2795 need_one_newline = FALSE;
2796 begin_paragraph(left_alignment);
2797 header.written_header = TRUE;
2802 * translate_str_to_html - translates a string, str, into html representation.
2803 * len indicates the string length.
2806 void translate_str_to_html (font *f, char *str, int len)
2808 char buf[MAX_STRING_LENGTH];
2810 str_translate_to_html(f, buf, MAX_STRING_LENGTH, str, len, TRUE);
2811 strncpy(str, buf, max(len, strlen(buf)+1));
2815 * write_headings - emits a list of links for the headings in this document
2818 void header_desc::write_headings (FILE *f)
2820 text_glob *g;
2822 if (! headers.is_empty()) {
2823 headers.start_from_head();
2824 do {
2825 g = headers.get_data();
2826 fprintf(f, "<a href=\"#%s\">%s</a><br>\n", g->text_string, g->text_string);
2827 headers.move_right();
2828 } while (! headers.is_equal_to_head());
2832 void html_printer::determine_header_level (void)
2834 int i;
2835 int l=strlen(header.header_buffer);
2836 int stops=0;
2838 for (i=0; ((i<l) && ((header.header_buffer[i] == '.') || is_digit(header.header_buffer[i]))) ; i++) {
2839 if (header.header_buffer[i] == '.') {
2840 stops++;
2843 if (stops > 0) {
2844 header.header_level = stops;
2849 void html_printer::build_header (text_glob *g)
2851 text_glob *l;
2852 int current_vpos;
2853 char buf[MAX_STRING_LENGTH];
2855 strcpy(header.header_buffer, "");
2856 do {
2857 l = g;
2858 current_vpos = g->minv;
2859 str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH, g->text_string, g->text_length, TRUE);
2860 strcat(header.header_buffer, (char *)buf);
2861 page_contents->words.move_right();
2862 g = page_contents->words.get_data();
2863 if (g->minv == current_vpos) {
2864 strcat(header.header_buffer, " ");
2866 } while ((! page_contents->words.is_equal_to_head()) &&
2867 ((g->minv == current_vpos) || (l->maxh == right_margin_indent)));
2869 determine_header_level();
2870 // finally set the output to neutral for after the header
2872 g = page_contents->words.get_data();
2873 output_vpos = g->minv; // set output_vpos to the next line since
2874 output_hpos = left_margin_indent; // html header forces a newline anyway
2875 page_contents->words.move_left(); // so that next time we use old g
2877 need_one_newline = FALSE;
2882 * is_whole_line_bold - returns TRUE if the whole line is bold.
2885 int html_printer::is_whole_line_bold (text_glob *g)
2887 text_glob *n=g;
2888 int current_vpos=g->minv;
2890 do {
2891 if (is_bold(n)) {
2892 page_contents->words.move_right();
2893 n = page_contents->words.get_data();
2894 } else {
2895 while (page_contents->words.get_data() != g) {
2896 page_contents->words.move_left();
2898 return( FALSE );
2900 } while ((! page_contents->words.is_equal_to_head()) && (is_on_same_line(n, current_vpos)));
2901 // was (n->minv == current_vpos)
2902 while (page_contents->words.get_data() != g) {
2903 page_contents->words.move_left();
2905 return( TRUE );
2910 * is_a_header - returns TRUE if the whole sequence of contineous lines are bold.
2911 * It checks to see whether a line is likely to be contineous and
2912 * then checks that all words are bold.
2915 int html_printer::is_a_header (text_glob *g)
2917 text_glob *l;
2918 text_glob *n=g;
2919 int current_vpos;
2921 do {
2922 l = n;
2923 current_vpos = n->minv;
2924 if (is_bold(n)) {
2925 page_contents->words.move_right();
2926 n = page_contents->words.get_data();
2927 } else {
2928 while (page_contents->words.get_data() != g) {
2929 page_contents->words.move_left();
2931 return( FALSE );
2933 } while ((! page_contents->words.is_equal_to_head()) &&
2934 ((n->minv == current_vpos) || (l->maxh == right_margin_indent)));
2935 while (page_contents->words.get_data() != g) {
2936 page_contents->words.move_left();
2938 return( TRUE );
2942 int html_printer::processed_header (text_glob *g)
2944 if ((guess_on) && (g->minh <= left_margin_indent) && (! using_table_for_indent()) &&
2945 (is_a_header(g))) {
2946 build_header(g);
2947 write_header(g);
2948 return( TRUE );
2949 } else {
2950 return( FALSE );
2954 int is_punctuation (char *s, int length)
2956 return( (length == 1) &&
2957 ((s[0] == '(') || (s[0] == ')') || (s[0] == '!') || (s[0] == '.') || (s[0] == '[') ||
2958 (s[0] == ']') || (s[0] == '?') || (s[0] == ',') || (s[0] == ';') || (s[0] == ':') ||
2959 (s[0] == '@') || (s[0] == '#') || (s[0] == '$') || (s[0] == '%') || (s[0] == '^') ||
2960 (s[0] == '&') || (s[0] == '*') || (s[0] == '+') || (s[0] == '-') || (s[0] == '=') ||
2961 (s[0] == '{') || (s[0] == '}') || (s[0] == '|') || (s[0] == '\"') || (s[0] == '\''))
2966 * move_horizontal - moves right into the position, g->minh.
2969 void html_printer::move_horizontal (text_glob *g, int left_margin)
2971 if (g->text_style.f != 0) {
2972 int w = g->text_style.f->get_space_width(g->text_style.point_size);
2974 if (w == 0) {
2975 fatal("space width is zero");
2977 if ((output_hpos == left_margin) && (g->minh > output_hpos)) {
2978 make_html_indent(g->minh-output_hpos);
2979 } else {
2980 emit_space(g, FALSE);
2982 output_hpos = g->maxh;
2983 output_vpos = g->minv;
2985 change_font(g, TRUE);
2990 * looks_like_subscript - returns TRUE if, g, looks like a subscript.
2993 int html_printer::looks_like_subscript (text_glob *g)
2995 int r = font::res;
2996 int height = output_style.point_size*r/72;
2998 /* was return( ((output_vpos < g->minv) && (output_style.point_size != 0) &&
2999 * (output_style.point_size > g->text_style.point_size)) );
3002 return( (output_style.point_size != 0) && (! supress_sub_sup) && (output_vpos+height < g->maxv) );
3006 * looks_like_superscript - returns TRUE if, g, looks like a superscript.
3009 int html_printer::looks_like_superscript (text_glob *g)
3011 int r = font::res;
3012 int height = output_style.point_size*r/72;
3014 /* was
3015 * return(((output_vpos > g->minv) && (output_style.point_size != 0) &&
3016 * (output_style.point_size > g->text_style.point_size)));
3019 return( (output_style.point_size != 0) && (! supress_sub_sup) && (output_vpos+height > g->maxv) );
3023 * looks_like_larger_font - returns TRUE if, g, can be treated as a larger font.
3024 * g needs to be on the same line
3027 int html_printer::looks_like_larger_font (text_glob *g)
3029 int r = font::res;
3030 int height = output_style.point_size*r/72;
3032 return( (output_vpos+height == g->maxv) && (output_style.point_size != 0) &&
3033 (convertSizeToHTML(g->text_style.point_size)+1 == convertSizeToHTML(output_style.point_size)) );
3037 * looks_like_smaller_font - returns TRUE if, g, can be treated as a smaller font.
3038 * g needs to be on the same line
3041 int html_printer::looks_like_smaller_font (text_glob *g)
3043 int r = font::res;
3044 int height = output_style.point_size*r/72;
3046 return( (output_vpos+height == g->maxv) && (output_style.point_size != 0) &&
3047 (convertSizeToHTML(g->text_style.point_size) == convertSizeToHTML(output_style.point_size)+1) );
3051 * pretend_is_on_same_line - returns TRUE if we think, g, is on the same line as the previous glob.
3052 * Note that it believes a single word spanning the left..right as being
3053 * on a different line.
3056 int html_printer::pretend_is_on_same_line (text_glob *g, int left_margin, int right_margin)
3058 return( auto_on && (right_margin == output_hpos) && (left_margin == g->minh) &&
3059 (right_margin != g->maxh) && ((! is_whole_line_bold(g)) || (g->text_style.f == output_style.f)) &&
3060 (! (using_table_for_indent()) || (indentation.wrap_margin)) );
3063 int html_printer::is_on_same_line (text_glob *g, int vpos)
3065 #if 0
3066 if (g->is_html_command) {
3067 stop();
3069 #endif
3070 return(
3071 (vpos >= 0) &&
3072 (is_intersection(vpos, vpos+g->text_style.point_size*font::res/72-1, g->minv, g->maxv))
3078 * make_html_indent - creates a relative indentation.
3081 void html_printer::make_html_indent (int indent)
3083 if ((indent > 0) && ((right_margin_indent-get_left()) > 0) &&
3084 ((indent*100)/(right_margin_indent-get_left()))) {
3085 html.put_string("<span style=\" text-indent: ");
3086 html.put_number((indent*100)/(right_margin_indent-get_left()));
3087 html.put_string("%;\"></span>");
3092 * using_table_for_indent - returns TRUE if we currently using a table for indentation
3093 * purposes.
3096 int html_printer::using_table_for_indent (void)
3098 return( indentation.no_of_columns != 0 );
3102 * calculate_min_gap - returns the minimum gap by which we deduce columns.
3103 * This is a rough heuristic.
3106 int html_printer::calculate_min_gap (text_glob *g)
3108 text_glob *t = g;
3110 while ((t->is_raw_command) && (! page_contents->words.is_equal_to_tail()) &&
3111 ((t->minv < end_region_vpos) || (end_region_vpos < 0))) {
3112 page_contents->words.move_right();
3113 t=page_contents->words.get_data();
3115 rewind_text_to(g);
3116 if (t->is_raw_command) {
3117 return( font::res * 10 ); // impossibly large gap width
3118 } else {
3119 return( t->text_style.f->get_space_width(t->text_style.point_size)*GAP_SPACES );
3124 * collect_columns - place html text in a column and return the vertical limit reached.
3127 int html_printer::collect_columns (struct text_defn *next_words,
3128 struct text_defn *next_cols,
3129 struct text_defn *last_words,
3130 struct text_defn *last_cols,
3131 int max_words)
3133 text_glob *start = page_contents->words.get_data();
3134 text_glob *t = start;
3135 int upper_limit = 0;
3138 * initialize cols and words
3140 next_words[0].left = 0;
3141 next_words[0].right = 0;
3142 next_cols [0].left = 0;
3143 next_cols [0].right = 0;
3146 * if we have not reached the end collect the words on the current line
3148 if (start != 0) {
3149 int graphic_limit = end_region_vpos;
3151 if (is_whole_line_bold(t) && (t->minh <= left_margin_indent) && (guess_on)) {
3153 * found header therefore terminate indentation table.
3154 * Return a negative number so we know a header has
3155 * stopped the column
3157 upper_limit = -t->minv;
3158 } else {
3159 int i =0; // is the index into next_cols
3160 int j =0; // is the column index for last_cols
3161 int k =0; // is the index into next_words
3162 int l =0; // is the index into next_words
3163 int prevh =0;
3164 int mingap =calculate_min_gap(start);
3167 * while words on the same line record them and any significant gaps
3169 while ((t != 0) && (is_on_same_line(t, start->minv) && (i<max_words)) &&
3170 ((graphic_limit == -1) || (graphic_limit > t->minv))) {
3173 * now find column index from the last line which corresponds to, t.
3175 j = find_column_index_in_line(t, last_cols);
3178 * now find word index from the last line which corresponds to, t.
3180 l = find_column_index_in_line(t, last_words);
3183 * Note t->minh might equal t->maxh when we are passing a special device character via \X
3184 * we currently ignore this when considering tables
3186 * if we have found a significant gap then record it
3188 if (((t->minh - prevh >= mingap) ||
3189 ((last_cols != 0) && (last_cols [j].right != 0) && (t->minh == last_cols [j].left))) &&
3190 (t->minh != t->maxh)) {
3191 next_cols[i].left = t->minh;
3192 next_cols[i].right = t->maxh;
3193 i++;
3195 * terminate the array
3197 if (i<max_words) {
3198 next_cols[i].left = 0;
3199 next_cols[i].right = 0;
3201 } else if (i>0) {
3203 * move previous right hand column to align with, t.
3206 if (t->minh > next_cols[i-1].left) {
3208 * a simple precaution in case we get globs which are technically on the same line
3209 * (sadly this does occur sometimes - maybe we should be stricter with is_on_same_line)
3210 * --fixme--
3212 next_cols[i-1].right = max(next_cols[i-1].right, t->maxh);
3216 * remember to record the individual words
3218 next_words[k].left = t->minh;
3219 next_words[k].right = t->maxh;
3220 k++;
3223 * and record the vertical upper limit
3225 upper_limit = max(t->minv, upper_limit);
3228 * and update prevh - used to detect a when a different line is seen
3230 prevh = t->maxh;
3233 * get next word into, t, which equals 0, if no word is found
3235 page_contents->words.move_right();
3236 t = page_contents->words.get_data();
3237 if (page_contents->words.is_equal_to_head()) {
3238 t = 0;
3243 * and terminate the next_words array
3246 if (k<max_words) {
3247 next_words[k].left = 0;
3248 next_words[k].right = 0;
3252 * consistency check, next_cols, after removing redundant colums.
3255 remove_redundant_columns(next_cols);
3257 #if 0
3258 for (k=0; k<count_columns(next_cols); k++) {
3259 if (next_cols[k].left > next_cols[k].right) {
3260 fprintf(stderr, "left > right\n"); fflush(stderr);
3261 stop();
3262 fatal("next_cols has messed up columns");
3264 if ((k>0) && (k+1<count_columns(next_cols)) && (next_cols[k].right > next_cols[k+1].left)) {
3265 fprintf(stderr, "next_cols[k].right > next_cols[k+1].left\n"); fflush(stderr);
3266 stop();
3267 fatal("next_cols has messed up columns");
3270 #endif
3273 return( upper_limit );
3277 * conflict_with_words - returns TRUE if a word sequence crosses a column.
3280 int html_printer::conflict_with_words (struct text_defn *column_guess, struct text_defn *words)
3282 int i=0;
3283 int j;
3285 while ((column_guess[i].right != 0) && (i<MAX_WORDS_PER_LINE)) {
3286 j=0;
3287 while ((words[j].right != 0) && (j<MAX_WORDS_PER_LINE)) {
3288 if ((words[j].left <= column_guess[i].right) && (i+1<MAX_WORDS_PER_LINE) &&
3289 (column_guess[i+1].right != 0) && (words[j].right >= column_guess[i+1].left)) {
3290 if (debug_table_on) {
3291 fprintf(stderr, "is a conflict with words\n");
3292 fflush(stderr);
3294 return( TRUE );
3296 j++;
3298 i++;
3300 if (debug_table_on) {
3301 fprintf(stderr, "is NOT a conflict with words\n");
3302 fflush(stderr);
3304 return( FALSE );
3308 * combine_line - combines dest and src.
3311 void html_printer::combine_line (struct text_defn *dest, struct text_defn *src)
3313 int i;
3315 for (i=0; (i<MAX_WORDS_PER_LINE) && (src[i].right != 0); i++) {
3316 include_into_list(dest, &src[i]);
3318 remove_redundant_columns(dest);
3322 * remove_entry_in_line - removes an entry, j, in, line.
3325 void html_printer::remove_entry_in_line (struct text_defn *line, int j)
3327 while (line[j].right != 0) {
3328 line[j].left = line[j+1].left;
3329 line[j].right = line[j+1].right;
3330 j++;
3335 * remove_redundant_columns - searches through the array columns and removes any redundant entries.
3338 void html_printer::remove_redundant_columns (struct text_defn *line)
3340 int i=0;
3341 int j=0;
3343 while (line[i].right != 0) {
3344 if ((i<MAX_WORDS_PER_LINE) && (line[i+1].right != 0)) {
3345 j = 0;
3346 while ((j<MAX_WORDS_PER_LINE) && (line[j].right != 0)) {
3347 if ((j != i) && (is_intersection(line[i].left, line[i].right, line[j].left, line[j].right))) {
3348 line[i].left = min(line[i].left , line[j].left);
3349 line[i].right = max(line[i].right, line[j].right);
3350 remove_entry_in_line(line, j);
3351 } else {
3352 j++;
3356 i++;
3361 * include_into_list - performs an order set inclusion
3364 void html_printer::include_into_list (struct text_defn *line, struct text_defn *item)
3366 int i=0;
3368 while ((i<MAX_WORDS_PER_LINE) && (line[i].right != 0) && (line[i].left<item->left)) {
3369 i++;
3372 if (line[i].right == 0) {
3373 // add to the end
3374 if (i<MAX_WORDS_PER_LINE) {
3375 if ((i>0) && (line[i-1].left > item->left)) {
3376 fatal("insertion error");
3378 line[i].left = item->left;
3379 line[i].right = item->right;
3380 i++;
3381 line[i].left = 0;
3382 line[i].right = 0;
3384 } else {
3385 if (line[i].left == item->left) {
3386 line[i].right = max(item->right, line[i].right);
3387 } else {
3388 // insert
3389 int left = item->left;
3390 int right = item->right;
3391 int l = line[i].left;
3392 int r = line[i].right;
3394 while ((i+1<MAX_WORDS_PER_LINE) && (line[i].right != 0)) {
3395 line[i].left = left;
3396 line[i].right = right;
3397 i++;
3398 left = l;
3399 right = r;
3400 l = line[i].left;
3401 r = line[i].right;
3403 if (i+1<MAX_WORDS_PER_LINE) {
3404 line[i].left = left;
3405 line[i].right = right;
3406 line[i+1].left = 0;
3407 line[i+1].right = 0;
3414 * is_in_column - return TRUE if value is present in line.
3417 int html_printer::is_in_column (struct text_defn *line, struct text_defn *item, int max_words)
3419 int i=0;
3421 while ((i<max_words) && (line[i].right != 0)) {
3422 if (line[i].left == item->left) {
3423 return( TRUE );
3424 } else {
3425 i++;
3428 return( FALSE );
3432 * calculate_right - calculate the right most margin for each column in line.
3435 void html_printer::calculate_right (struct text_defn *line, int max_words)
3437 int i=0;
3439 while ((i<max_words) && (line[i].right != 0)) {
3440 if (i>0) {
3441 line[i-1].right = line[i].left;
3443 i++;
3448 * add_right_full_width - adds an extra column to the right to bring the table up to
3449 * full width.
3452 void html_printer::add_right_full_width (struct text_defn *line, int mingap)
3454 int i=0;
3456 while ((i<MAX_WORDS_PER_LINE) && (line[i].right != 0)) {
3457 i++;
3460 if ((i>0) && (line[i-1].right != right_margin_indent) && (i+1<MAX_WORDS_PER_LINE)) {
3461 line[i].left = min(line[i-1].right+mingap, right_margin_indent);
3462 line[i].right = right_margin_indent;
3463 i++;
3464 if (i<MAX_WORDS_PER_LINE) {
3465 line[i].left = 0;
3466 line[i].right = 0;
3472 * determine_right_most_column - works out the right most limit of the right most column.
3473 * Required as we might be performing a .2C and only
3474 * have enough text to fill the left column.
3477 void html_printer::determine_right_most_column (struct text_defn *line, int max_words)
3479 int i=0;
3481 while ((i<max_words) && (line[i].right != 0)) {
3482 i++;
3484 if (i>0) {
3485 // remember right_margin_indent is the right most position for this page
3486 line[i-1].right = column_calculate_right_margin(line[i-1].left, right_margin_indent);
3491 * is_column_match - returns TRUE if a word is aligned in the same horizontal alignment
3492 * between two lines, line1 and line2. If so then this horizontal
3493 * position is saved in match.
3496 int html_printer::is_column_match (struct text_defn *match,
3497 struct text_defn *line1, struct text_defn *line2, int max_words)
3499 int i=0;
3500 int j=0;
3501 int found=FALSE;
3502 int first=(match[0].left==0);
3504 if (first) {
3505 struct text_defn t;
3507 t.left = left_margin_indent;
3508 t.right = 0;
3510 include_into_list(match, &t);
3512 while ((line1[i].right != 0) && (line2[i].right != 0)) {
3513 if (line1[i].left == line2[j].left) {
3514 // same horizontal alignment found
3515 include_into_list(match, &line1[i]);
3516 i++;
3517 j++;
3518 found = TRUE;
3519 } else if (line1[i].left < line2[j].left) {
3520 i++;
3521 } else {
3522 j++;
3525 calculate_right(match, max_words);
3526 return( found );
3530 * check_lack_of_hits - returns TRUE if a column has been moved to a position
3531 * of only one hit from a position of more than one hit.
3534 int html_printer::check_lack_of_hits (struct text_defn *next_guess,
3535 struct text_defn *last_guess,
3536 text_glob *start, int limit)
3538 text_glob *current=page_contents->words.get_data();
3539 int n=count_columns(last_guess);
3540 int m=count_columns(next_guess);
3541 int i, j;
3543 if (limit > 0) {
3544 rewind_text_to(start);
3545 count_hits(last_guess, n, limit);
3546 rewind_text_to(current);
3547 i=0;
3548 j=0;
3549 while ((i<n) && (j<m) &&
3550 (last_guess[i].right != 0) && (next_guess[j].right != 0)) {
3551 if ((is_intersection(last_guess[i].left, last_guess[i].right,
3552 next_guess[j].left, next_guess[j].right)) &&
3553 (next_guess[j].left < last_guess[i].left) &&
3554 (last_guess[i].is_used >= 2)) {
3556 * next_guess has to be = 1 as this position is new
3558 return( TRUE );
3560 if (last_guess[i].left < next_guess[j].left) {
3561 i++;
3562 } else {
3563 j++;
3567 return( FALSE );
3571 * remove_white_using_words - remove white space in, last_guess, by examining, next_line
3572 * placing results into next_guess.
3573 * It returns TRUE if the same columns exist in next_guess and last_guess
3574 * we do allow columns to shrink but if a column disappears then we return FALSE.
3577 int html_printer::remove_white_using_words (struct text_defn *next_guess,
3578 struct text_defn *last_guess, struct text_defn *next_line)
3580 int i=0;
3581 int j=0;
3582 int k=0;
3583 int removed=FALSE;
3585 while ((last_guess[j].right != 0) && (next_line[k].right != 0)) {
3586 if (last_guess[j].left == next_line[k].left) {
3587 // same horizontal alignment found
3588 next_guess[i].left = last_guess[j].left;
3589 next_guess[i].right = max(last_guess[j].right, next_line[k].right);
3590 i++;
3591 j++;
3592 k++;
3593 if ((next_guess[i-1].right > last_guess[j].left) && (last_guess[j].right != 0)) {
3594 removed = TRUE;
3596 } else if (last_guess[j].right < next_line[k].left) {
3597 next_guess[i].left = last_guess[j].left;
3598 next_guess[i].right = last_guess[j].right;
3599 i++;
3600 j++;
3601 } else if (last_guess[j].left > next_line[k].right) {
3602 // insert a word sequence from next_line[k]
3603 next_guess[i].left = next_line[k].left;
3604 next_guess[i].right = next_line[k].right;
3605 i++;
3606 k++;
3607 } else if (is_intersection(last_guess[j].left, last_guess[j].right, next_line[k].left, next_line[k].right)) {
3608 // potential for a column disappearing
3609 next_guess[i].left = min(last_guess[j].left , next_line[k].left);
3610 next_guess[i].right = max(last_guess[j].right, next_line[k].right);
3611 i++;
3612 j++;
3613 k++;
3614 if ((next_guess[i-1].right > last_guess[j].left) && (last_guess[j].right != 0)) {
3615 removed = TRUE;
3619 while (next_line[k].right != 0) {
3620 next_guess[i].left = next_line[k].left;
3621 next_guess[i].right = next_line[k].right;
3622 i++;
3623 k++;
3625 if (i<MAX_WORDS_PER_LINE) {
3626 next_guess[i].left = 0;
3627 next_guess[i].right = 0;
3629 if (debug_table_on) {
3630 if (removed) {
3631 fprintf(stderr, "have removed column\n");
3632 } else {
3633 fprintf(stderr, "have NOT removed column\n");
3635 fflush(stderr);
3637 remove_redundant_columns(next_guess);
3638 return( removed );
3642 * count_columns - returns the number of elements inside, line.
3645 int html_printer::count_columns (struct text_defn *line)
3647 int i=0;
3649 while (line[i].right != 0) {
3650 i++;
3652 return( i );
3656 * rewind_text_to - moves backwards until page_contents is looking at, g.
3659 void html_printer::rewind_text_to (text_glob *g)
3661 while (page_contents->words.get_data() != g) {
3662 if (page_contents->words.is_equal_to_head()) {
3663 page_contents->words.start_from_tail();
3664 } else {
3665 page_contents->words.move_left();
3671 * can_loose_column - checks to see whether we should combine two columns.
3672 * This is allowed if there are is only one hit on the
3673 * left hand edge and the previous column is very close.
3676 void html_printer::can_loose_column (text_glob *start, struct text_defn *last_guess, int limit)
3678 text_glob *current=page_contents->words.get_data();
3679 int n=count_columns(last_guess);
3680 int i;
3682 rewind_text_to(start);
3683 count_hits(last_guess, n, limit);
3684 i=0;
3685 while (i<n-1) {
3686 if ((last_guess[i+1].is_used == 1) &&
3687 (calculate_min_gap(start) > (last_guess[i+1].left-last_guess[i].right))) {
3688 last_guess[i].right = last_guess[i+1].right;
3689 remove_entry_in_line(last_guess, i+1);
3690 n = count_columns(last_guess);
3691 i = 0;
3692 } else {
3693 i++;
3696 rewind_text_to(current);
3700 * display_columns - a long overdue debugging function, as this column code is causing me grief :-(
3703 void html_printer::display_columns (const char *word, const char *name, text_defn *line)
3705 int i=0;
3707 fprintf(stderr, "[%s:%s]", name, word);
3708 while (line[i].right != 0) {
3709 fprintf(stderr, " <left=%d right=%d %d%%> ", line[i].left, line[i].right, line[i].percent);
3710 i++;
3712 fprintf(stderr, "\n");
3713 fflush(stderr);
3717 * copy_line - dest = src
3720 void html_printer::copy_line (struct text_defn *dest, struct text_defn *src)
3722 int k;
3724 for (k=0; ((src[k].right != 0) && (k<MAX_WORDS_PER_LINE)); k++) {
3725 dest[k].left = src[k].left;
3726 dest[k].right = src[k].right;
3728 if (k<MAX_WORDS_PER_LINE) {
3729 dest[k].left = 0;
3730 dest[k].right = 0;
3735 * add_column_gaps - adds empty columns between columns which don't exactly align
3738 void html_printer::add_column_gaps (struct text_defn *line)
3740 int i=0;
3741 struct text_defn t;
3743 // firstly lets see whether we need an initial column on the left hand side
3744 if ((line[0].left != get_left()) && (line[0].right != 0) &&
3745 (get_left() < line[0].left) && (is_worth_column(get_left(), line[0].left))) {
3746 t.left = get_left();
3747 t.right = line[0].left;
3748 include_into_list(line, &t);
3751 while ((i<MAX_WORDS_PER_LINE) && (line[i].right != 0)) {
3752 if ((i+1<MAX_WORDS_PER_LINE) && (line[i+1].right != 0) && (line[i].right != line[i+1].left) &&
3753 (is_worth_column(line[i].right, line[i+1].left))) {
3754 t.left = line[i].right;
3755 t.right = line[i+1].left;
3756 include_into_list(line, &t);
3757 i=0;
3758 } else {
3759 i++;
3762 // now let us see whether we need a final column on the right hand side
3763 if ((i>0) && (line[i-1].right != right_margin_indent) &&
3764 (is_worth_column(line[i-1].right, right_margin_indent))) {
3765 t.left = line[i-1].right;
3766 t.right = right_margin_indent;
3767 include_into_list(line, &t);
3772 * is_continueous_column - returns TRUE if a line has a word on one
3773 * of the last_col right most boundaries.
3776 int html_printer::is_continueous_column (text_defn *last_col, text_defn *next_line)
3778 int w = count_columns(next_line);
3779 int c = count_columns(last_col);
3780 int i, j;
3782 for (i=0; i<c; i++) {
3783 for (j=0; j<w; j++) {
3784 if (last_col[i].right == next_line[j].right) {
3785 return( TRUE );
3789 return( FALSE );
3793 * is_exact_left - returns TRUE if a line has a word on one
3794 * of the last_col left most boundaries.
3797 int html_printer::is_exact_left (text_defn *last_col, text_defn *next_line)
3799 int w = count_columns(next_line);
3800 int c = count_columns(last_col);
3801 int i, j;
3803 for (i=0; i<c; i++) {
3804 for (j=0; j<w; j++) {
3805 if ((last_col[i].left == next_line[j].left) ||
3806 (last_col[i].left != left_margin_indent)) {
3807 return( TRUE );
3811 return( FALSE );
3815 * continue_searching_column - decides whether we should carry on searching text for a column.
3818 int html_printer::continue_searching_column (text_defn *next_col,
3819 text_defn *last_col,
3820 text_defn *all_words)
3822 int count = count_columns(next_col);
3823 int words = count_columns(all_words);
3825 if ((words == 0) || ((words == 1) &&
3826 (all_words[0].left == left_margin_indent) &&
3827 (all_words[0].right == right_margin_indent))) {
3828 // no point as we have now seen a full line of contineous text with no gap
3829 return( FALSE );
3831 return( (count == count_columns(last_col)) &&
3832 (last_col[0].left != left_margin_indent) || (last_col[0].right != right_margin_indent) );
3836 * is_worth_column - returns TRUE if the size of this column is worth defining.
3839 int html_printer::is_worth_column (int left, int right)
3841 #if 0
3842 return( abs(right-left) >= MIN_COLUMN );
3843 #endif
3844 return( TRUE );
3848 * large_enough_gap - returns TRUE if a large enough gap for one line was seen.
3849 * We need to make sure that a single line definitely warrents
3850 * a table.
3851 * It also removes other smaller gaps.
3854 int html_printer::large_enough_gap (text_defn *last_col)
3856 int i=0;
3857 int found=FALSE;
3858 int r=font::res;
3859 int gap=r/GAP_WIDTH_ONE_LINE;
3861 if (abs(last_col[i].left - left_margin_indent) >= gap) {
3862 found = TRUE;
3864 while ((last_col[i].right != 0) && (last_col[i+1].right != 0)) {
3865 if (abs(last_col[i+1].left-last_col[i].right) >= gap) {
3866 found = TRUE;
3867 i++;
3868 } else {
3869 // not good enough for a single line, remove it
3870 last_col[i].right = last_col[i+1].right;
3871 remove_entry_in_line(last_col, i+1);
3874 return( found );
3878 * is_subset_of_columns - returns TRUE if line, a, is a subset of line, b.
3881 int html_printer::is_subset_of_columns (text_defn *a, text_defn *b)
3883 int i;
3884 int j;
3886 i=0;
3887 while ((i<MAX_WORDS_PER_LINE) && (a[i].right != 0)) {
3888 j=0;
3889 while ((j<MAX_WORDS_PER_LINE) && (b[j].right != 0) &&
3890 ((b[j].left != a[i].left) || (b[j].right != a[i].right))) {
3891 j++;
3893 if ((j==MAX_WORDS_PER_LINE) || (b[j].right == 0)) {
3894 // found a different column - not a subset
3895 return( FALSE );
3897 i++;
3899 return( TRUE );
3903 * count_hits - counts the number of hits per column. A left hit
3904 * is when the left hand position of a glob hits
3905 * the left hand column.
3908 void html_printer::count_hits (text_defn *col, int no_of_columns, int limit)
3910 int i;
3911 text_glob *start = page_contents->words.get_data();
3912 text_glob *g = start;
3914 // firstly reset the used field
3915 for (i=0; i<no_of_columns; i++) {
3916 col[i].is_used = 0;
3918 // now calculate the left hand hits
3919 while ((g != 0) && (g->minv <= limit)) {
3920 i=0;
3921 while ((i<no_of_columns) && (col[i].right < g->minh)) {
3922 i++;
3924 if ((col[i].left == g->minh) && (col[i].right != 0)) {
3925 col[i].is_used++;
3927 page_contents->words.move_right();
3928 if (page_contents->words.is_equal_to_head()) {
3929 g = 0;
3930 page_contents->words.start_from_tail();
3931 } else {
3932 g=page_contents->words.get_data();
3938 * count_right_hits - counts the number of right hits per column.
3939 * A right hit is when the left hand position
3940 * of a glob hits the right hand column.
3943 void html_printer::count_right_hits (text_defn *col, int no_of_columns)
3945 int i;
3946 text_glob *start = page_contents->words.get_data();
3947 text_glob *g = start;
3949 // firstly reset the used field
3950 for (i=0; i<no_of_columns; i++) {
3951 col[i].right_hits = 0;
3953 // now calculate the left hand hits
3954 while ((g != 0) && (g->minv <= indentation.vertical_limit)) {
3955 i=0;
3956 while ((i<no_of_columns) && (col[i].right < g->minh)) {
3957 i++;
3959 if ((i<no_of_columns) && (col[i].right == g->maxh)) {
3960 if (debug_table_on) {
3961 fprintf(stderr, "found right hit [%s] at %d in %d\n",
3962 g->text_string, g->maxh, i);
3963 fflush(stderr);
3965 col[i].right_hits++;
3967 page_contents->words.move_right();
3968 if (page_contents->words.is_equal_to_head()) {
3969 g = 0;
3970 page_contents->words.start_from_tail();
3971 } else {
3972 g=page_contents->words.get_data();
3978 * right_indentation - returns TRUE if a single column has been found and
3979 * it resembles an indentation. Ie .RS/.RE or ABSTACT
3982 int html_printer::right_indentation (struct text_defn *last_guess)
3984 // it assumes that last_guess contains a single column
3985 return( (last_guess[0].left > left_margin_indent) );
3989 * able_to_steal_width - returns TRUE if we have an unused column which we can steal from.
3990 * It must have more than MIN_TEXT_PERCENT to do this.
3993 int html_printer::able_to_steal_width (void)
3995 int i;
3997 for (i=0; i<indentation.no_of_columns; i++) {
3998 if ((! indentation.columns[i].is_used) &&
3999 (indentation.columns[i].percent > MIN_TEXT_PERCENT)) {
4000 return( TRUE );
4003 return( FALSE );
4007 * is_divisible_by - returns TRUE if n is divisible by d leaving no remainder.
4010 static int is_divisible_by (int n, int d)
4012 return( (n % d) == 0 );
4016 * need_to_steal_width - returns TRUE if a used column need to be
4017 * given a little extra width for safty sake.
4020 int html_printer::need_to_steal_width (void)
4022 int i;
4024 for (i=0; i<indentation.no_of_columns; i++) {
4025 if ((indentation.columns[i].is_used) &&
4026 (indentation.columns[i].percent == (((indentation.columns[i].right - indentation.columns[i].left) * 100) /
4027 (right_margin_indent-left_margin_indent))) &&
4028 (indentation.columns[i].percent < PERCENT_THRESHOLD)) {
4029 return( TRUE );
4032 return( FALSE );
4036 * utilize_round_off - utilize the remaining percent width in text columns
4039 void html_printer::utilize_round_off (void)
4041 int total = total_percentages();
4042 int excess, i;
4044 // use up the spare excess
4046 excess = 100-total;
4048 for (i=0; (i<indentation.no_of_columns) && (excess>0); i++) {
4049 if ((indentation.columns[i].is_used) &&
4050 (indentation.columns[i].percent < PERCENT_THRESHOLD)) {
4051 indentation.columns[i].percent++;
4052 excess--;
4055 // we might as well try and keep any numbers simple if possible
4056 for (i=0; (i<indentation.no_of_columns) && (excess>0); i++) {
4057 if ((indentation.columns[i].is_used) &&
4058 (! is_divisible_by(indentation.columns[i].percent, MIN_TEXT_PERCENT))) {
4059 indentation.columns[i].percent++;
4060 excess--;
4063 // forget the niceties lets just use excess up now!
4064 for (i=0; (i<indentation.no_of_columns) && (excess>0); i++) {
4065 if (indentation.columns[i].is_used) {
4066 indentation.columns[i].percent++;
4067 excess--;
4073 * can_distribute_fairly - returns TRUE if we can redistribute some of the unused width into
4074 * columns that are used.
4077 int html_printer::can_distribute_fairly (void)
4079 int i;
4080 int total=0;
4081 int used =0;
4082 int excess;
4084 // firstly total up all percentages - so we can use round offs
4085 for (i=0; i<indentation.no_of_columns; i++) {
4086 total += indentation.columns[i].percent;
4087 if ((indentation.columns[i].is_used) &&
4088 (indentation.columns[i].percent < PERCENT_THRESHOLD)) {
4089 used++;
4093 excess = 100-total;
4094 if (excess < used) {
4095 for (i=0; i<indentation.no_of_columns; i++) {
4096 if (! indentation.columns[i].is_used) {
4097 if (indentation.columns[i].percent > MIN_TEXT_PERCENT) {
4098 indentation.columns[i].percent--;
4099 excess++;
4104 if (excess >= used) {
4105 for (i=0; i<indentation.no_of_columns; i++) {
4106 if ((indentation.columns[i].is_used) &&
4107 (indentation.columns[i].percent < PERCENT_THRESHOLD) &&
4108 (indentation.columns[i].percent == (((indentation.columns[i].right - indentation.columns[i].left) * 100) /
4109 (right_margin_indent-left_margin_indent)))) {
4110 indentation.columns[i].percent++;
4111 excess--;
4114 return( TRUE );
4116 return( FALSE );
4120 * remove_table_column - removes column, i, from the indentation.
4123 void html_printer::remove_table_column (int i)
4125 while (i<indentation.no_of_columns) {
4126 indentation.columns[i].left = indentation.columns[i+1].left;
4127 indentation.columns[i].right = indentation.columns[i+1].right;
4128 indentation.columns[i].is_used = indentation.columns[i+1].is_used;
4129 indentation.columns[i].percent = indentation.columns[i+1].percent;
4130 i++;
4132 indentation.no_of_columns--;
4136 * next_line_on_left_column - returns TRUE if the next line in
4137 * column, i, has a word on the left margin.
4140 int html_printer::next_line_on_left_column (int i, text_glob *start)
4142 int current_vpos=start->minv;
4144 while ((start != 0) && (start->minv < indentation.vertical_limit) &&
4145 (is_on_same_line(start, current_vpos))) {
4146 if (page_contents->words.is_equal_to_tail()) {
4147 start = 0;
4148 } else {
4149 page_contents->words.move_right();
4150 start = page_contents->words.get_data();
4153 if ((start != 0) && (start->minv < indentation.vertical_limit)) {
4154 // onto next line now
4155 current_vpos=start->minv;
4156 while ((start != 0) && (start->minv < indentation.vertical_limit) &&
4157 (is_on_same_line(start, current_vpos))) {
4158 if (start->minh == indentation.columns[i].left) {
4159 return( TRUE );
4161 if (page_contents->words.is_equal_to_tail()) {
4162 start = 0;
4163 } else {
4164 page_contents->words.move_right();
4165 start = page_contents->words.get_data();
4169 return( FALSE );
4173 * will_wrap_text - returns TRUE if text is wrapped in column, i.
4176 int html_printer::will_wrap_text (int i, text_glob *start)
4178 text_glob *current=page_contents->words.get_data();
4180 if (auto_on) {
4181 rewind_text_to(start);
4182 while ((start != 0) && (start->minv < indentation.vertical_limit)) {
4183 if (indentation.columns[i].right == start->maxh) {
4184 // ok right word is on column boarder - check next line
4185 if (next_line_on_left_column(i, start)) {
4186 rewind_text_to(current);
4187 return( TRUE );
4190 if (page_contents->words.is_equal_to_tail()) {
4191 start = 0;
4192 } else {
4193 page_contents->words.move_right();
4194 start = page_contents->words.get_data();
4198 rewind_text_to(current);
4199 return( FALSE );
4203 * remove_unnecessary_unused - runs through a table and decides whether an unused
4204 * column can be removed. This is only true if the
4205 * column to the left does not wrap text.
4208 void html_printer::remove_unnecessary_unused (text_glob *start)
4210 int i=0;
4211 int left=get_left();
4212 int right;
4214 while (i<indentation.no_of_columns) {
4215 if ((indentation.columns[i].is_used) &&
4216 (i+1<indentation.no_of_columns) && (! indentation.columns[i+1].is_used)) {
4218 * so i+1 is unused and there is a used column to the left.
4219 * Now we check whether we can add the unused column to the column, i.
4220 * This can only be done if column, i, is not wrapping text.
4222 if (! will_wrap_text(i, start)) {
4223 #if 1
4224 if (i+1 < indentation.no_of_columns) {
4225 right = indentation.columns[i+1].right;
4226 } else {
4227 right = right_margin_indent;
4229 indentation.columns[i].percent = (((right - indentation.columns[i].left) * 100) /
4230 (right_margin_indent-left));
4231 #else
4232 indentation.columns[i].percent = (((indentation.columns[i+1].right - indentation.columns[i].left) * 100) /
4233 (right_margin_indent-left));
4234 #endif
4235 remove_table_column(i+1);
4236 i=-1;
4239 i++;
4244 * remove_zero_percentage_column - removes all zero percentage width columns
4247 void html_printer::remove_zero_percentage_column (void)
4249 int i=0;
4251 while (i<indentation.no_of_columns) {
4252 if (indentation.columns[i].percent == 0) {
4253 remove_table_column(i);
4254 i=0;
4255 } else {
4256 i++;
4262 * get_left - returns the actual left most margin.
4265 int html_printer::get_left (void)
4267 if ((header_indent < left_margin_indent) && (header_indent != -1)) {
4268 return( header_indent );
4269 } else {
4270 if (margin_on) {
4271 return( 0 );
4272 } else {
4273 return( left_margin_indent );
4279 * calculate_percentage_width - calculates the percentage widths,
4280 * this function will be generous to
4281 * columns which have words as some browsers
4282 * produce messy output if the percentage is exactly
4283 * that required for text..
4284 * We try and round up to MIN_TEXT_PERCENT
4285 * of course we can only do this if we can steal from
4286 * an unused column.
4289 void html_printer::calculate_percentage_width (text_glob *start)
4291 int i;
4292 int left=get_left();
4293 int right;
4295 // firstly calculate raw percentages
4296 for (i=0; i<indentation.no_of_columns; i++) {
4297 #if 0
4298 indentation.columns[i].percent = (((indentation.columns[i].right - indentation.columns[i].left) * 100) /
4299 (right_margin_indent-left));
4300 #else
4301 if (i+1 < indentation.no_of_columns) {
4302 right = indentation.columns[i+1].left;
4303 } else {
4304 right = right_margin_indent;
4306 indentation.columns[i].percent = (((right - indentation.columns[i].left) * 100) /
4307 (right_margin_indent-left));
4308 #endif
4310 if (debug_table_on) {
4311 display_columns(start->text_string, "[b4 steal] indentation.columns", indentation.columns);
4314 // now steal from the unused columns..
4315 remove_unnecessary_unused(start);
4317 if (debug_table_on) {
4318 display_columns(start->text_string, "[after steal] indentation.columns", indentation.columns);
4321 #if 0
4322 utilize_round_off();
4323 #endif
4324 remove_zero_percentage_column();
4329 * is_column_subset - returns TRUE if the columns described by small can be contained in
4330 * the columns in large.
4333 int html_printer::is_column_subset (struct text_defn *small, struct text_defn *large)
4335 int ns=count_columns(small);
4336 int nl=count_columns(large);
4337 int found;
4338 int i=0;
4339 int j;
4341 while (i<ns) {
4342 j=0;
4343 found = FALSE;
4344 while (j<nl) {
4345 if (is_intersection(small[i].left, small[i].right, large[j].left, large[j].right)) {
4346 found = TRUE;
4347 if (! is_subsection(small[i].left, small[i].right, large[j].left, large[j].right)) {
4348 // found column which is not a subset
4349 return( FALSE );
4352 j++;
4354 if (! found) {
4355 return( FALSE );
4357 i++;
4359 // small cannot be an empty set
4360 return( ns>0 );
4364 * right_most_column - returns the right most column position.
4367 int html_printer::right_most_column (struct text_defn *col)
4369 int i = count_columns(col);
4371 if (i>0) {
4372 return( col[i-1].right );
4373 } else {
4374 return( 0 );
4379 * large_enough_gap_for_two - returns TRUE if there exists a large enough gap
4380 * for two lines.
4383 int html_printer::large_enough_gap_for_two (struct text_defn *col)
4385 int i=0;
4386 int found=FALSE;
4387 int gap=MIN_COLUMN_FOR_TWO_LINES;
4389 if (abs(col[i].left - left_margin_indent) >= gap) {
4390 found = TRUE;
4392 while ((col[i].right != 0) && (col[i+1].right != 0)) {
4393 if (abs(col[i+1].left-col[i].right) >= gap) {
4394 found = TRUE;
4395 i++;
4396 } else {
4397 // not good enough for this table, remove it
4398 col[i].right = col[i+1].right;
4399 remove_entry_in_line(col, i+1);
4402 return( found );
4406 * is_small_table - applies some rigorous rules to test whether we should start this
4407 * table at this point.
4410 int html_printer::is_small_table (int lines, struct text_defn *last_guess,
4411 struct text_defn *words_1, struct text_defn *cols_1,
4412 struct text_defn *words_2, struct text_defn *cols_2,
4413 int *limit, int *limit_1)
4416 * firstly we check for an indented paragraph
4419 if ((lines >= 2) &&
4420 (count_columns(cols_1) == count_columns(cols_2)) && (count_columns(cols_1) == 1) &&
4421 right_indentation(cols_1) && (! right_indentation(cols_2)) &&
4422 (cols_1[0].right == right_margin_indent)) {
4423 return( FALSE );
4426 if (lines == 2) {
4428 * as we only have two lines in our table we need to examine in detail whether
4429 * we should construct a table from these two lines.
4430 * For example if the text is the start of an indented paragraph and
4431 * line1 and line2 are contineous then they should form one row in our table but
4432 * if line1 and line2 are not contineous it is safer to treat them separately.
4434 * We are prepared to reduce the table to one line
4436 if (((count_columns(cols_1) != count_columns(cols_2)) && (cols_1[0].left > cols_2[0].left)) ||
4437 (! ((is_column_subset(cols_1, cols_2)) ||
4438 (is_column_subset(cols_2, cols_1))))) {
4440 * now we must check to see whether line1 and line2 join
4442 if ((right_most_column(cols_1) == right_margin_indent) &&
4443 (cols_2[0].left == left_margin_indent)) {
4445 * looks like they join, we don't want a table at all.
4447 return( FALSE );
4450 * use single line table
4452 lines--;
4453 *limit = *limit_1;
4454 copy_line(last_guess, cols_1);
4458 if ((count_columns(last_guess)==1) && (right_indentation(last_guess))) {
4459 if (lines == 1) {
4460 *limit = *limit_1;
4462 return( TRUE );
4466 * check for large gap with single line or if multiple lines with more than one column
4469 if (lines == 1) {
4470 if (large_enough_gap(last_guess)) {
4471 *limit = *limit_1;
4472 return( TRUE );
4474 } else if (count_columns(last_guess)>1) {
4475 if (lines == 2) {
4476 return( large_enough_gap_for_two(last_guess) );
4478 return( TRUE );
4480 return( FALSE );
4485 * is_appropriate_to_start_table - returns TRUE if it is appropriate to start the table
4486 * at this point.
4489 int html_printer::is_appropriate_to_start_table (struct text_defn *cols_1,
4490 struct text_defn *cols_2,
4491 struct text_defn *last_guess)
4493 if (count_columns(last_guess) == 1) {
4494 if (debug_table_on) {
4495 display_columns("", "[is] cols_1" , cols_1);
4496 display_columns("", "[is] cols_2" , cols_2);
4497 display_columns("", "[is] last_guess", last_guess);
4500 if (! ((is_column_subset(cols_1, cols_2)) ||
4501 (is_column_subset(cols_2, cols_1)))) {
4502 return( FALSE );
4504 if ((count_columns(cols_1) == 1) &&
4505 (cols_1[0].left > left_margin_indent) && (cols_1[0].right < right_margin_indent) &&
4506 (cols_1[0].right != cols_2[0].right) &&
4507 (count_columns(last_guess) == 1)) {
4508 return( FALSE );
4511 return( TRUE );
4515 * is_a_full_width_column - returns TRUE if there exists a full width column.
4518 int html_printer::is_a_full_width_column (void)
4520 int i=0;
4522 while (i<indentation.no_of_columns) {
4523 if (((indentation.columns[i].left == get_left()) ||
4524 (indentation.columns[i].left == left_margin_indent)) &&
4525 (indentation.columns[i].right == right_margin_indent)) {
4526 return( TRUE );
4528 i++;
4530 return( FALSE );
4534 * should_defer_table - returns TRUE if we should defer this table.
4535 * This can occur if the first line seen indent
4536 * is < than future lines. In which case it
4537 * will cause future lines in this table
4538 * to be indented. The lesser of the evils
4539 * is to treat the first line by itself.
4542 int html_printer::should_defer_table (int lines, struct text_glob *start, struct text_defn *cols_1)
4544 if (lines > 2) {
4545 int i=0;
4546 int c=count_columns(cols_1);
4548 count_hits(cols_1, count_columns(cols_1), indentation.vertical_limit);
4549 rewind_text_to(start);
4550 count_right_hits(cols_1, count_columns(cols_1));
4551 rewind_text_to(start);
4552 while (i<c) {
4553 if ((cols_1[i].is_used > 1) || (cols_1[i].right_hits > 1)) {
4554 return( FALSE );
4556 i++;
4559 * first line (cols_1) is not aligned on any future column, we defer.
4561 return( TRUE );
4563 return( FALSE );
4567 * is_new_exact_right - returns TRUE if the, next_cols, has a word sitting
4568 * on the right hand margin of last_guess. But only
4569 * if no exact right word was found in last_cols.
4572 int html_printer::is_new_exact_right (struct text_defn *last_guess,
4573 struct text_defn *last_cols,
4574 struct text_defn *next_cols)
4576 int n=count_columns(last_guess)-1;
4577 return( FALSE );
4579 if ((n>=0) && (last_guess[n].right != 0) && (last_cols[n].right != 0) && (next_cols[n].right != 0)) {
4580 if ((last_cols[n].right != last_guess[n].right) &&
4581 ((next_cols[n].right == last_guess[n].right) || (next_cols[n].right == right_margin_indent))) {
4582 return( TRUE );
4585 return( FALSE );
4589 * found_use_for_table - checks whether the some words on one line directly match
4590 * the horizontal alignment of the line below.
4591 * This is rather complex as we need to detect text tables
4592 * such as .2C .IP Abstracts and indentations
4594 * Algorithm is:
4596 * read first line of text and calculate the significant
4597 * gaps between words
4598 * next next line of text and do the same
4599 * if a conflict between these lines exists and
4600 * first line is centered
4601 * then
4602 * return centered line
4603 * elsif start of a table is found
4604 * then
4605 * repeat
4606 * read next line of text and calculate significant gaps
4607 * until conflict between the gaps is found
4608 * record table
4609 * return table found
4610 * else
4611 * return no table found
4612 * fi
4615 int html_printer::found_use_for_table (text_glob *start)
4617 text_glob *t;
4618 struct text_defn all_words [MAX_WORDS_PER_LINE]; // logical OR of words on each line
4619 struct text_defn words_1 [MAX_WORDS_PER_LINE]; // actual words found on first line
4620 struct text_defn words_2 [MAX_WORDS_PER_LINE]; // actual words found on second line
4621 struct text_defn cols_1 [MAX_WORDS_PER_LINE]; // columns found on line 1
4622 struct text_defn cols_2 [MAX_WORDS_PER_LINE]; // columns found on line 2
4623 struct text_defn last_words [MAX_WORDS_PER_LINE]; // actual words found on last line
4624 struct text_defn last_cols [MAX_WORDS_PER_LINE]; // columns found so far
4625 struct text_defn next_words [MAX_WORDS_PER_LINE]; // actual words found on last line (new)
4626 struct text_defn next_cols [MAX_WORDS_PER_LINE]; // columns found on next line
4627 struct text_defn last_guess [MAX_WORDS_PER_LINE]; // columns found on last line
4628 // (logical AND of gaps (treat gaps = true))
4629 struct text_defn next_guess [MAX_WORDS_PER_LINE]; // columns found on next line
4630 // (logical AND of gaps (treat gaps = true))
4631 struct text_defn prev_guess [MAX_WORDS_PER_LINE]; // temporary copy of last_guess
4632 int i =0;
4633 int lines =1; // number of lines read
4634 int limit; // vertical limit reached in our table
4635 int limit_1; // vertical position after line 1
4637 #if 0
4638 if (strcmp(start->text_string, "<hr>") == 0) {
4639 stop();
4641 #endif
4644 * get first set of potential columns into last_line, call this last_guess
4646 limit = collect_columns(words_1, cols_1, 0, 0, MAX_WORDS_PER_LINE);
4647 limit_1 = limit;
4648 copy_line(last_guess, cols_1);
4651 * initialize the all_words columns - if this should ever equal a complete line
4652 * with no gaps then we terminate the table.
4654 copy_line(all_words, cols_1);
4657 * and set the current limit found
4659 indentation.vertical_limit = limit;
4662 * have we reached the end of page?
4664 if (page_contents->words.is_equal_to_head() || (limit == 0)) {
4665 cols_2[0].left = 0;
4666 cols_2[0].right = 0;
4667 } else {
4669 * the answer to the previous question was no.
4670 * So we need to examine the next line
4672 limit = collect_columns(words_2, cols_2, words_1, cols_1, MAX_WORDS_PER_LINE);
4673 if (limit >= 0) {
4674 lines++;
4679 * now check to see whether the first line looks like a single centered line
4681 if (single_centered_line(cols_1, cols_2, start)) {
4682 rewind_text_to(start);
4683 write_centered_line(start);
4685 * indicate to caller than we have centered text, not found a table.
4687 indentation.no_of_columns = 0;
4688 return( TRUE );
4689 } else if (! table_on) {
4691 * user does not allow us to find a table (we are allowed to find centered lines (above))
4693 rewind_text_to(start);
4694 return( FALSE );
4698 * remove any gaps from all_words
4700 combine_line(all_words, cols_2);
4701 if (debug_table_on) {
4702 display_columns(start->text_string, "[1] all_words" , all_words);
4703 display_columns(start->text_string, "[1] cols_1" , cols_1);
4704 display_columns(start->text_string, "[1] words_1" , words_1);
4705 display_columns(start->text_string, "[1] cols_2" , cols_2);
4706 display_columns(start->text_string, "[1] words_2" , words_2);
4707 display_columns(start->text_string, "[1] last_guess", last_guess);
4711 * next_guess = last_guess AND next_cols (where gap = true)
4714 if (remove_white_using_words(prev_guess, last_guess, cols_2)) {
4716 if (remove_white_using_words(next_guess, prev_guess, all_words)) {
4719 if (debug_table_on) {
4720 display_columns(start->text_string, "[2] next_guess", next_guess);
4723 copy_line(prev_guess, cols_1);
4724 combine_line(prev_guess, cols_2);
4727 * if no sequence of words crosses a column and
4728 * both the last column and all_words are not a full solid line of text
4730 if ((! conflict_with_words(next_guess, all_words)) &&
4731 (continue_searching_column(next_guess, next_guess, all_words)) &&
4732 (is_appropriate_to_start_table(cols_1, cols_2, prev_guess)) &&
4733 (! page_contents->words.is_equal_to_head()) &&
4734 ((end_region_vpos < 0) || (limit < end_region_vpos)) &&
4735 (limit > 0)) {
4738 * subtract any columns which are bridged by a sequence of words
4741 copy_line(next_cols , cols_2);
4742 copy_line(next_words, words_2);
4744 do {
4745 copy_line(prev_guess, next_guess); // copy next_guess away so we can compare it later
4746 combine_line(last_guess, next_guess);
4748 if (debug_table_on) {
4749 t = page_contents->words.get_data();
4750 display_columns(t->text_string, "[l] last_guess", last_guess);
4752 indentation.vertical_limit = limit;
4754 copy_line(last_cols, next_cols);
4755 copy_line(last_words, next_words);
4756 if (page_contents->words.is_equal_to_head()) {
4758 * terminate the search
4760 next_cols[0].left = 0;
4761 next_cols[0].right = 0;
4762 } else {
4763 limit = collect_columns(next_words, next_cols, last_words, last_cols, MAX_WORDS_PER_LINE);
4764 lines++;
4767 combine_line(all_words, next_cols);
4768 if (debug_table_on) {
4769 display_columns(t->text_string, "[l] all_words" , all_words);
4770 display_columns(t->text_string, "[l] last_cols" , last_cols);
4771 display_columns(t->text_string, "[l] next_words", next_words);
4772 display_columns(t->text_string, "[l] next_cols" , next_cols);
4775 if (limit >= 0) {
4777 * (if limit is < 0 then the table ends anyway.)
4778 * we check to see whether we should combine close columns.
4780 can_loose_column(start, last_guess, limit);
4782 t = page_contents->words.get_data();
4783 #if 0
4784 if (strcmp(t->text_string, "heT") == 0) {
4785 stop();
4787 #endif
4789 } while ((! remove_white_using_words(next_guess, last_guess, next_cols)) &&
4790 (! conflict_with_words(next_guess, all_words)) &&
4791 (continue_searching_column(next_guess, last_guess, all_words)) &&
4792 ((is_continueous_column(prev_guess, last_cols)) || (is_exact_left(last_guess, next_cols))) &&
4793 (! is_new_exact_right(last_guess, last_cols, next_cols)) &&
4794 (! page_contents->words.is_equal_to_head()) &&
4795 (! check_lack_of_hits(next_guess, last_guess, start, limit)) &&
4796 ((end_region_vpos <= 0) || (t->minv < end_region_vpos)) &&
4797 (limit >= 0));
4798 lines--;
4801 if (limit < 0) {
4802 indentation.vertical_limit = limit;
4805 if (page_contents->words.is_equal_to_head()) {
4806 // end of page check whether we should include everything
4807 if ((! conflict_with_words(next_guess, all_words)) &&
4808 (continue_searching_column(next_guess, last_guess, all_words)) &&
4809 ((is_continueous_column(prev_guess, last_cols)) || (is_exact_left(last_guess, next_cols)))) {
4810 // end of page reached - therefore include everything
4811 page_contents->words.start_from_tail();
4812 t = page_contents->words.get_data();
4813 combine_line(last_guess, next_guess);
4814 indentation.vertical_limit = t->minv;
4816 } else {
4817 t = page_contents->words.get_data();
4818 if (((! conflict_with_words(last_guess, all_words))) &&
4819 (t->minv > end_region_vpos) && (end_region_vpos > 0)) {
4820 indentation.vertical_limit = limit;
4822 if ((end_region_vpos > 0) && (t->minv > end_region_vpos)) {
4823 indentation.vertical_limit = min(indentation.vertical_limit, end_region_vpos+1);
4824 } else if (indentation.vertical_limit < 0) {
4825 // -1 as we don't want to include section heading itself
4826 indentation.vertical_limit = -indentation.vertical_limit-1;
4830 if (debug_table_on) {
4831 display_columns(start->text_string, "[1] all_words" , all_words);
4832 display_columns(start->text_string, "[1] cols_1" , cols_1);
4833 display_columns(start->text_string, "[1] words_1" , words_1);
4834 display_columns(start->text_string, "[1] cols_2" , cols_2);
4835 display_columns(start->text_string, "[1] words_2" , words_2);
4836 display_columns(start->text_string, "[1] last_guess", last_guess);
4837 display_columns(start->text_string, "[1] next_guess", next_guess);
4839 rewind_text_to(start);
4841 i = count_columns(last_guess);
4842 if ((i>1) || (right_indentation(last_guess))) {
4844 // was (continue_searching_column(last_guess, last_guess, all_words)))) {
4845 if (should_defer_table(lines, start, cols_1)) {
4847 * yes, but let us check for a single line table
4849 lines = 1;
4850 copy_line(last_guess, cols_1);
4853 if (is_small_table(lines, last_guess, words_1, cols_1, words_2, cols_2,
4854 &indentation.vertical_limit, &limit_1)) {
4856 // copy match into permenant html_table
4858 if (indentation.columns != 0) {
4859 free(indentation.columns);
4861 if (debug_table_on) {
4862 display_columns(start->text_string, "[x] last_guess", last_guess);
4864 add_column_gaps(last_guess);
4865 if (debug_table_on) {
4866 display_columns(start->text_string, "[g] last_guess", last_guess);
4870 * +1 for the potential header_margin
4871 * +1 for null
4874 indentation.no_of_columns = count_columns(last_guess);
4875 indentation.columns = (struct text_defn *)malloc((indentation.no_of_columns+2)*sizeof(struct text_defn));
4877 i=0;
4878 while (i<=indentation.no_of_columns) {
4879 indentation.columns[i].left = last_guess[i].left;
4880 indentation.columns[i].right = last_guess[i].right;
4881 i++;
4884 if (indentation.no_of_columns>0) {
4885 assign_used_columns(start);
4886 rewind_text_to(start);
4887 calculate_percentage_width(start);
4889 if (debug_table_on) {
4890 display_columns(start->text_string, "[g] indentation.columns", indentation.columns);
4894 * clearly a single column 100% is not worth using a table.
4895 * Also we check to see whether the first line is sensibly
4896 * part of this table.
4898 if (is_a_full_width_column()) {
4899 indentation.no_of_columns = 0;
4900 free( indentation.columns );
4901 indentation.columns = 0;
4902 } else {
4903 return( TRUE );
4908 return( FALSE );
4912 * define_cell - creates a table cell using the percentage width.
4915 void html_printer::define_cell (int i)
4917 html.put_string("<td valign=\"top\" align=\"left\" width=\"");
4918 html.put_number(indentation.columns[i].percent);
4919 html.put_string("%\">\n");
4923 * column_display_word - given a left, right pair and the indentation.vertical_limit
4924 * write out html text within this region.
4927 void html_printer::column_display_word (int cell, int vert, int left, int right, int next)
4929 text_glob *g=page_contents->words.get_data();
4931 supress_sub_sup = TRUE;
4932 if (left != next) {
4933 define_cell(cell);
4934 begin_paragraph_no_height(left_alignment);
4935 while ((g != 0) && (g->minv <= vert)) {
4936 if ((left <= g->minh) && (g->minh<right)) {
4937 char *postword=html_position_text(g, left, right);
4939 if (header.written_header) {
4940 fatal("should never generate a header inside a table");
4941 } else {
4942 if (g->is_raw_command) {
4943 html.put_string((char *)g->text_string);
4944 } else {
4945 translate_to_html(g);
4947 if (postword != 0) {
4948 html.put_string(postword);
4950 issued_newline = FALSE;
4953 if (page_contents->words.is_equal_to_tail()) {
4954 g = 0;
4955 } else {
4956 page_contents->words.move_right();
4957 g=page_contents->words.get_data();
4960 end_paragraph();
4961 html.put_string("</td>\n");
4962 if (g != 0) {
4963 page_contents->words.move_left();
4964 // and correct output_vpos
4965 g=page_contents->words.get_data();
4966 output_vpos = g->minv;
4972 * total_percentages - returns the total of all the percentages in the table.
4975 int html_printer::total_percentages ()
4977 int i;
4978 int sum=0;
4980 for (i=0; i<indentation.no_of_columns; i++) {
4981 sum += indentation.columns[i].percent;
4983 return( sum );
4987 * start_table - creates a table according with parameters contained within class html_table.
4990 void html_printer::start_table (void)
4992 save_paragraph();
4993 html.put_string("\n<table width=\"");
4994 html.put_number(total_percentages());
4995 html.put_string("%\" rules=\"none\" frame=\"none\" cols=\"");
4996 html.put_number(indentation.no_of_columns);
4997 html.put_string("\" cellspacing=\"0\" cellpadding=\"0\">\n");
5001 * end_table - finishes off a table.
5004 void html_printer::end_table (void)
5006 html.put_string("</table>\n");
5007 indentation.no_of_columns = 0;
5008 restore_paragraph();
5009 supress_sub_sup = TRUE;
5014 * is_in_table - returns TRUE if we are inside an html table.
5017 int html_printer::is_in_table (void)
5019 return( indentation.no_of_columns != 0 );
5024 * column_calculate_right_margin - scan through the column and find the right most margin
5027 int html_printer::column_calculate_right_margin (int left, int right)
5029 if (left == right) {
5030 return( right );
5031 } else {
5032 int rightmost =-1;
5033 int count = 0;
5034 text_glob *start = page_contents->words.get_data();
5035 text_glob *g = start;
5037 while ((g != 0) && (g->minv <= indentation.vertical_limit)) {
5038 if ((left <= g->minh) && (g->minh<right)) {
5039 if (debug_on) {
5040 fprintf(stderr, "right word = %s %d\n", g->text_string, g->maxh); fflush(stderr);
5042 if (g->maxh == rightmost) {
5043 count++;
5044 } else if (g->maxh > rightmost) {
5045 count = 1;
5046 rightmost = g->maxh;
5048 if (g->maxh > right) {
5049 if (debug_on) {
5050 fprintf(stderr, "problem as right word = %s %d [%d..%d]\n",
5051 g->text_string, right, g->minh, g->maxh); fflush(stderr);
5052 // stop();
5056 page_contents->words.move_right();
5057 if (page_contents->words.is_equal_to_head()) {
5058 g = 0;
5059 page_contents->words.start_from_tail();
5060 } else {
5061 g=page_contents->words.get_data();
5064 rewind_text_to(start);
5065 if (rightmost == -1) {
5066 return( right ); // no words in this column
5067 } else {
5068 return( rightmost );
5074 * column_calculate_left_margin - scan through the column and find the left most margin
5077 int html_printer::column_calculate_left_margin (int left, int right)
5079 if (left == right) {
5080 return( left );
5081 } else {
5082 int leftmost=right;
5083 text_glob *start = page_contents->words.get_data();
5084 text_glob *g = start;
5086 while ((g != 0) && (g->minv <= indentation.vertical_limit)) {
5087 if ((left <= g->minh) && (g->minh<right)) {
5088 leftmost = min(g->minh, leftmost);
5090 page_contents->words.move_right();
5091 if (page_contents->words.is_equal_to_head()) {
5092 g = 0;
5093 page_contents->words.start_from_tail();
5094 } else {
5095 g=page_contents->words.get_data();
5098 rewind_text_to(start);
5099 if (leftmost == right) {
5100 return( left ); // no words in this column
5101 } else {
5102 return( leftmost );
5108 * find_column_index - returns the index to the column in which glob, t, exists.
5111 int html_printer::find_column_index_in_line (text_glob *t, text_defn *line)
5113 int i=0;
5115 while ((line != 0) && ((line[i].right != 0) || (line[i].right != 0)) &&
5116 (! ((line[i].left<=t->minh) && (line[i].right>t->minh)))) {
5117 i++;
5119 return( i );
5123 * find_column_index - returns the index to the column in which glob, t, exists.
5126 int html_printer::find_column_index (text_glob *t)
5128 int i=0;
5130 while ((i<indentation.no_of_columns) &&
5131 (! ((indentation.columns[i].left<=t->minh) &&
5132 (indentation.columns[i].right>t->minh)))) {
5133 i++;
5135 return( i );
5139 * determine_row_limit - checks each row to see if there is a gap in a cell.
5140 * We return the vertical position after the empty cell
5141 * at the start of the next line.
5144 int html_printer::determine_row_limit (text_glob *start, int v)
5146 text_glob *t;
5147 int i;
5148 int vpos, last, prev;
5149 text_glob *is_gap[MAX_WORDS_PER_LINE];
5150 text_glob zero(&start->text_style, 0, 0, 0, 0, 0, 0, 0, 0);
5152 #if 1
5153 if ((v == -1) && (strcmp(start->text_string, "CASE") == 0)) {
5154 stop();
5156 #endif
5158 if (v >= indentation.vertical_limit) {
5159 return( v+1 );
5160 } else {
5162 * initially we start with all gaps in our table
5163 * after a gap we start a new row
5164 * here we set the gap array to the previous line
5167 if (v>=0) {
5168 t = page_contents->words.get_data();
5169 if (t->minv < v) {
5170 do {
5171 page_contents->words.move_right();
5172 t = page_contents->words.get_data();
5173 } while ((! page_contents->words.is_equal_to_head()) &&
5174 (t->minv <= v));
5177 if (page_contents->words.is_equal_to_head()) {
5178 t = &zero;
5179 } else {
5180 page_contents->words.move_left();
5181 t = page_contents->words.get_data();
5184 prev = t->minv;
5185 for (i=0; i<indentation.no_of_columns; i++) {
5186 is_gap[i] = t;
5189 if (page_contents->words.is_equal_to_tail()) {
5190 rewind_text_to(start);
5191 return( indentation.vertical_limit );
5192 } else {
5193 page_contents->words.move_right();
5195 t = page_contents->words.get_data();
5196 vpos = t->minv;
5198 // now check each row for a gap
5199 do {
5200 last = vpos;
5201 vpos = t->minv;
5202 if (vpos > indentation.vertical_limit) {
5203 // we have reached the end of the table, quit
5204 rewind_text_to(start);
5205 return( indentation.vertical_limit );
5208 i = find_column_index(t);
5209 if (i>=indentation.no_of_columns) {
5210 error("find_column_index has failed");
5211 stop();
5212 } else {
5213 if (! is_on_same_line(t, last)) {
5214 prev = last;
5217 if ((! is_on_same_line(is_gap[i], vpos)) && (! is_on_same_line(is_gap[i], prev)) &&
5218 (indentation.columns[i].is_used)) {
5219 // no word on previous line - must be a gap - force alignment of row
5220 rewind_text_to(start);
5221 return( prev );
5223 is_gap[i] = t;
5225 page_contents->words.move_right();
5226 t = page_contents->words.get_data();
5227 } while ((! page_contents->words.is_equal_to_head()) &&
5228 (vpos < indentation.vertical_limit) && (vpos >= last));
5229 page_contents->words.move_left();
5230 t = page_contents->words.get_data();
5231 rewind_text_to(start);
5232 return( indentation.vertical_limit );
5237 * assign_used_columns - sets the is_used field of the column array of records.
5240 void html_printer::assign_used_columns (text_glob *start)
5242 text_glob *t = start;
5243 int i;
5245 for (i=0; i<indentation.no_of_columns; i++) {
5246 indentation.columns[i].is_used = FALSE;
5249 rewind_text_to(start);
5250 if (! page_contents->words.is_empty()) {
5251 do {
5252 i = find_column_index(t);
5253 if (indentation.columns[i].right != 0) {
5254 if (debug_table_on) {
5255 fprintf(stderr, "[%s] in column %d at %d..%d limit %d\n", t->text_string,
5256 i, t->minv, t->maxv, indentation.vertical_limit); fflush(stderr);
5258 indentation.columns[i].is_used = TRUE;
5260 page_contents->words.move_right();
5261 t = page_contents->words.get_data();
5262 } while ((t->minv<indentation.vertical_limit) &&
5263 (! page_contents->words.is_equal_to_head()));
5265 if (debug_table_on) {
5266 for (i=0; i<indentation.no_of_columns; i++) {
5267 fprintf(stderr, " <left=%d right=%d is_used=%d> ",
5268 indentation.columns[i].left,
5269 indentation.columns[i].right,
5270 indentation.columns[i].is_used);
5272 fprintf(stderr, "\n");
5273 fflush(stderr);
5278 * adjust_margin_percentages - so far we have ignored the header_indent
5279 * and just considered left_margin_indent..right_margin_indent.
5280 * (We do this since we can assume 100% is total width for main text).
5281 * However as header_indent can be < left_margin_indent we need to
5282 * recalculate the real percentages in the light of the extended width.
5285 void html_printer::adjust_margin_percentages (void)
5287 if ((header_indent < left_margin_indent) && (header_indent != -1)) {
5289 * recalculation necessary
5291 int i=0;
5293 while (i<indentation.no_of_columns) {
5294 indentation.columns[i].percent = (indentation.columns[i].percent *
5295 (right_margin_indent - left_margin_indent)) /
5296 (right_margin_indent - header_indent);
5297 i++;
5299 // remove_zero_percentage_column();
5304 * foreach_column_include_text - foreach column in a table place the
5305 * appropriate html text.
5308 void html_printer::foreach_column_include_text (text_glob *start)
5310 if (indentation.no_of_columns>0) {
5311 int i;
5312 int left, right;
5313 int limit=-1;
5315 start_table();
5316 rewind_text_to(start);
5317 count_right_hits(indentation.columns, indentation.no_of_columns);
5318 rewind_text_to(start);
5320 do {
5321 limit = determine_row_limit(start, limit); // find the bottom of the next row
5322 html.put_string("<tr valign=\"top\" align=\"left\">\n");
5323 i=0;
5324 start = page_contents->words.get_data();
5325 while (i<indentation.no_of_columns) {
5326 // reset the output position to the start of column
5327 rewind_text_to(start);
5328 output_vpos = start->minv;
5329 output_hpos = indentation.columns[i].left;
5330 // and display each column until limit
5331 right = column_calculate_right_margin(indentation.columns[i].left,
5332 indentation.columns[i].right);
5333 left = column_calculate_left_margin(indentation.columns[i].left,
5334 indentation.columns[i].right);
5336 if (right>indentation.columns[i].right) {
5337 if (debug_on) {
5338 fprintf(stderr, "assert calculated right column edge is greater than column\n"); fflush(stderr);
5339 // stop();
5343 if (left<indentation.columns[i].left) {
5344 if (debug_on) {
5345 fprintf(stderr, "assert calculated left column edge is less than column\n"); fflush(stderr);
5346 // stop();
5350 if ((indentation.columns[i].right_hits == 1) &&
5351 (indentation.columns[i].right != right_margin_indent)) {
5352 indentation.wrap_margin = FALSE;
5353 if (debug_on) {
5354 fprintf(stderr, "turning auto wrap off during column %d for start word %s\n",
5355 i, start->text_string);
5356 fflush(stderr);
5357 // stop();
5359 } else {
5360 indentation.wrap_margin = TRUE;
5363 column_display_word(i, limit, left, right, indentation.columns[i].right);
5364 i++;
5367 if (page_contents->words.is_equal_to_tail()) {
5368 start = 0;
5369 } else {
5370 page_contents->words.sub_move_right();
5371 if (page_contents->words.is_empty()) {
5372 start = 0;
5373 } else {
5374 start = page_contents->words.get_data();
5378 html.put_string("</tr>\n");
5379 } while (((limit < indentation.vertical_limit) && (start != 0) &&
5380 (! page_contents->words.is_empty())) || (limit == -1));
5381 end_table();
5383 if (start == 0) {
5384 // finished page remove all words
5385 page_contents->words.start_from_head();
5386 while (! page_contents->words.is_empty()) {
5387 page_contents->words.sub_move_right();
5389 } else if (! page_contents->words.is_empty()) {
5390 page_contents->words.move_left();
5396 * write_centered_line - generates a line of centered text.
5399 void html_printer::write_centered_line (text_glob *g)
5401 int current_vpos=g->minv;
5403 move_vertical(g, center_alignment);
5405 header.written_header = FALSE;
5406 supress_sub_sup = TRUE;
5407 output_vpos = g->minv;
5408 output_hpos = g->minh;
5409 do {
5410 char *postword=html_position_text(g, left_margin_indent, right_margin_indent);
5412 if (! header.written_header) {
5413 if (g->is_raw_command) {
5414 html.put_string((char *)g->text_string);
5415 } else {
5416 translate_to_html(g);
5418 if (postword != 0) {
5419 html.put_string(postword);
5421 need_one_newline = TRUE;
5422 issued_newline = FALSE;
5424 page_contents->words.move_right();
5425 g = page_contents->words.get_data();
5426 } while ((! page_contents->words.is_equal_to_head()) && (is_on_same_line(g, current_vpos)));
5427 page_contents->words.move_left(); // so when we move right we land on the word following this centered line
5428 need_one_newline = TRUE;
5432 * is_in_middle - returns TRUE if the text defn, t, is in the middle of the page.
5435 int html_printer::is_in_middle (int left, int right)
5437 return( abs(abs(left-left_margin_indent) - abs(right_margin_indent-right)) <= CENTER_TOLERANCE );
5441 * single_centered_line - returns TRUE if first is a centered line with a different
5442 * margin to second.
5445 int html_printer::single_centered_line (text_defn *first, text_defn *second, text_glob *g)
5447 return(
5448 ((count_columns(first) == 1) && (first[0].left != left_margin_indent) &&
5449 (first[0].left != second[0].left) && is_in_middle(first->left, first->right))
5454 * check_able_to_use_center - returns TRUE if we can see a centered line.
5457 int html_printer::check_able_to_use_center (text_glob *g)
5459 if (auto_on && table_on && ((! is_on_same_line(g, output_vpos)) || issued_newline) && (! using_table_for_indent())) {
5460 // we are allowed to check for centered line
5461 // first check to see whether we might be looking at a set of columns
5462 struct text_defn last_guess[MAX_WORDS_PER_LINE];
5463 struct text_defn last_words[MAX_WORDS_PER_LINE];
5465 collect_columns(last_words, last_guess, 0, 0, MAX_WORDS_PER_LINE);
5467 rewind_text_to(g);
5468 if ((count_columns(last_guess) == 1) && (is_in_middle(last_guess[0].left, last_guess[0].right))) {
5469 write_centered_line(g);
5470 return( TRUE );
5473 return( FALSE );
5477 * check_able_to_use_table - examines forthcoming text to see whether we can
5478 * better format it by using an html transparent table.
5481 int html_printer::check_able_to_use_table (text_glob *g)
5483 if (auto_on && ((! is_on_same_line(g, output_vpos)) || issued_newline) && (! using_table_for_indent())) {
5484 // we are allowed to check for table
5486 if ((output_hpos != right_margin_indent) && (found_use_for_table(g))) {
5487 foreach_column_include_text(g);
5488 return( TRUE );
5491 return( FALSE );
5495 * move_vertical - if we are using html auto formatting then decide whether to
5496 * break the line via a <br> or a </p><p> sequence.
5499 void html_printer::move_vertical (text_glob *g, paragraph_type p)
5501 int r = font::res;
5502 int height = (g->text_style.point_size+2)*r/72; // --fixme-- we always assume VS is PS+2 (could do better)
5503 int temp_vpos;
5505 if (auto_on) {
5506 if ((more_than_line_break(output_vpos, g->minv, height)) || (p != current_paragraph->para_type)) {
5507 end_paragraph();
5508 begin_paragraph(p);
5509 } else {
5510 html_newline();
5512 } else {
5513 if (output_vpos == -1) {
5514 temp_vpos = g->minv;
5515 } else {
5516 temp_vpos = output_vpos;
5519 force_begin_paragraph();
5520 if (need_one_newline) {
5521 html_newline();
5522 temp_vpos += height;
5523 } else {
5524 need_one_newline = TRUE;
5527 while ((temp_vpos < g->minv) && (more_than_line_break(temp_vpos, g->minv, height))) {
5528 html_newline();
5529 temp_vpos += height;
5535 * emit_space - emits a space within html, it checks for the font type and
5536 * will change font depending upon, g. Courier spaces are larger
5537 * than roman so we need consistancy when changing between them.
5540 void html_printer::emit_space (text_glob *g, int force_space)
5542 if (! current_paragraph->need_paragraph) {
5543 // only generate a space if we have written a word - as html will ignore it otherwise
5544 if ((output_style != g->text_style) && (g->text_style.f != 0)) {
5545 terminate_current_font();
5547 if (force_space || (g->minh > output_hpos)) {
5548 html.put_string(" ");
5550 change_font(g, TRUE);
5555 * html_position_text - determine whether the text is subscript/superscript/normal
5556 * or a header.
5559 char *html_printer::html_position_text (text_glob *g, int left_margin, int right_margin)
5561 char *postword=0;
5563 begin_paragraph(left_alignment);
5565 if ((! header.written_header) &&
5566 (is_on_same_line(g, output_vpos) ||
5567 pretend_is_on_same_line(g, left_margin, right_margin))) {
5570 * check whether we should supress superscripts and subscripts.
5571 * I guess we might be able to do better by examining text on this line
5572 * --fixme--
5575 if ((! is_on_same_line(g, output_vpos)) && (pretend_is_on_same_line(g, left_margin, right_margin))) {
5576 supress_sub_sup = TRUE;
5578 header.written_header = FALSE;
5579 force_begin_paragraph();
5581 // check whether we need to insert white space between words on 'same' line
5582 if (pretend_is_on_same_line(g, left_margin, right_margin)) {
5583 emit_space(g, TRUE);
5586 // check whether the font was reset after generating an image
5587 if (output_style.f == 0) {
5588 change_font(g, TRUE);
5591 if (looks_like_subscript(g)) {
5593 g->text_style.point_size = output_style.point_size;
5594 g->minv = output_vpos; // this ensures that output_vpos doesn't alter
5595 // which allows multiple subscripted words
5596 move_horizontal(g, left_margin);
5597 html.put_string("<sub>");
5598 postword = "</sub>";
5599 } else if (looks_like_superscript(g)) {
5601 g->text_style.point_size = output_style.point_size;
5602 g->minv = output_vpos;
5604 move_horizontal(g, left_margin);
5605 html.put_string("<sup>");
5606 postword = "</sup>";
5607 } else {
5608 move_horizontal(g, left_margin);
5610 supress_sub_sup = FALSE;
5611 } else {
5612 // we have found a new line
5613 if (! header.written_header) {
5614 move_vertical(g, left_alignment);
5616 header.written_header = FALSE;
5618 if (processed_header(g)) {
5619 // we must not alter output_vpos as we have peeped at the next word
5620 // and set vpos to this - to ensure we do not generate a <br> after
5621 // a heading. (The html heading automatically generates a line break)
5622 output_hpos = left_margin;
5623 return( postword );
5624 } else {
5625 force_begin_paragraph();
5626 if ((! is_in_table()) && (margin_on)) {
5627 make_html_indent(left_margin);
5629 if (g->minh-left_margin != 0) {
5630 make_html_indent(g->minh-left_margin);
5632 change_font(g, TRUE);
5633 supress_sub_sup = FALSE;
5636 output_vpos = g->minv;
5637 output_hpos = g->maxh;
5638 return( postword );
5642 int html_printer::html_position_region (void)
5644 int r = font::res;
5645 int height = output_style.point_size*r/72;
5646 int temp_vpos;
5647 int is_center = FALSE;
5649 if (output_style.point_size != 0) {
5650 if (output_vpos != start_region_vpos) {
5652 // graphic starts on a different line
5653 if (output_vpos == -1) {
5654 temp_vpos = start_region_vpos;
5655 } else {
5656 temp_vpos = output_vpos;
5658 supress_sub_sup = TRUE;
5659 if (need_one_newline) {
5660 html_newline();
5661 temp_vpos += height;
5662 } else {
5663 need_one_newline = TRUE;
5666 while ((temp_vpos < start_region_vpos) &&
5667 (more_than_line_break(temp_vpos, start_region_vpos, height))) {
5668 html_newline();
5669 temp_vpos += height;
5673 if (auto_on && (is_in_middle(start_region_hpos, end_region_hpos))) {
5674 is_center = TRUE;
5675 } else {
5676 if (start_region_hpos > get_left()) {
5677 make_html_indent(start_region_hpos-get_left());
5680 output_vpos = start_region_vpos;
5681 output_hpos = start_region_hpos;
5682 return( is_center );
5686 * gs_x - translate and scale the x axis
5689 int html_printer::gs_x (int x)
5691 x += IMAGE_BOARDER_PIXELS/2;
5692 return((x-start_region_hpos)*postscript_res/font::res);
5697 * gs_y - translate and scale the y axis
5700 int html_printer::gs_y (int y)
5702 int yoffset=((int)(A4_PAGE_LENGTH*(double)font::res))-end_region_vpos;
5704 y += IMAGE_BOARDER_PIXELS/2;
5705 return( (y+yoffset)*postscript_res/font::res );
5709 void html_printer::troff_position_text (text_glob *g)
5711 change_font(g, FALSE);
5713 troff.put_string("V");
5714 troff.put_number(gs_y(g->maxv));
5715 troff.put_string("\n");
5717 troff.put_string("H");
5718 troff.put_number(gs_x(g->minh));
5719 troff.put_string("\n");
5722 void html_printer::troff_change_font (const char *fontname, int size, int font_no)
5724 troff.put_string("x font ");
5725 troff.put_number(font_no);
5726 troff.put_string(" ");
5727 troff.put_string(fontname);
5728 troff.put_string("\nf");
5729 troff.put_number(font_no);
5730 troff.put_string("\ns");
5731 troff.put_number(size*1000);
5732 troff.put_string("\n");
5736 void html_printer::set_style(const style &sty)
5738 #if 0
5739 const char *fontname = sty.f->get_name();
5740 if (fontname == 0)
5741 fatal("no internalname specified for font");
5743 change_font(fontname, (font::res/(72*font::sizescale))*sty.point_size);
5744 #endif
5747 void html_printer::end_of_line()
5749 flush_sbuf();
5750 output_hpos = -1;
5753 void html_printer::html_display_word (text_glob *g)
5755 #if 0
5756 if (strcmp(g->text_string, "ot") == 0) {
5757 stop();
5759 #endif
5760 if (! check_able_to_use_table(g)) {
5761 char *postword=html_position_text(g, left_margin_indent, right_margin_indent);
5763 if (! header.written_header) {
5764 if (g->is_raw_command) {
5765 html.put_string((char *)g->text_string);
5766 } else {
5767 translate_to_html(g);
5769 if (postword != 0) {
5770 html.put_string(postword);
5772 need_one_newline = TRUE;
5773 issued_newline = FALSE;
5778 void html_printer::troff_display_word (text_glob *g)
5780 troff_position_text(g);
5781 if (g->is_raw_command) {
5782 int l=strlen((char *)g->text_string);
5783 if (l == 1) {
5784 troff.put_string("c");
5785 troff.put_string((char *)g->text_string);
5786 troff.put_string("\n");
5787 } else if (l > 1) {
5788 troff.put_string("C");
5789 troff.put_troffps_char((char *)g->text_string);
5790 troff.put_string("\n");
5792 } else {
5793 troff_position_text(g);
5794 troff.put_string("t");
5795 troff.put_translated_string((const char *)g->text_string);
5796 troff.put_string("\n");
5800 void html_printer::display_word (text_glob *g, int is_to_html)
5802 if (is_to_html) {
5803 html_display_word(g);
5804 } else if ((g->is_raw_command) && (g->is_html_command)) {
5805 // found a raw html command inside a graphic glob.
5806 // We should emit the command to the html device, but of course we
5807 // cannot place it correctly as we are dealing with troff words.
5808 // Remember output_vpos will refer to troff and not html.
5809 html.put_string((char *)g->text_string);
5810 } else {
5811 troff_display_word(g);
5816 * translate_to_html - translates a textual string into html text
5819 void html_printer::translate_to_html (text_glob *g)
5821 char buf[MAX_STRING_LENGTH];
5823 str_translate_to_html(g->text_style.f, buf, MAX_STRING_LENGTH,
5824 g->text_string, g->text_length, TRUE);
5825 html.put_string(buf);
5829 * html_knows_about - given a character name, troff, return TRUE
5830 * if we know how to display this character using
5831 * html unicode.
5834 int html_printer::html_knows_about (char *troff)
5836 // --fixme-- needs to have similar code as above
5837 return( FALSE );
5841 * display_fill - generates a troff format fill command
5844 void html_printer::display_fill (graphic_glob *g)
5846 troff.put_string("Df ") ;
5847 troff.put_number(g->fill);
5848 troff.put_string(" 0\n");
5852 * display_line - displays a line using troff format
5855 void html_printer::display_line (graphic_glob *g, int is_to_html)
5857 if (is_to_html) {
5858 fatal("cannot emit lines in html");
5860 if (g->code == 'l') {
5861 // straight line
5863 troff.put_string("V");
5864 troff.put_number(gs_y(g->point[0].y));
5865 troff.put_string("\n");
5867 troff.put_string("H");
5868 troff.put_number(gs_x(g->point[0].x));
5869 troff.put_string("\n");
5871 display_fill(g);
5873 troff.put_string("Dl ");
5874 troff.put_number((g->point[1].x-g->point[0].x)*postscript_res/font::res);
5875 troff.put_string(" ");
5876 troff.put_number((g->point[1].y-g->point[0].y)*postscript_res/font::res);
5877 troff.put_string("\n");
5878 // printf("line %c %d %d %d %d size %d\n", (char)g->code, g->point[0].x, g->point[0].y,
5879 // g->point[1].x, g->point[1].y, g->size);
5880 } else if ((g->code == 'c') || (g->code == 'C')) {
5881 // circle
5883 int xradius = (g->maxh - g->minh) / 2;
5884 int yradius = (g->maxv - g->minv) / 2;
5885 // center of circle or elipse
5887 troff.put_string("V");
5888 troff.put_number(gs_y(g->minv+yradius));
5889 troff.put_string("\n");
5891 troff.put_string("H");
5892 troff.put_number(gs_x(g->minh));
5893 troff.put_string("\n");
5895 display_fill(g);
5897 if (g->code == 'c') {
5898 troff.put_string("Dc ");
5899 } else {
5900 troff.put_string("DC ");
5903 troff.put_number(xradius*2*postscript_res/font::res);
5904 troff.put_string("\n");
5906 } else if ((g->code == 'e') || (g->code == 'E')) {
5907 // ellipse
5909 int xradius = (g->maxh - g->minh) / 2;
5910 int yradius = (g->maxv - g->minv) / 2;
5911 // center of elipse - this is untested
5913 troff.put_string("V");
5914 troff.put_number(gs_y(g->minv+yradius));
5915 troff.put_string("\n");
5917 troff.put_string("H");
5918 troff.put_number(gs_x(g->minh));
5919 troff.put_string("\n");
5921 display_fill(g);
5923 if (g->code == 'e') {
5924 troff.put_string("De ");
5925 } else {
5926 troff.put_string("DE ");
5929 troff.put_number(xradius*2*postscript_res/font::res);
5930 troff.put_string(" ");
5931 troff.put_number(yradius*2*postscript_res/font::res);
5932 troff.put_string("\n");
5933 } else if ((g->code == 'p') || (g->code == 'P')) {
5934 // polygon
5935 troff.put_string("V");
5936 troff.put_number(gs_y(g->yc));
5937 troff.put_string("\n");
5939 troff.put_string("H");
5940 troff.put_number(gs_x(g->xc));
5941 troff.put_string("\n");
5943 display_fill(g);
5945 if (g->code == 'p') {
5946 troff.put_string("Dp");
5947 } else {
5948 troff.put_string("DP");
5951 int i;
5952 int xc=g->xc;
5953 int yc=g->yc;
5954 for (i=0; i<g->nopoints; i++) {
5955 troff.put_string(" ");
5956 troff.put_number((g->point[i].x-xc)*postscript_res/font::res);
5957 troff.put_string(" ");
5958 troff.put_number((g->point[i].y-yc)*postscript_res/font::res);
5959 xc = g->point[i].x;
5960 yc = g->point[i].y;
5962 troff.put_string("\n");
5963 } else if (g->code == 'a') {
5964 // arc
5965 troff.put_string("V");
5966 troff.put_number(gs_y(g->yc));
5967 troff.put_string("\n");
5969 troff.put_string("H");
5970 troff.put_number(gs_x(g->xc));
5971 troff.put_string("\n");
5973 display_fill(g);
5975 troff.put_string("Da");
5977 int i;
5979 for (i=0; i<g->nopoints; i++) {
5980 troff.put_string(" ");
5981 troff.put_number(g->point[i].x*postscript_res/font::res);
5982 troff.put_string(" ");
5983 troff.put_number(g->point[i].y*postscript_res/font::res);
5985 troff.put_string("\n");
5986 } else if (g->code == '~') {
5987 // spline
5988 troff.put_string("V");
5989 troff.put_number(gs_y(g->yc));
5990 troff.put_string("\n");
5992 troff.put_string("H");
5993 troff.put_number(gs_x(g->xc));
5994 troff.put_string("\n");
5996 display_fill(g);
5998 troff.put_string("D~");
6000 int i;
6001 int xc=g->xc;
6002 int yc=g->yc;
6003 for (i=0; i<g->nopoints; i++) {
6004 troff.put_string(" ");
6005 troff.put_number((g->point[i].x-xc)*postscript_res/font::res);
6006 troff.put_string(" ");
6007 troff.put_number((g->point[i].y-yc)*postscript_res/font::res);
6008 xc = g->point[i].x;
6009 yc = g->point[i].y;
6011 troff.put_string("\n");
6017 * flush_sbuf - flushes the current sbuf into the list of glyphs.
6020 void html_printer::flush_sbuf()
6022 if (sbuf_len > 0) {
6023 int r=font::res; // resolution of the device
6024 set_style(sbuf_style);
6026 page_contents->add(&sbuf_style, sbuf, sbuf_len,
6027 sbuf_vpos-sbuf_style.point_size*r/72, sbuf_start_hpos,
6028 sbuf_vpos , sbuf_end_hpos);
6030 output_hpos = sbuf_end_hpos;
6031 output_vpos = sbuf_vpos;
6032 sbuf_len = 0;
6033 sbuf_dmark_hpos = -1;
6038 void html_printer::set_line_thickness(const environment *env)
6040 line_thickness = env->size;
6041 printf("line thickness = %d\n", line_thickness);
6044 void html_printer::draw(int code, int *p, int np, const environment *env)
6046 switch (code) {
6048 case 'l':
6049 if (np == 2) {
6050 page_contents->add_line(code,
6051 env->hpos, env->vpos, env->hpos+p[0], env->vpos+p[1],
6052 env->size, fill);
6053 } else {
6054 error("2 arguments required for line");
6056 break;
6057 case 't':
6059 if (np == 0) {
6060 line_thickness = -1;
6061 } else {
6062 // troff gratuitously adds an extra 0
6063 if (np != 1 && np != 2) {
6064 error("0 or 1 argument required for thickness");
6065 break;
6067 line_thickness = p[0];
6069 break;
6072 case 'P':
6073 // fall through
6074 case 'p':
6076 if (np & 1) {
6077 error("even number of arguments required for polygon");
6078 break;
6080 if (np == 0) {
6081 error("no arguments for polygon");
6082 break;
6084 // firstly lets add our current position to polygon
6085 int oh=env->hpos;
6086 int ov=env->vpos;
6087 int i=0;
6089 while (i<np) {
6090 p[i+0] += oh;
6091 p[i+1] += ov;
6092 oh = p[i+0];
6093 ov = p[i+1];
6094 i += 2;
6096 // now store polygon in page
6097 page_contents->add_polygon(code, np, p, env->hpos, env->vpos, env->size, fill);
6099 break;
6100 case 'E':
6101 // fall through
6102 case 'e':
6103 if (np != 2) {
6104 error("2 arguments required for ellipse");
6105 break;
6107 page_contents->add_line(code,
6108 env->hpos, env->vpos-p[1]/2, env->hpos+p[0], env->vpos+p[1]/2,
6109 env->size, fill);
6111 break;
6112 case 'C':
6113 // fill circle
6115 case 'c':
6117 // troff adds an extra argument to C
6118 if (np != 1 && !(code == 'C' && np == 2)) {
6119 error("1 argument required for circle");
6120 break;
6122 page_contents->add_line(code,
6123 env->hpos, env->vpos-p[0]/2, env->hpos+p[0], env->vpos+p[0]/2,
6124 env->size, fill);
6126 break;
6127 case 'a':
6129 if (np == 4) {
6130 double c[2];
6132 if (adjust_arc_center(p, c)) {
6133 page_contents->add_arc('a', env->hpos, env->vpos, p, c, env->size, fill);
6134 } else {
6135 // a straignt line
6136 page_contents->add_line('l', env->hpos, env->vpos, p[0]+p[2], p[1]+p[3], env->size, fill);
6138 } else {
6139 error("4 arguments required for arc");
6142 break;
6143 case '~':
6145 if (np & 1) {
6146 error("even number of arguments required for spline");
6147 break;
6149 if (np == 0) {
6150 error("no arguments for spline");
6151 break;
6153 // firstly lets add our current position to spline
6154 int oh=env->hpos;
6155 int ov=env->vpos;
6156 int i=0;
6158 while (i<np) {
6159 p[i+0] += oh;
6160 p[i+1] += ov;
6161 oh = p[i+0];
6162 ov = p[i+1];
6163 i += 2;
6165 page_contents->add_spline('~', env->hpos, env->vpos, np, p, env->size, fill);
6167 break;
6168 case 'f':
6170 if (np != 1 && np != 2) {
6171 error("1 argument required for fill");
6172 break;
6174 fill = p[0];
6175 if (fill < 0 || fill > FILL_MAX) {
6176 // This means fill with the current color.
6177 fill = FILL_MAX + 1;
6179 break;
6182 default:
6183 error("unrecognised drawing command `%1'", char(code));
6184 break;
6189 void html_printer::begin_page(int n)
6191 page_number = n;
6192 html.begin_comment("Page: ").comment_arg(i_to_a(page_number)).end_comment();;
6193 no_of_printed_pages++;
6195 output_style.f = 0;
6196 output_space_code = 32;
6197 output_draw_point_size = -1;
6198 output_line_thickness = -1;
6199 output_hpos = -1;
6200 output_vpos = -1;
6203 void testing (text_glob *g) {}
6205 void html_printer::flush_graphic (void)
6207 graphic_glob g;
6209 graphic_level = 0;
6210 page_contents->is_in_graphic = FALSE;
6212 g.minv = -1;
6213 g.maxv = -1;
6214 calculate_region_range(&g);
6215 if (g.minv != -1) {
6216 page_contents->make_new_region(&g);
6218 move_region_to_page();
6221 void html_printer::end_page(int)
6223 flush_sbuf();
6224 flush_graphic();
6225 flush_page();
6228 font *html_printer::make_font(const char *nm)
6230 return html_font::load_html_font(nm);
6233 html_printer::~html_printer()
6235 if (fseek(tempfp, 0L, 0) < 0)
6236 fatal("fseek on temporary file failed");
6237 html.set_file(stdout);
6238 fputs("<html>\n", stdout);
6239 fputs("<head>\n", stdout);
6240 fputs("<meta name=\"Content-Style\" content=\"text/css\">\n", stdout);
6241 write_title(TRUE);
6242 fputs("</head>\n", stdout);
6243 fputs("<body>\n", stdout);
6244 write_title(FALSE);
6245 header.write_headings(stdout);
6247 extern const char *Version_string;
6248 html.begin_comment("Creator : ")
6249 .comment_arg("groff ")
6250 .comment_arg("version ")
6251 .comment_arg(Version_string)
6252 .end_comment();
6255 #ifdef LONG_FOR_TIME_T
6256 long
6257 #else
6258 time_t
6259 #endif
6260 t = time(0);
6261 html.begin_comment("CreationDate: ")
6262 .comment_arg(ctime(&t))
6263 .end_comment();
6265 html.begin_comment("Total number of pages: ").comment_arg(i_to_a(no_of_printed_pages)).end_comment();
6266 html.end_line();
6267 html.copy_file(tempfp);
6268 fputs("</body>\n", stdout);
6269 fputs("</html>\n", stdout);
6270 fclose(tempfp);
6275 * calculate_region_range - calculates the vertical range for words and lines
6276 * within the region lists.
6279 void html_printer::calculate_region_range (graphic_glob *r)
6281 text_glob *w;
6282 graphic_glob *g;
6284 if (! page_contents->region_lines.is_empty()) {
6285 page_contents->region_lines.start_from_head();
6286 do {
6287 g = page_contents->region_lines.get_data();
6288 if ((r->minv == -1) || (g->minv < r->minv)) {
6289 r->minv = g->minv;
6291 if ((r->maxv == -1) || (g->maxv > r->maxv)) {
6292 r->maxv = g->maxv;
6294 page_contents->region_lines.move_right();
6295 } while (! page_contents->region_lines.is_equal_to_head());
6297 if (! page_contents->region_words.is_empty()) {
6298 page_contents->region_words.start_from_head();
6299 do {
6300 w = page_contents->region_words.get_data();
6302 if ((r->minv == -1) || (w->minv < r->minv)) {
6303 r->minv = w->minv;
6305 if ((r->maxv == -1) || (w->maxv > r->maxv)) {
6306 r->maxv = w->maxv;
6308 page_contents->region_words.move_right();
6309 } while (! page_contents->region_words.is_equal_to_head());
6315 * move_region_to_page - moves lines and words held in the temporary region
6316 * list to the page list.
6319 void html_printer::move_region_to_page (void)
6321 text_glob *w;
6322 graphic_glob *g;
6324 page_contents->region_lines.start_from_head();
6325 while (! page_contents->region_lines.is_empty()) {
6326 g = page_contents->region_lines.get_data(); // remove from our temporary region list
6327 page_contents->lines.add(g); // and add to the page list
6328 page_contents->region_lines.sub_move_right();
6330 page_contents->region_words.start_from_head();
6331 while (! page_contents->region_words.is_empty()) {
6332 w = page_contents->region_words.get_data(); // remove from our temporary region list
6333 page_contents->words.add(w); // and add to the page list
6334 page_contents->region_words.sub_move_right();
6339 * is_graphic_start - returns TRUE if the start of table, pic, eqn was seen.
6342 int is_graphic_start (char *s)
6344 return( (strcmp(s, "graphic-start") == 0) ||
6345 ((strcmp(s, "table-start") == 0) && (table_image_on)) );
6349 * is_graphic_end - return TRUE if the end of a table, pic, eqn was seen.
6352 int is_graphic_end (char *s)
6354 return( (strcmp(s, "graphic-end") == 0) ||
6355 ((strcmp(s, "table-end") == 0) && (table_image_on)) );
6359 * special - handle all x X requests from troff. For grohtml they allow users
6360 * to pass raw html commands, turn auto linked headings off/on and
6361 * also allow tbl, eqn & pic say what commands they have generated.
6364 void html_printer::special(char *s, const environment *env)
6366 if (s != 0) {
6367 if (is_graphic_start(s)) {
6368 graphic_level++;
6369 if (graphic_level == 1) {
6370 page_contents->is_in_graphic = TRUE; // add words and lines to temporary region lists
6372 } else if (is_graphic_end(s) && (graphic_level > 0)) {
6373 graphic_level--;
6374 if (graphic_level == 0) {
6375 flush_graphic();
6377 } else if (strncmp(s, "html:", 5) == 0) {
6378 int r=font::res; // resolution of the device
6379 char buf[MAX_STRING_LENGTH];
6380 font *f=sbuf_style.f;
6382 if (f == NULL) {
6383 int found=FALSE;
6385 f = font::load_font("TR", &found);
6387 str_translate_to_html(f, buf, MAX_STRING_LENGTH,
6388 &s[5], strlen(s)-5, FALSE);
6389 page_contents->add_html_command(&sbuf_style, buf, strlen(buf),
6391 // need to pass rest of string through to html output during flush
6393 env->vpos-env->size*r/72, env->hpos,
6394 env->vpos , env->hpos);
6395 // assume that the html command has no width, if it does then we hopefully troff
6396 // will have fudged this in a macro and requested that the formatting move right by
6397 // the appropriate width
6398 } else if (strncmp(s, "index:", 6) == 0) {
6399 cutoff_heading = atoi(&s[6]);
6404 void set_image_type (char *type)
6406 if (strcmp(type, "gif") == 0) {
6407 image_type = gif;
6408 } else if (strcmp(type, "png") == 0) {
6409 image_type = png;
6410 image_device = "png256";
6411 } else if (strncmp(type, "png", 3) == 0) {
6412 image_type = png;
6413 image_device = type;
6417 printer *make_printer()
6419 return new html_printer;
6422 static void usage();
6424 int main(int argc, char **argv)
6426 program_name = argv[0];
6427 static char stderr_buf[BUFSIZ];
6428 setbuf(stderr, stderr_buf);
6429 int c;
6430 while ((c = getopt(argc, argv, "F:atTvdgmx?I:r:")) != EOF)
6431 switch(c) {
6432 case 'v':
6434 extern const char *Version_string;
6435 fprintf(stderr, "grohtml version %s\n", Version_string);
6436 fflush(stderr);
6437 break;
6439 case 'a':
6440 auto_on = FALSE;
6441 break;
6442 case 't':
6443 table_on = FALSE;
6444 break;
6445 case 'T':
6446 table_image_on = FALSE;
6447 break;
6448 case 'F':
6449 font::command_line_font_dir(optarg);
6450 break;
6451 case 'I':
6452 // user specifying the type of images we should generate
6453 set_image_type(optarg);
6454 break;
6455 case 'r':
6456 // resolution (dots per inch for an image)
6457 image_res = atoi(optarg);
6458 break;
6459 case 'd':
6460 // debugging on
6461 debug_on = TRUE;
6462 break;
6463 case 'x':
6464 debug_table_on = TRUE;
6465 break;
6466 case 'g':
6467 // do not guess title and headings
6468 guess_on = FALSE;
6469 break;
6470 case 'm':
6471 // leave margins alone
6472 margin_on = TRUE;
6473 break;
6474 case '?':
6475 usage();
6476 break;
6477 default:
6478 assert(0);
6480 if (optind >= argc) {
6481 do_file("-");
6482 } else {
6483 for (int i = optind; i < argc; i++)
6484 do_file(argv[i]);
6486 delete pr;
6487 return 0;
6490 static void usage()
6492 fprintf(stderr, "usage: %s [-avdgmt?] [-r resolution] [-F dir] [-I imagetype] [files ...]\n",
6493 program_name);
6494 exit(1);