1 #ifndef production
2 static char rcsId[]="$Header$";
3 #endif
4 /*****
5 * format.c : XmHTML formatting routines: translates parsed HTML to info
6 * required for displaying a HTML page.
7 *
8 * This file Version $Revision$
9 *
10 * Creation date: Tue Nov 26 17:03:09 GMT+0100 1996
11 * Last modification: $Date$
12 * By: $Author$
13 * Current State: $State$
14 *
15 * Author: newt
16 * (C)Copyright 1995-1996 Ripley Software Development
17 * All Rights Reserved
18 *
19 * This file is part of the XmHTML Widget Library.
20 *
21 * This library is free software; you can redistribute it and/or
22 * modify it under the terms of the GNU Library General Public
23 * License as published by the Free Software Foundation; either
24 * version 2 of the License, or (at your option) any later version.
25 *
26 * This library is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
29 * Library General Public License for more details.
30 *
31 * You should have received a copy of the GNU Library General Public
32 * License along with this library; if not, write to the Free
33 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
34 *
35 *****/
36 /*****
37 * ChangeLog
38 * $Log$
39 * Revision 1.1 2011/06/30 16:10:38 rwcox
40 * Cadd
41 *
42 * Revision 1.19 1998/04/27 06:59:23 newt
43 * tka stuff
44 *
45 * Revision 1.18 1998/04/04 06:28:07 newt
46 * XmHTML Beta 1.1.3
47 *
48 * Revision 1.17 1997/10/23 00:24:56 newt
49 * XmHTML Beta 1.1.0 release
50 *
51 * Revision 1.16 1997/08/31 17:34:24 newt
52 * renamed _rec structures to Rec.
53 *
54 * Revision 1.15 1997/08/30 00:55:24 newt
55 * Completed <form></form> support.
56 * Bugfix in _XmHTMLInitializeFontSizeLists, the default font is now properly
57 * changed.
58 * Made the font loading routines again robuster.
59 * ParseBodyTags now always attempts to load a body image.
60 * Made XmHTMLGetURLType a bit stricter.
61 *
62 * Revision 1.14 1997/08/01 13:00:21 newt
63 * Bugfixes in font switching (<b>...<font><i>...</i></font>...</b>) now
64 * properly handled. Enhanced form support.
65 *
66 * Revision 1.13 1997/05/28 01:46:35 newt
67 * Added support for the XmNbodyImage resource: it's now used but only if no
68 * bgcolor resource has been set.
69 *
70 * Revision 1.12 1997/04/29 14:26:18 newt
71 * HTML forms changes
72 *
73 * Revision 1.11 1997/04/03 05:34:25 newt
74 * _XmHTMLLoadBodyImage added.
75 * Placed a large number of warnings between a #ifdef PEDANTIC/#endif
76 *
77 * Revision 1.10 1997/03/28 07:12:43 newt
78 * Fixed buffer overrun in TexToPre.
79 * Fixed font resolution: x and y resolution are now always equal.
80 * XmHTML now ignores the ending body tag.
81 *
82 * Revision 1.9 1997/03/20 08:10:04 newt
83 * Split font cache in a true cache and a font stack.
84 * Added stack checks when document has been formatted.
85 *
86 * Revision 1.8 1997/03/11 19:52:17 newt
87 * added ImageToWord
88 *
89 * Revision 1.7 1997/03/04 00:59:26 newt
90 * ?
91 *
92 * Revision 1.6 1997/03/02 23:17:46 newt
93 * Way too many changes. Most important: font loading/switching scheme; anchor
94 * treatment; image/imagemap treatment
95 *
96 * Revision 1.5 1997/02/11 02:08:44 newt
97 * Way to many. Anchor treatment has been changed completely.
98 * Bugfixes in anchor parsing. Potential buffer overruns eliminated.
99 *
100 * Revision 1.4 1997/02/04 02:56:49 newt
101 * Bugfix in LoadQueryFont.
102 * Added code to deal with the basefont element.
103 * Changed the font element handling.
104 *
105 * Revision 1.3 1997/01/09 06:55:39 newt
106 * expanded copyright marker
107 *
108 * Revision 1.2 1997/01/09 06:44:42 newt
109 * lots of changes: linebreaking and changes related to changed XmHTMLWord
110 *
111 * Revision 1.1 1996/12/19 02:17:10 newt
112 * Initial Revision
113 *
114 *****/
115 #ifdef HAVE_CONFIG_H
116 #include "config.h"
117 #endif
118
119 #include <stdlib.h>
120 #include <stdio.h>
121 #include <string.h>
122 #include <ctype.h> /* isspace, tolower */
123
124 /* Local includes */
125 #include "toolkit.h"
126 #include XmHTMLPrivateHeader
127 #include "stack.h"
128
129 /*** External Function Prototype Declarations ***/
130
131 /*** Public Variable Declarations ***/
132
133 /*** Private Datatype Declarations ****/
134
135 typedef struct{
136 String name;
137 Marker type; /* Marker is an enumeration type defined in XmHTMLP.h */
138 }listMarkers;
139
140 /* for nesting of ordered and unordered lists */
141 typedef struct{
142 Boolean isindex; /* propagate index numbers? */
143 int level; /* item number */
144 htmlEnum type; /* ol or ul, used for custom markers */
145 Marker marker; /* marker to use */
146 }listStack;
147
148 /*** Private Function Prototype Declarations ****/
149
150 /****
151 * Formatting routines
152 *****/
153
154 /* Release the formatted element table */
155 static void FreeObjectTable(XmHTMLObjectTable *list);
156
157 /* Free the given list of tables */
158 static void freeTables(XmHTMLTable *table);
159
160 /* Initialize the formatted element table */
161 static void InitObjectTable(XmHTMLObjectTable *list, XmHTMLAnchor *anchors);
162
163 /* copy given text into an internal buffer */
164 static String CopyText(XmHTMLWidget html, String text, Boolean formatted,
165 Byte *text_data, Boolean expand_escapes, Boolean *i18n);
166
167 /* collapse all consecutive whitespace into a single space */
168 static void CollapseWhiteSpace(String text);
169
170 /* Split raw text into an array of words */
171 static XmHTMLWord* TextToWords(String text, int *num_words, Dimension *height,
172 XmHTMLfont *font, Byte line_data, Byte text_data,
173 XmHTMLObjectTableElement owner, ToolkitAbstraction *tka);
174
175 /* Split an image into an array of words ;-) */
176 static XmHTMLWord *ImageToWord(XmHTMLWidget html, String attributes,
177 int *num_words, Dimension *height, XmHTMLObjectTableElement owner,
178 Boolean formatted, ToolkitAbstraction *tka, Boolean is_anchor,
179 Byte text_data);
180
181 static XmHTMLWord *allocFormWord(XmHTMLWidget html, XmHTMLForm *form,
182 Dimension *width, Dimension *height, XmHTMLObjectTableElement owner,
183 Boolean formatted);
184
185 static XmHTMLWord *InputToWord(XmHTMLWidget html, String attributes,
186 int *num_words, Dimension *width, Dimension *height,
187 XmHTMLObjectTableElement owner, Boolean formatted);
188
189 static XmHTMLWord *SelectToWord(XmHTMLWidget html, XmHTMLObject *start,
190 int *num_words, Dimension *width, Dimension *height,
191 XmHTMLObjectTableElement owner, Boolean formatted);
192
193 static XmHTMLWord *TextAreaToWord(XmHTMLWidget html, XmHTMLObject *start,
194 int *num_words, Dimension *width, Dimension *height,
195 XmHTMLObjectTableElement owner, Boolean formatted);
196
197 static XmHTMLWord *BreakToWord(Dimension *height, XmHTMLfont *font,
198 int linefeed, XmHTMLObjectTableElement owner);
199
200 static XmHTMLWord *MakeDummyWord(Dimension *height, XmHTMLfont *font,
201 Byte line_data, XmHTMLObjectTableElement owner);
202
203 /* Split raw text into a chunk of preformatted lines */
204 static XmHTMLWord *TextToPre(String text, int *num_words, XmHTMLfont *font,
205 Byte line_data, XmHTMLObjectTableElement owner, ToolkitAbstraction *tka,
206 int tabwidth);
207
208 /* Insert a horizontal tab */
209 static XmHTMLWord* SetTab(int size, Dimension *height, XmHTMLfont *font,
210 XmHTMLObjectTableElement owner, ToolkitAbstraction *tka);
211
212 /* Initialize a bullet (a real bullet or some number) */
213 static void FillBullet(XmHTMLWidget html, XmHTMLObjectTableElement owner,
214 ToolkitAbstraction *tka);
215
216 /* get properties of a table element */
217 static TableProperties *tableCheckProperties(XmHTMLWidget html,
218 String attributes, TableProperties *parent, Alignment halign, Pixel bg);
219
220 /* open a new table */
221 static XmHTMLTable *tableOpen(XmHTMLWidget html, XmHTMLTable *parent,
222 XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign,
223 Pixel *bg);
224
225 /* close the current table */
226 static XmHTMLTable *tableClose(XmHTMLWidget html, XmHTMLTable *parent,
227 XmHTMLObjectTableElement end);
228
229 /* open a caption in the current table */
230 static void tableOpenCaption(XmHTMLWidget html, XmHTMLTable *parent,
231 XmHTMLObjectTableElement start, XmHTMLObject *obj,
232 Pixel *bg);
233
234 /* close the current caption */
235 static void tableCloseCaption(XmHTMLWidget html, XmHTMLTable *parent,
236 XmHTMLObjectTableElement end);
237
238 /* open a row in the current table */
239 static void tableOpenRow(XmHTMLWidget html, XmHTMLTable *parent,
240 XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign,
241 Pixel *bg);
242
243 /* close the current row */
244 static void tableCloseRow(XmHTMLWidget html, XmHTMLTable *parent,
245 XmHTMLObjectTableElement end);
246
247 /* open a cell in the current row */
248 static void tableOpenCell(XmHTMLWidget html, XmHTMLTable *parent,
249 XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign,
250 Pixel *bg);
251
252 /* close the current cell in the current row */
253 static void tableCloseCell(XmHTMLWidget html, XmHTMLTable *parent,
254 XmHTMLObjectTableElement end);
255
256 /* Parse body tags and update the widget */
257 static void ParseBodyTags(XmHTMLWidget html, XmHTMLObject *data);
258
259 /* Check whether a linefeed is required or not */
260 static int CheckLineFeed(int op, Boolean force, Byte *text_data);
261
262 /* split the given anchor spec into a href, target and other stuff */
263 static void parseHref(String text, XmHTMLAnchor *anchor);
264
265 /*** Private Variable Declarations ***/
266 /* Element data bits */
267 #define ELE_ANCHOR (1<<1) /* an anchor */
268 #define ELE_ANCHOR_TARGET (1<<2) /* with a target attribute */
269 #define ELE_ANCHOR_VISITED (1<<3) /* it's a previously visited one */
270 #define ELE_ANCHOR_INTERN (1<<4) /* with a name attribute */
271 #define ELE_UNDERLINE (1<<5) /* underlined object */
272 #define ELE_UNDERLINE_TEXT (1<<6) /* underlined text */
273 #define ELE_STRIKEOUT (1<<7) /* strikeout object */
274 #define ELE_STRIKEOUT_TEXT (1<<8) /* strikeout text */
275
276 /* Private formatted element table data */
277 static struct{
278 #ifdef DEBUG
279 unsigned long num_elements;
280 unsigned long num_anchors;
281 #endif
282 XmHTMLObjectTableElement head;
283 XmHTMLObjectTableElement current;
284 XmHTMLAnchor *anchor_head;
285 XmHTMLAnchor *anchor_current;
286 }list_data;
287
288 /* Marker information for HTML lists, ordered list. */
289 #define OL_ARRAYSIZE 5
290 static listMarkers ol_markers[OL_ARRAYSIZE] = {
291 {"1", XmMARKER_ARABIC},
292 {"a", XmMARKER_ALPHA_LOWER},
293 {"A", XmMARKER_ALPHA_UPPER},
294 {"i", XmMARKER_ROMAN_LOWER},
295 {"I", XmMARKER_ROMAN_UPPER},
296 };
297
298 /* Unordered list. */
299 #define UL_ARRAYSIZE 3
300 static listMarkers ul_markers[UL_ARRAYSIZE] = {
301 {"disc", XmMARKER_DISC},
302 {"square", XmMARKER_SQUARE},
303 {"circle", XmMARKER_CIRCLE},
304 };
305
306 #ifdef DEBUG
307 static int allocated;
308 #endif
309
310 /*****
311 * Name: NewTableElement
312 * Return Type: MACRO
313 * Description: creates a XmHTMLObjectTableElement and fills it.
314 * Macro for obvious perfomance reasons.
315 * In:
316 * ELEMENT: element to be allocated
317 * data: raw data for this element.
318 * Returns:
319 * the newly created element.
320 *****/
321 #ifdef DEBUG
322
323 #define NewTableElement(ELEMENT,DATA) do{ \
324 (ELEMENT) = (XmHTMLObjectTableElement)malloc(sizeof(XmHTMLObjectTable)); \
325 (void)memset((ELEMENT), 0, sizeof(XmHTMLObjectTable)); \
326 (ELEMENT)->object = DATA; \
327 allocated++; \
328 }while(0)
329
330 #else
331
332 #define NewTableElement(ELEMENT,DATA) do{ \
333 ELEMENT = (XmHTMLObjectTableElement)malloc(sizeof(XmHTMLObjectTable)); \
334 (void)memset(ELEMENT, 0, sizeof(XmHTMLObjectTable)); \
335 ELEMENT->object = DATA; \
336 }while(0)
337
338 #endif /* DEBUG */
339
340 /*****
341 * Name: InsertTableElement
342 * Return Type: MACRO
343 * Description: inserts a given formatted element in the list of elements.
344 * Macro for obvious perfomance reasons.
345 * In:
346 * element: element to add
347 * is_anchor: true if this element is an anchor.
348 * Returns:
349 * NA
350 *****/
351 #ifdef DEBUG
352
353 #define InsertTableElement(ELEMENT,IS_ANCHOR) do { \
354 ELEMENT->prev = list_data.current; \
355 list_data.current->next = ELEMENT; \
356 list_data.current = ELEMENT; \
357 list_data.num_elements++; \
358 if((IS_ANCHOR)) list_data.num_anchors++; \
359 }while(0)
360
361 #else
362
363 #define InsertTableElement(ELEMENT,IS_ANCHOR) do { \
364 ELEMENT->prev = list_data.current; \
365 list_data.current->next = ELEMENT; \
366 list_data.current = ELEMENT; \
367 }while(0)
368
369 #endif /* DEBUG */
370
371 /*****
372 * Name: parseHref
373 * Return Type: void
374 * Description: returns the url specification found in the given anchor.
375 * In:
376 * text: full anchor spec.
377 * href: url found in given anchor. Filled upon return.
378 * target: any target attribute found. Filled upon return.
379 * extra: any additional attributes found. Filled upon return.
380 * Returns:
381 * nothing.
382 *****/
383 static void
parseHref(String text,XmHTMLAnchor * anchor)384 parseHref(String text, XmHTMLAnchor *anchor)
385 {
386 if(text == NULL ||
387 (anchor->href = _XmHTMLTagGetValue(text, "href")) == NULL)
388 {
389 /* allocate empty href field so later strcmps won't explode */
390 anchor->href = (char *)malloc(1);
391 anchor->href[0] = '\0'; /* fix 02/03/97-05, kdh */
392 /*
393 * Could be a named anchor with a target spec. Rather impossible but
394 * allow for it anyway (I can imagine this to be true for a
395 * split-screen display).
396 */
397 if(text == NULL)
398 return;
399 }
400
401 /* Check if there is a target specification */
402 anchor->target= _XmHTMLTagGetValue(text, "target");
403
404 /* Also check for rel, rev and title */
405 anchor->rel = _XmHTMLTagGetValue(text, "rel");
406 anchor->rev = _XmHTMLTagGetValue(text, "rev");
407 anchor->title = _XmHTMLTagGetValue(text, "title");
408 }
409
410 /*****
411 * Name: FreeObjectTable
412 * Return Type: void
413 * Description: releases all memory occupied by the formatted list of elements.
414 * In:
415 * list: previous list to be released.
416 * Returns:
417 * nothing.
418 * Note:
419 * Images are freed in XmHTML.c, which calls XmHTMLFreeAllImages to do the
420 * job.
421 *****/
422 static void
FreeObjectTable(XmHTMLObjectTable * list)423 FreeObjectTable(XmHTMLObjectTable *list)
424 {
425 XmHTMLObjectTableElement temp;
426
427 #ifdef DEBUG
428 int i = 0, j = 0;
429 #endif
430
431 /* free all parsed objects */
432 while(list != NULL)
433 {
434 temp = list->next;
435 if(list->text) /* space occupied by text to display */
436 free(list->text);
437
438 /* free list of words. Can't be done above, <pre> doesn't have this! */
439 if(list->n_words)
440 {
441 /*
442 * only the first word contains a valid ptr, all others point to
443 * some char in this buffer, so freeing them *will* cause a
444 * segmentation fault eventually.
445 */
446 free(list->words[0].word);
447 /* Free raw word data */
448 free(list->words);
449 }
450 free(list);
451 list = temp;
452 #ifdef DEBUG
453 i++;
454 #endif
455 }
456 _XmHTMLDebug(2, ("format.c: FreeObjectTable End, freed %i elements and "
457 "%i anchors.\n", i, j));
458 }
459
460 /*****
461 * Name: freeTables
462 * Return Type: void
463 * Description: frees all data allocated for HTML table support.
464 * In:
465 * table: list of tables to be freed.
466 * Returns:
467 * nothing.
468 *****/
469 static void
freeTables(XmHTMLTable * table)470 freeTables(XmHTMLTable *table)
471 {
472 XmHTMLTable *tab, *tmp = table;
473 TableRow *row;
474 int i, j, k;
475
476 while(table)
477 {
478 tmp = table->next;
479
480 /*****
481 * Free all child tables (first table in the childs array is the
482 * table itself)
483 *****/
484 for(i = 0; i < table->nchilds; i++)
485 {
486 tab = &table->childs[i];
487
488 /* free all rows */
489 for(j = 0; j < tab->nrows; j++)
490 {
491 row = &tab->rows[j];
492 /* free all cells in this row */
493 for(k = 0; k < row->ncells; k++)
494 {
495 free(row->cells[k].props);
496 }
497 free(row->cells);
498 free(row->props);
499 }
500 free(tab->rows);
501 free(tab->props);
502 }
503 free(table->childs);
504 free(table);
505 table = tmp;
506 }
507 }
508
509 /*****
510 * Name: FreeAnchors
511 * Return Type: void
512 * Description: frees the memory occupied by the anchor data
513 * In:
514 * anchors: list of anchors to be freed
515 * Returns:
516 * nothing.
517 *****/
518 static void
FreeAnchors(XmHTMLAnchor * anchors)519 FreeAnchors(XmHTMLAnchor *anchors)
520 {
521 XmHTMLAnchor *tmp;
522 int i = 0;
523
524 while(anchors)
525 {
526 tmp = anchors->next;
527 /* href field is always allocated */
528 free(anchors->href);
529 if(anchors->target)
530 free(anchors->target);
531 if(anchors->rel)
532 free(anchors->rel);
533 if(anchors->rev)
534 free(anchors->rev);
535 if(anchors->title)
536 free(anchors->title);
537 if(anchors->name) /* fix 07/09/97-01, kdh */
538 free(anchors->name);
539 if(anchors->events)
540 free(anchors->events);
541 free(anchors);
542 anchors = NULL;
543 anchors = tmp;
544 i++;
545 }
546 _XmHTMLDebug(2, ("format.c: FreeAnchors, freed %i XmHTMLAnchor objects\n",
547 i));
548 }
549
550 /*****
551 * Name: InitObjectTable
552 * Return Type: void
553 * Description: initializes the list of formatted elements.
554 * In:
555 * list: previous list to be released.
556 * Returns:
557 * nothing
558 * Note:
559 * The list head is a dummy element and is never used. It is done to gain
560 * some performance (a test on an empty head is not required now in the
561 * InsertTableElement routine).
562 *****/
563 static void
InitObjectTable(XmHTMLObjectTable * list,XmHTMLAnchor * anchors)564 InitObjectTable(XmHTMLObjectTable *list, XmHTMLAnchor *anchors)
565 {
566 if(list != NULL)
567 {
568 FreeObjectTable(list);
569 list = NULL;
570 }
571
572 if(anchors != NULL)
573 {
574 FreeAnchors(anchors);
575 anchors = NULL;
576 }
577 if(list_data.head)
578 free(list_data.head);
579 NewTableElement(list_data.head,NULL);
580 list_data.current = list_data.head;
581 list_data.anchor_head = (XmHTMLAnchor*)NULL;
582 list_data.anchor_current = (XmHTMLAnchor*)NULL;
583 #ifdef DEBUG
584 list_data.num_elements = 1;
585 list_data.num_anchors = 0;
586 #endif
587 }
588
589 /*****
590 * Name: CollapseWhiteSpace
591 * Return Type: void
592 * Description: collapses whitespace in the given text
593 * In:
594 * text: text for which multiple whitespace has to be collapsed.
595 * Returns:
596 * nothing, but text is updated when this function returns.
597 *****/
598 static void
CollapseWhiteSpace(String text)599 CollapseWhiteSpace(String text)
600 {
601 register char *outPtr = text;
602 #ifdef I18N
603 int n = 1;
604 #endif /* I18N */
605
606 /*
607 * We only collapse valid text and text that contains more than whitespace
608 * only. This should never be true since CopyText will filter these
609 * things out. It's just here for sanity.
610 */
611 if(*text == '\0' || !strlen(text))
612 return;
613
614 _XmHTMLDebug(2, ("format.c: CollapseWhiteSpace, text in is:\n%s\n", text));
615
616 /*
617 * Now collapse each occurance of multiple whitespaces.
618 * This may produce different results on different systems since
619 * isspace() might not produce the same on each and every platform.
620 */
621 while(True)
622 {
623
624 #ifdef I18N
625 if((n = mblen((char*)text, (size_t)(strlen(text)))) == 1)
626 {
627 #endif /* I18N */
628 switch(*text)
629 {
630 case '\f':
631 case '\n':
632 case '\r':
633 case '\t':
634 case '\v':
635 *text = ' '; /* replace by a single space */
636 /* fall through */
637 case ' ':
638 /* skip past first space */
639 *(outPtr++) = *(text++);
640 #ifdef I18N
641 n = mblen((char*)text, (size_t)(strlen(text)));
642 while(n == 1 && *text != '\0' && isspace(*text))
643 *text++ = '\0';
644 #else
645 /* collapse every space following */
646 while(*text != '\0' && isspace(*text))
647 *text++ = '\0';
648 #endif /* I18N */
649 break;
650 default:
651 *(outPtr++) = *(text++);
652 break;
653 }
654 if(*text == 0)
655 {
656 *outPtr = '\0';
657 return;
658 }
659 #ifdef I18N
660 }
661 else if(n > 1)
662 {
663 /*****
664 * Multibyte char, copy it.
665 *****/
666 int i;
667 for(i = 0; i < n; i++)
668 *(outPtr++) = *(text++);
669 }
670 else
671 break;
672 #endif /* I18N */
673 }
674 }
675
676 /*****
677 * Name: TextToWords
678 * Return Type: XmHTMLWord*
679 * Description: splits the given text into an array of words.
680 * In:
681 * text: text to split
682 * num_words: number of words in the given text. Filled upon return;
683 * font: font to use for this text.
684 * Returns:
685 * an array of words. When allocation fails, this routine exits.
686 *****/
687 static XmHTMLWord*
TextToWords(String text,int * num_words,Dimension * height,XmHTMLfont * font,Byte line_data,Byte text_data,XmHTMLObjectTableElement owner,ToolkitAbstraction * tka)688 TextToWords(String text, int *num_words, Dimension *height, XmHTMLfont *font,
689 Byte line_data, Byte text_data, XmHTMLObjectTableElement owner,
690 ToolkitAbstraction *tka)
691 {
692 int n_words, len, i;
693 char *start;
694 static XmHTMLWord *words;
695 static char *raw;
696 register int j;
697 register char *chPtr;
698
699 /* sanity check */
700 if(text == NULL)
701 {
702 *height = *num_words = 0;
703 return(NULL);
704 }
705
706 _XmHTMLFullDebug(2, ("format.c: TextToWords, text in is:\n%s\n", text));
707
708 /* compute how many words we have */
709 n_words = 0;
710 for(chPtr = text; *chPtr != '\0'; chPtr++)
711 if(*chPtr == ' ')
712 n_words++;
713 /* also pick up the last word */
714 n_words++;
715
716 /* copy text */
717 raw = strdup(text);
718
719 /* allocate memory for all words */
720 words = (XmHTMLWord*)calloc(n_words, sizeof(XmHTMLWord));
721
722 /* Split the text in words and fill in the appropriate fields */
723 *height = font->height;
724
725 chPtr = start = raw;
726
727 for(i = 0, j = 0, len = 0; ; chPtr++, len++, j++)
728 {
729 /* also pick up the last word! */
730 if(*chPtr == ' ' || *chPtr == '\0')
731 {
732 if(*chPtr)
733 {
734 chPtr++; /* nuke the space */
735 raw[j++] = '\0';
736 }
737 /* fill in required fields */
738 words[i].self = &words[i];
739 words[i].word = start;
740 words[i].len = len;
741 #ifdef I18N
742
743 /*****
744 * Possible multibyte input, use multibyte functions to
745 * determine correct size of this word.
746 *****/
747 if(font->type == XmHTML_FONTSET)
748 {
749 int nbytes = 0;
750 Byte *mbPtr = start;
751 int n = mblen((char*)mbPtr, (size_t)len);
752
753 /*****
754 * Compute number of multibyte characters this word
755 * occupies.
756 *****/
757 while(n > 0)
758 {
759 nbytes += n;
760 mbPtr += n;
761 len -= n;
762 n = mblen((char*)mbPtr, (size_t)len);
763 }
764 len = nbytes; /* used by TextWidth computation */
765 words[i].len = len;
766 }
767 else
768 words[i].len = len;
769 #else
770 words[i].len = len;
771 #endif /* I18N */
772
773 words[i].height = *height;
774 words[i].width = tka->TextWidth(font, words[i].word, len);
775 words[i].owner = owner;
776 words[i].font = font;
777 words[i].spacing = TEXT_SPACE_LEAD | TEXT_SPACE_TRAIL;
778 words[i].type = OBJ_TEXT;
779 words[i].line_data = line_data;
780
781 _XmHTMLFullDebug(2, ("format.c: TextToWords, word is %s, len is "
782 "%i, width is %i, height is %i\n", words[i].word, words[i].len,
783 words[i].width, words[i].height));
784
785 start = chPtr;
786 i++;
787 len = 0;
788 }
789 if(*chPtr == '\0')
790 break;
791 }
792 /*
793 * when there is more than one word in this block, the first word
794 * _always_ has a trailing space.
795 * Likewise, the last word always has a leading space.
796 */
797 if(n_words > 1)
798 {
799 /* unset nospace bit */
800 Byte spacing = text_data & ~TEXT_SPACE_NONE;
801 words[0].spacing = spacing | TEXT_SPACE_TRAIL;
802 words[n_words-1].spacing = spacing | TEXT_SPACE_LEAD;
803 }
804 else
805 words[0].spacing = text_data;
806
807 _XmHTMLFullDebug(2, ("format.c: TextToWords counted %i words\n", n_words));
808
809 *num_words = i; /* n_words */;
810 return(words);
811 }
812
813 /*****
814 * Name: ImageToWord
815 * Return Type: XmHTMLWord*
816 * Description: converts an image to a word
817 * In:
818 * w: XmHTMLWidget id
819 * attributes: raw <img> specification
820 * height: object height, updated upon return
821 * owner: owning object
822 * formatted: True when this image is part of a block of <pre></pre> text.
823 * ToolkitAbstraction: renderer to be used;
824 * is_anchor: True when image is part of an anchor, False if not.
825 * Returns:
826 * a word representing the image
827 *****/
828 static XmHTMLWord*
ImageToWord(XmHTMLWidget html,String attributes,int * num_words,Dimension * height,XmHTMLObjectTableElement owner,Boolean formatted,ToolkitAbstraction * tka,Boolean is_anchor,Byte text_data)829 ImageToWord(XmHTMLWidget html, String attributes, int *num_words,
830 Dimension *height, XmHTMLObjectTableElement owner, Boolean formatted,
831 ToolkitAbstraction *tka, Boolean is_anchor, Byte text_data)
832 {
833 static XmHTMLWord *word;
834 static XmHTMLImage *image;
835 Dimension width = 0;
836
837 *num_words = 0;
838
839 /* sanity check */
840 if(attributes == NULL ||
841 (image = _XmHTMLNewImage(html, attributes, &width, height)) == NULL)
842 {
843 *height = 0;
844 return(NULL);
845 }
846
847 /*****
848 * Check for border, isn't done in the image loading routines since
849 * the default border is context-dependent: if it's an anchor the default
850 * is to add a border, if it's plain text default is no border.
851 *****/
852 image->border = _XmHTMLTagGetNumber(attributes, "border", (int)is_anchor);
853
854 _XmHTMLFullDebug(2, ("format.c: ImageToWord, image in is: %s\n",
855 image->url));
856
857 word = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));
858
859 /* required for image anchoring/replace/update */
860 image->owner = owner;
861
862 /* fill in required fields */
863 word->self = word;
864 word->word = strdup(image->alt); /* we always have this */
865
866 #ifdef I18N
867 if((HTML_ATTR(default_font))->type == XmHTML_FONTSET)
868 _XmHTMLWarning(__WFUNC__(html, "ImageToWord"),
869 "I18N not (yet) supported for <IMG> alt attribute.\n");
870 #else
871 word->len = strlen(image->alt);
872 #endif /* I18N */
873
874 word->width = width + 2*image->hspace + 2*image->border;
875 word->height = *height + 2*image->vspace + 2*image->border;
876 word->owner = owner;
877 word->font = HTML_ATTR(default_font); /* always use the default font */
878 /*****
879 * if image support is disabled, add width of the alt text to the
880 * image width (either from default image or specified in the doc).
881 * This is required for proper exposure handling when images are disabled.
882 *****/
883 if(!HTML_ATTR(images_enabled))
884 word->width += tka->TextWidth(word->font, word->word, word->len);
885
886 /*****
887 * No spacing if part of a chunk of <pre></pre> text
888 * Fix 07/24/97, kdh
889 *****/
890 word->spacing = formatted ? 0 : text_data;
891 word->type = OBJ_IMG;
892 word->line_data = NO_LINE; /* no underlining for images */
893 word->image = image;
894
895 _XmHTMLFullDebug(2, ("format.c: TextToWords, word is %s, len is %i, "
896 "width is %i, height is %i\n", word->word, word->len,
897 word->width, word->height));
898
899 *num_words = 1;
900 return(word);
901 }
902
903 /*****
904 * Name: allocFormWord
905 * Return Type: XmHTMLWord*
906 * Description: allocates a default XmHTMLWord for use within a HTML form.
907 * In:
908 * html: XmHTMLWidget id
909 * form: form entry for which this word should be allocated;
910 * *width: object's width, updated upon return;
911 * *height: object's height, updated upon return;
912 * owner: owning object.
913 * formatted: true when allocating a form component present in <pre></pre>
914 * Returns:
915 * a newly allocated word.
916 *****/
917 static XmHTMLWord*
allocFormWord(XmHTMLWidget html,XmHTMLForm * form,Dimension * width,Dimension * height,XmHTMLObjectTableElement owner,Boolean formatted)918 allocFormWord(XmHTMLWidget html, XmHTMLForm *form, Dimension *width,
919 Dimension *height, XmHTMLObjectTableElement owner, Boolean formatted)
920 {
921 static XmHTMLWord *word;
922
923 /* allocate new entry */
924 word = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));
925
926 /* fill in required fields */
927 word->self = word;
928 word->word = strdup(form->name); /* we always have this */
929 word->len = strlen(form->name);
930 word->height = *height = form->height;
931 word->width = *width = form->width;
932 word->owner = owner;
933 word->font = HTML_ATTR(default_font); /* always use default font */
934 word->spacing = formatted ? 0 : TEXT_SPACE_LEAD | TEXT_SPACE_TRAIL;
935 word->type = OBJ_FORM;
936 word->form = form;
937
938 return(word);
939 }
940
941 /*****
942 * Name: InputToWord
943 * Return Type: XmHTMLWord*
944 * Description: converts a HTML form <input> element to a word
945 * In:
946 * w: XmHTMLWidget id
947 * attributes: raw form element specification
948 * width: object width, updated upon return
949 * height: object height, updated upon return
950 * owner: owning object
951 * formatted: true when this form component is placed in a <pre></pre> tag.
952 * Returns:
953 * a word representing the image
954 *****/
955 static XmHTMLWord*
InputToWord(XmHTMLWidget html,String attributes,int * num_words,Dimension * width,Dimension * height,XmHTMLObjectTableElement owner,Boolean formatted)956 InputToWord(XmHTMLWidget html, String attributes, int *num_words,
957 Dimension *width, Dimension *height, XmHTMLObjectTableElement owner,
958 Boolean formatted)
959 {
960 static XmHTMLForm *form_entry;
961 XmHTMLWord *word;
962
963 *num_words = 0;
964
965 /* sanity check */
966 if(attributes == NULL ||
967 (form_entry = _XmHTMLFormAddInput(html, attributes)) == NULL)
968 return(NULL);
969
970 /* save owner, we need it in the paint routines */
971 form_entry->data = owner;
972
973 /* image buttons are treated as anchored images */
974 if(form_entry->type == FORM_IMAGE)
975 {
976 word = ImageToWord(html, attributes, num_words, height, owner,
977 formatted, HTML_ATTR(tka), True,
978 TEXT_SPACE_LEAD|TEXT_SPACE_TRAIL);
979 /* remove alt text */
980 free(word->word);
981 /* use form member name instead */
982 word->word = strdup(form_entry->name);
983 word->len = strlen(form_entry->name);
984 word->form = form_entry;
985
986 _XmHTMLFullDebug(2, ("format.c: InputToWord, word is %s, len is %i, "
987 "width is %i, height is %i (type = image)\n", word->word,
988 word->len, word->width, word->height));
989
990 return(word);
991 }
992
993 /* allocate new word for this form member */
994 word = allocFormWord(html, form_entry, width, height, owner, formatted);
995
996 _XmHTMLFullDebug(2, ("format.c: InputToWord, word is %s, len is %i, "
997 "width is %i, height is %i\n", word->word, word->len,
998 word->width, word->height));
999
1000 *num_words = 1;
1001 return(word);
1002 }
1003
1004 /*****
1005 * Name: SelectToWord
1006 * Return Type: XmHTMLWord*
1007 * Description: converts a HTML form <select></select> to a HTMLWord.
1008 * Also processes any <option></option> items within this select.
1009 * In:
1010 * html: XmHTMLWidget id;
1011 * start: object at which <select> starts;
1012 * *num_words: no of words allocated. Updated upon return;
1013 * *width: width of returned object. Updated upon return;
1014 * *height: height of returned object. Updated upon return;
1015 * owner: owning element.
1016 * formatted: true when this form component is placed in a <pre></pre> tag.
1017 * Returns:
1018 * a newly allocated word upon success. NULL on failure.
1019 *****/
1020 static XmHTMLWord*
SelectToWord(XmHTMLWidget html,XmHTMLObject * start,int * num_words,Dimension * width,Dimension * height,XmHTMLObjectTableElement owner,Boolean formatted)1021 SelectToWord(XmHTMLWidget html, XmHTMLObject *start, int *num_words,
1022 Dimension *width, Dimension *height, XmHTMLObjectTableElement owner,
1023 Boolean formatted)
1024 {
1025 static XmHTMLForm *form_entry;
1026 XmHTMLWord *word;
1027 XmHTMLObject *tmp = start;
1028 Boolean i18n = False;
1029
1030 *num_words = 0;
1031
1032 /* sanity check */
1033 if(start->attributes == NULL ||
1034 (form_entry = _XmHTMLFormAddSelect(html, start->attributes)) == NULL)
1035 return(NULL);
1036
1037 /* save owner */
1038 form_entry->data = owner;
1039
1040 /* move to next element */
1041 tmp = tmp->next;
1042
1043 /* add all option tags */
1044 for(; tmp != NULL && tmp->id != HT_SELECT; tmp = tmp->next)
1045 {
1046 if(tmp->id == HT_OPTION && !tmp->is_end)
1047 {
1048 XmHTMLObject *sel_start = tmp;
1049 Byte foo;
1050 String text = NULL;
1051
1052 /*
1053 * The next object should be plain text, if not it's an
1054 * error and we should ignore it
1055 */
1056 tmp = tmp->next;
1057 if(tmp->id != HT_ZTEXT)
1058 {
1059 if(HTML_ATTR(bad_html_warnings))
1060 {
1061 /* empty option tag, ignore it */
1062 if(tmp->id == HT_OPTION)
1063 _XmHTMLWarning(__WFUNC__(html, "SelectToWord"),
1064 XMHTML_MSG_40, tmp->line);
1065 else
1066 _XmHTMLWarning(__WFUNC__(html, "SelectToWord"),
1067 XMHTML_MSG_41, html_tokens[tmp->id],
1068 html_tokens[HT_OPTION], tmp->line);
1069 }
1070 continue;
1071 }
1072 /* get text */
1073 if((text = CopyText(html, tmp->element, False, &foo, True,
1074 &i18n)) == NULL)
1075 continue;
1076
1077 CollapseWhiteSpace(text);
1078 if(strlen(text))
1079 {
1080 _XmHTMLFormSelectAddOption(html, form_entry,
1081 sel_start->attributes, text);
1082 /* no longer needed */
1083 free(text);
1084 }
1085 }
1086 }
1087 /* close this selection and get width and height */
1088 _XmHTMLFormSelectClose(html, form_entry);
1089
1090 /* allocate new word for this form member */
1091 word = allocFormWord(html, form_entry, width, height, owner, formatted);
1092
1093 _XmHTMLFullDebug(2, ("format.c: SelectToWord, word is %s, len is %i, "
1094 "width is %i, height is %i\n", word->word, word->len,
1095 word->width, word->height));
1096
1097 *num_words = 1;
1098 return(word);
1099 }
1100
1101 /*****
1102 * Name: TextAreaToWord
1103 * Return Type: XmHTMLWord*
1104 * Description: converts a HTML form <textarea> to a HTMLWord.
1105 * In:
1106 * html: XmHTMLWidget id;
1107 * start: object at which <textarea> starts;
1108 * *num_words: no of words allocated. Updated upon return;
1109 * *width: width of returned object. Updated upon return;
1110 * *height: height of returned object. Updated upon return;
1111 * owner: owning element.
1112 * formatted: true when this form component is placed in a <pre></pre> tag.
1113 * Returns:
1114 * a newly allocated word upon success. NULL on failure.
1115 *****/
1116 static XmHTMLWord*
TextAreaToWord(XmHTMLWidget html,XmHTMLObject * start,int * num_words,Dimension * width,Dimension * height,XmHTMLObjectTableElement owner,Boolean formatted)1117 TextAreaToWord(XmHTMLWidget html, XmHTMLObject *start, int *num_words,
1118 Dimension *width, Dimension *height, XmHTMLObjectTableElement owner,
1119 Boolean formatted)
1120 {
1121 static XmHTMLForm *form_entry;
1122 XmHTMLWord *word;
1123 String text = NULL;
1124 Byte foo;
1125 Boolean i18n;
1126
1127 *num_words = 0;
1128 *height = *width = 0;
1129
1130 /* sanity check */
1131 if(start->attributes == NULL)
1132 return(NULL);
1133
1134 /* get text between opening and closing <textarea>, if any */
1135 if(start->next->id == HT_ZTEXT)
1136 text = CopyText(html, start->next->element, True, &foo, False, &i18n);
1137
1138 /* create new form entry. text will serve as the default content */
1139 if((form_entry = _XmHTMLFormAddTextArea(html, start->attributes,
1140 text)) == NULL)
1141 {
1142 if(text)
1143 free(text);
1144 return(NULL);
1145 }
1146 form_entry->data = owner;
1147
1148 /* allocate new word for this form member */
1149 word = allocFormWord(html, form_entry, width, height, owner, formatted);
1150
1151 _XmHTMLFullDebug(2, ("format.c: TextAreaToWord, word is %s, len is %i, "
1152 "width is %i, height is %i\n", word->word, word->len,
1153 word->width, word->height));
1154
1155 *num_words = 1;
1156 return(word);
1157 }
1158
1159 /*****
1160 * Name: indexToWord
1161 * Return Type: XmHTMLWord
1162 * Description: creates a prefix for numbered lists with the ISINDEX
1163 * attribute set.
1164 * In:
1165 * html: XmHTMLWidget id;
1166 * list_stack: stack of all lists;
1167 * current...: current list id;
1168 * owner: owning element.
1169 * formatted: true when this form component is placed in a <pre></pre> tag.
1170 * Returns:
1171 * a newly allocated word.
1172 * Note:
1173 * This routine creates the prefix based on the type and depth of the
1174 * current list. All types can be intermixed, so this routine is capable
1175 * of returning something like 1.A.IV.c.iii for a list nested five levels,
1176 * the first with type `1', second with type `A', third with type `I',
1177 * fourth with type `a' and fifth with type `i'.
1178 *****/
1179 static XmHTMLWord*
indexToWord(XmHTMLWidget html,listStack list_stack[],int current_list,XmHTMLObjectTableElement owner,Boolean formatted)1180 indexToWord(XmHTMLWidget html, listStack list_stack[], int current_list,
1181 XmHTMLObjectTableElement owner, Boolean formatted)
1182 {
1183 static XmHTMLWord *word;
1184 int i;
1185 char index[128], number[128]; /* enough for a zillion numbers & depths */
1186
1187 word = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));
1188
1189 (void)memset(&index, '\0', 128);
1190 for(i = 0; i < current_list; i++)
1191 {
1192 if(list_stack[i].type == HT_OL)
1193 {
1194 switch(list_stack[i].marker)
1195 {
1196 case XmMARKER_ALPHA_LOWER:
1197 sprintf(number, "%s.", ToAsciiLower(list_stack[i].level));
1198 break;
1199 case XmMARKER_ALPHA_UPPER:
1200 sprintf(number, "%s.", ToAsciiUpper(list_stack[i].level));
1201 break;
1202 case XmMARKER_ROMAN_LOWER:
1203 sprintf(number, "%s.", ToRomanLower(list_stack[i].level));
1204 break;
1205 case XmMARKER_ROMAN_UPPER:
1206 sprintf(number, "%s.", ToRomanUpper(list_stack[i].level));
1207 break;
1208 case XmMARKER_ARABIC:
1209 default:
1210 sprintf(number, "%i.", list_stack[i].level);
1211 break;
1212 }
1213 /* no buffer overflow */
1214 if(strlen(index) + strlen(number) > 128)
1215 break;
1216 strcat(index, number);
1217 }
1218 }
1219
1220 /* fill in required fields */
1221 word->word = strdup(index);
1222 word->len = strlen(index);
1223 word->self = word; /* unused */
1224 word->owner = owner; /* unused */
1225 word->font = HTML_ATTR(default_font); /* unused */
1226 word->spacing = formatted ? 0 : TEXT_SPACE_NONE;
1227 word->type = OBJ_TEXT; /* unused */
1228 word->line_data = NO_LINE; /* unused */
1229
1230 return(word);
1231 }
1232
1233 /*****
1234 * Name: TextToPre
1235 * Return Type: XmHTMLWord*
1236 * Description: splits the given text into an array of preformatted lines
1237 * In:
1238 * text: text to split
1239 * num_words: number of words in the given text. Filled upon return;
1240 * font: font to use for this text.
1241 * Returns:
1242 * an array of words. When allocation fails, this routine exits.
1243 * Note:
1244 * the static var nchars is used to propagate the tab index to another
1245 * chunk of preformatted text if the current text is a block of preformatted
1246 * text with whatever formatting. It is only reset if an explicit newline
1247 * is encountered.
1248 *****/
1249 static XmHTMLWord*
TextToPre(String text,int * num_words,XmHTMLfont * font,Byte line_data,XmHTMLObjectTableElement owner,ToolkitAbstraction * tka,int tabwidth)1250 TextToPre(String text, int *num_words, XmHTMLfont *font, Byte line_data,
1251 XmHTMLObjectTableElement owner, ToolkitAbstraction *tka, int tabwidth)
1252 {
1253 int nwords, len, i, j, ntabs, max_width, in_word, size, nfeeds;
1254 static char *raw;
1255 static XmHTMLWord *words;
1256 static int nchars = 0;
1257 register char *chPtr, *start, *end;
1258 #ifdef DEBUG
1259 int used;
1260 #endif
1261
1262 /* sanity check */
1263 if(text == NULL)
1264 {
1265 *num_words = 0;
1266 return(NULL);
1267 }
1268
1269 _XmHTMLFullDebug(2, ("format.c: TextToPre, text in is:\n%s\n", text));
1270
1271 chPtr = text;
1272 raw = NULL;
1273
1274 /*****
1275 * compute how many words we have. A preformatted word is started
1276 * with a printing char and is terminated by either a newline or a
1277 * sequence of whitespaces. Multiple newlines are collapsed into a
1278 * single word where the height of the word indicates the number of
1279 * newlines to insert.
1280 *****/
1281 in_word = nwords = ntabs = 1; /* fix 01/30/97-02, kdh */
1282 while(True)
1283 {
1284 switch(*chPtr)
1285 {
1286 /* tabs and single spaces are collapsed */
1287 case '\t': /* horizontal tab */
1288 case ' ':
1289 if(in_word)
1290 {
1291 while(*chPtr != '\0' && (*chPtr == ' ' || *chPtr == '\t'))
1292 {
1293 if(*chPtr == '\t')
1294 ntabs++; /* need to know how many to expand */
1295 chPtr++;
1296 }
1297 nwords++;
1298 in_word = False;
1299 }
1300 else
1301 {
1302 /* fix 03/23/97-01, kdh */
1303 if(*chPtr == '\t')
1304 ntabs++; /* need to know how many to expand */
1305 chPtr++;
1306 }
1307 break;
1308 /* newlines reset the tab index and are collapsed */
1309 case '\n':
1310 while(*chPtr != '\0' && *chPtr == '\n')
1311 chPtr++;
1312 nwords++; /* current word is terminated */
1313 nchars = 1;
1314 break;
1315 default:
1316 chPtr++;
1317 in_word = True;
1318 break;
1319 }
1320 if(*chPtr == '\0')
1321 break;
1322 }
1323
1324 /* sanity check */
1325 if(nwords == 0)
1326 {
1327 *num_words = 0;
1328 return(NULL);
1329 }
1330
1331 /* add an extra word and tab for safety */
1332 nwords++; /* preformatted text with other formatting *needs* this */
1333 ntabs++;
1334
1335 /* compute amount of memory to allocate */
1336 size = ((ntabs*tabwidth)+strlen(text)+1)*sizeof(char);
1337
1338 raw = (char*)malloc(size);
1339
1340 _XmHTMLDebug(2, ("format.c: TextToPre, allocated %i bytes\n", size));
1341
1342 /* allocate memory for all words */
1343 words = (XmHTMLWord*)calloc(nwords, sizeof(XmHTMLWord));
1344
1345 chPtr = text;
1346 end = raw;
1347
1348 /*****
1349 * If the previous element is also part of a pre-formatted object
1350 * that doesn't contain a newline, we need to get it's length.
1351 * This is required for proper continuation of horizontal tabs.
1352 * If we do not do this, tabulation will be incorrect.
1353 *****/
1354 if(owner->prev->object_type == OBJ_PRE_TEXT)
1355 {
1356 XmHTMLWord lastw = owner->prev->words[owner->prev->n_words-1];
1357
1358 if(!lastw.spacing)
1359 {
1360 /* prevent divide by zero */
1361 int sw = lastw.font->isp ? lastw.font->isp : 3;
1362
1363 if(lastw.type == OBJ_IMG && lastw.image && lastw.image->width)
1364 nchars = lastw.image->width / sw;
1365 else if(lastw.type == OBJ_FORM && lastw.form && lastw.form->width)
1366 nchars = lastw.form->width / sw;
1367 else
1368 nchars = lastw.len;
1369 }
1370 }
1371
1372 #ifdef DEBUG
1373 used = 0;
1374 #endif
1375 /* first filter out all whitespace and other non-printing characters */
1376 while(True)
1377 {
1378 switch(*chPtr)
1379 {
1380 case '\f': /* formfeed, ignore */
1381 case '\r': /* carriage return, ignore */
1382 case '\v': /* vertical tab, ignore */
1383 chPtr++;
1384 #ifdef DEBUG
1385 used++;
1386 #endif
1387 break;
1388 case '\t': /* horizontal tab */
1389 /* no of ``floating spaces'' to emulate a tab */
1390 len = ((nchars / tabwidth) + 1) * tabwidth;
1391 for(j = 0; j < (len - nchars); j++)
1392 {
1393 *end++ = ' '; /* insert a tab */
1394 #ifdef DEBUG
1395 used++;
1396 #endif
1397 }
1398 nchars = len;
1399 #ifdef DEBUG
1400 used++;
1401 #endif
1402 chPtr++;
1403 break;
1404 /* newlines reset the tab index */
1405 case '\n':
1406 nchars = 0; /* reset tab spacing index */
1407 /* fall thru */
1408 default:
1409 nchars++;
1410 *end++ = *chPtr++;
1411 #ifdef DEBUG
1412 used++;
1413 #endif
1414 break;
1415 }
1416 if(*chPtr == '\0') /* terminate loop */
1417 {
1418 *end = '\0';
1419 break;
1420 }
1421 }
1422 _XmHTMLDebug(2, ("format.c: TextToPre, %i bytes actually used\n", used));
1423
1424 /* Now go and fill all words */
1425 start = end = raw;
1426 max_width = i = len = 0;
1427 nfeeds = 0;
1428
1429 while(True)
1430 {
1431 /* also pick up the last word! */
1432 if(*end == ' ' || *end == '\n' || *end == '\0')
1433 {
1434 if(*end)
1435 {
1436 /* skip past all spaces */
1437 while(*end != '\0' && *end != '\n' && *end == ' ')
1438 {
1439 end++;
1440 len++;
1441 }
1442
1443 /*****
1444 * if this word is ended by a newline, remove the newline.
1445 * X doesn't know how to interpret them.
1446 * We also want to recognize multiple newlines, so we must
1447 * skip past them.
1448 *****/
1449 if(*end == '\n')
1450 {
1451 while(*end != '\0' && *end == '\n')
1452 {
1453 nfeeds++;
1454 *end++ = '\0';
1455 }
1456 /*****
1457 * Since the no of newlines to add is stored in a
1458 * Byte, we need to limit the no of newlines to the
1459 * max. value a Byte can have: 255 (= 2^8)
1460 *****/
1461 if(nfeeds > 255)
1462 nfeeds = 255;
1463 }
1464 }
1465
1466 words[i].type = OBJ_TEXT;
1467 words[i].self = &words[i];
1468 words[i].word = start;
1469 words[i].height = font->height;
1470 words[i].owner = owner;
1471 words[i].spacing = (Byte)nfeeds; /* no of newlines */
1472 words[i].font = font;
1473 words[i].line_data = line_data;
1474 words[i].len = len;
1475 words[i].width = tka->TextWidth(font, words[i].word, len);
1476 start = end;
1477 i++;
1478 len = 0;
1479 nfeeds = 0;
1480 }
1481 if(*end == '\0') /* terminate loop */
1482 break;
1483 end++; /* move to the next char */
1484 len++;
1485 }
1486
1487 _XmHTMLDebug(2, ("format.c: TexToPre, allocated %i words, %i actually "
1488 "used\n", nwords, i));
1489
1490 /* total no of words */
1491 *num_words = i;
1492 return(words);
1493 }
1494
1495 static XmHTMLWord*
BreakToWord(Dimension * height,XmHTMLfont * font,int linefeed,XmHTMLObjectTableElement owner)1496 BreakToWord(Dimension *height, XmHTMLfont *font, int linefeed,
1497 XmHTMLObjectTableElement owner)
1498 {
1499 static XmHTMLWord *word;
1500
1501 word = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));
1502
1503 word->type = OBJ_BLOCK;
1504 word->self = word;
1505 word->word = (String)malloc(1); /* needs an empty word */
1506 word->word[0] = '\0';
1507 word->len = 0;
1508 word->height = font->height; /* height of current font */
1509 word->owner = owner;
1510 word->spacing = TEXT_SPACE_NONE; /* obviously no spacing */
1511 word->line_data = linefeed+1; /* how much lines to break */
1512 word->font = font;
1513
1514 return(word);
1515 }
1516
1517 static XmHTMLWord*
MakeDummyWord(Dimension * height,XmHTMLfont * font,Byte line_data,XmHTMLObjectTableElement owner)1518 MakeDummyWord(Dimension *height, XmHTMLfont *font, Byte line_data,
1519 XmHTMLObjectTableElement owner)
1520 {
1521 static XmHTMLWord *word;
1522
1523 word = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));
1524
1525 word->type = OBJ_TEXT;
1526 word->self = word;
1527 word->word = (String)malloc(1); /* needs an empty word */
1528 word->word[0] = '\0';
1529 word->len = 0;
1530 word->height = *height = font->height; /* height of current font */
1531 word->owner = owner;
1532 word->line_data = line_data;
1533 word->font = font;
1534 /* an empty word acts as a single space */
1535 word->spacing = TEXT_SPACE_LEAD|TEXT_SPACE_TRAIL;
1536
1537 return(word);
1538 }
1539
1540
1541 /*****
1542 * Name: SetTab
1543 * Return Type: XmHTMLWord*
1544 * Description: returns a XmHTMLWord with spaces required for a tab.
1545 * In:
1546 * Returns:
1547 * the tab.
1548 *****/
1549 static XmHTMLWord*
SetTab(int size,Dimension * height,XmHTMLfont * font,XmHTMLObjectTableElement owner,ToolkitAbstraction * tka)1550 SetTab(int size, Dimension *height, XmHTMLfont *font,
1551 XmHTMLObjectTableElement owner, ToolkitAbstraction *tka)
1552 {
1553 static XmHTMLWord *tab;
1554 static char *raw;
1555
1556 /* The tab itself */
1557 raw = (char*)malloc((size+1)*sizeof(char));
1558
1559 /* fill with spaces */
1560 (void)memset(raw, ' ', size);
1561 raw[size] = '\0'; /* NULL terminate */
1562
1563 tab = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));
1564
1565 /* Set all text fields for this tab */
1566 tab->self = tab;
1567 tab->word = raw;
1568 tab->len = size;
1569 tab->height = *height = font->height;
1570 tab->width = tka->TextWidth(font, raw, size);
1571 tab->owner = owner;
1572 tab->spacing = TEXT_SPACE_NONE; /* a tab is already spacing */
1573 tab->font = font;
1574 tab->type = OBJ_TEXT;
1575 tab->line_data = NO_LINE;
1576
1577 return(tab);
1578 }
1579
1580 /*****
1581 * Name: CopyText
1582 * Return Type: String
1583 * Description: copies the given text to a newly malloc'd buffer.
1584 * In:
1585 * html: XmHTMLWidget id;
1586 * text: text to clean out.
1587 * formatted: true when this text occurs inside <pre></pre>
1588 * text_data: text option bits, spacing and such
1589 * expand_escapes:
1590 * True -> expand escape sequences in text. Only viable when
1591 * copying pre-formatted text (plain text documents are handled
1592 * internally as consisting completely of preformatted text for
1593 * which the escapes may not be expanded).
1594 * Returns:
1595 * cleaned up text. Terminates if malloc fails.
1596 *****/
1597 static String
CopyText(XmHTMLWidget html,String text,Boolean formatted,Byte * text_data,Boolean expand_escapes,Boolean * i18n)1598 CopyText(XmHTMLWidget html, String text, Boolean formatted, Byte *text_data,
1599 Boolean expand_escapes, Boolean *i18n)
1600 {
1601 static String ret_val;
1602 char *start = text;
1603 int len;
1604 #ifdef I18N
1605 char *chPtr, *end;
1606 int n;
1607 *i18n = False; /* assume no multibyte text yet */
1608 #endif /* I18N */
1609
1610 /* sanity check */
1611 if(*text == '\0' || !strlen(text))
1612 return(NULL);
1613
1614 /* preformatted text, just copy and return */
1615 if(formatted)
1616 {
1617 /* formatted text resets spacing */
1618 *text_data = TEXT_SPACE_NONE;
1619 ret_val = strdup(text);
1620 /* expand all escape sequences in this text */
1621 if(expand_escapes)
1622 _XmHTMLExpandEscapes(ret_val, HTML_ATTR(bad_html_warnings));
1623 return(ret_val);
1624 }
1625
1626 _XmHTMLFullDebug(2, ("format.c: CopyText, text in is:\n%s\n", text));
1627
1628 /* initial length of full text */
1629 len = strlen(text);
1630
1631 /*****
1632 * Check interword spacing.
1633 * If we've just had an explicit break, only check for possible trailing
1634 * spaces.
1635 *****/
1636 if(*text_data & TEXT_BREAK && isspace(text[len-1]))
1637 *text_data |= TEXT_SPACE_TRAIL;
1638 else
1639 {
1640 /* unset no-space bit */
1641 *text_data &= ~TEXT_SPACE_NONE;
1642 /*****
1643 * If first char is a space or the previous chunk had a trailing space,
1644 * this chunk gets a leading space. Else it's glueud to the previous
1645 * chunk.
1646 *****/
1647 if((isspace(*text) || *text_data & TEXT_SPACE_TRAIL))
1648 *text_data = TEXT_SPACE_LEAD;
1649 else
1650 *text_data &= ~TEXT_SPACE_LEAD;
1651
1652 /* do we have a trailing space? */
1653 if(isspace(text[len-1]))
1654 *text_data |= TEXT_SPACE_TRAIL;
1655 else
1656 *text_data &= ~TEXT_SPACE_TRAIL;
1657
1658 /* set no-space bit if we don't have any spaces */
1659 if(!(*text_data & TEXT_SPACE_TRAIL) && !(*text_data & TEXT_SPACE_LEAD))
1660 *text_data |= TEXT_SPACE_NONE;
1661 }
1662
1663 /*****
1664 * Now remove leading/trailing spaces
1665 * very special case: spaces between different text formatting
1666 * elements must be retained
1667 *****/
1668 /* remove all leading space */
1669 #ifdef I18N
1670 while(*start != '\0' && isspace(*start))
1671 {
1672 /* compute size of this character */
1673 n = mblen((char*)text, (size_t)(strlen(start)));
1674 if(n > 1)
1675 break; /* not a space */
1676 if(n > 0)
1677 start = start + n; /* a space, skip to next multibyte char */
1678 else
1679 break; /* break on error */
1680 }
1681
1682 /* remove all trailing space */
1683 chPtr = start;
1684 n = mblen((char*)start, (size_t)(strlen(start)));
1685 end = (char*)NULL;
1686
1687 while(n > 0)
1688 {
1689 if(n == 1)
1690 {
1691 /* not a multibyte character */
1692 if(!isspace(*chPtr))
1693 end = (char*)NULL; /* last char not a space, move to next */
1694 else if(end == NULL)
1695 end = chPtr; /* possible end space */
1696 }
1697 else if(n > 1)
1698 {
1699 int z;
1700 end = (char*)NULL; /* multibyte char, not a space */
1701 *i18n = True; /* we have found multibyte text */
1702 }
1703 else
1704 break; /* break on error */
1705
1706 /* compute size of next char */
1707 n = mblen((char*)chPtr, (size_t)(strlen(chPtr)));
1708 if(n > 0)
1709 {
1710 chPtr += n; /* move to next char */
1711 }
1712 }
1713 #else
1714 while(*start != '\0' && isspace(*start))
1715 start++;
1716
1717 /* remove all trailing space */
1718 len = strlen(start);
1719 while(len > 0 && isspace(start[len-1]))
1720 len--;
1721
1722 #endif /* I18N */
1723
1724 /*****
1725 * Spaces *can* appear between different text formatting elements.
1726 * We want to retain this spacing since the above whitespace checking
1727 * only yields the current element, and does not take the previous text
1728 * element into account, *UNLESS* we just had a break.
1729 *
1730 * So when the current element doesn't have any leading or trailing spaces,
1731 * we use the spacing from the previous full whitespace element.
1732 *****/
1733 if(!len)
1734 {
1735 if(*text_data & TEXT_BREAK)
1736 {
1737 *text_data &= (~TEXT_BREAK & ~TEXT_SPACE_TRAIL);
1738 *text_data |= TEXT_SPACE_NONE;
1739 }
1740 return(NULL);
1741 }
1742
1743 /* release break bit, no longer needed */
1744 *text_data &= ~TEXT_BREAK;
1745
1746 /*****
1747 * We are a little bit to generous here: consecutive multiple whitespace
1748 * will be collapsed into a single space, so we may over-allocate.
1749 * Hey, better to overdo this than to have one byte to short ;-)
1750 *****/
1751 ret_val = (String)malloc((len+1)*sizeof(char));
1752 strncpy(ret_val, start, len); /* copy it */
1753 ret_val[len] = '\0'; /* NULL terminate */
1754
1755 /* expand all escape sequences in this text */
1756 if(expand_escapes)
1757 _XmHTMLExpandEscapes(ret_val, HTML_ATTR(bad_html_warnings));
1758
1759 #ifdef I18N
1760 if(*i18n == True)
1761 fprintf(stderr, "found multibyte text!\n");
1762 #endif
1763 return(ret_val);
1764 }
1765
1766 /*****
1767 * Name: ParseBodyTags
1768 * Return Type: void
1769 * Description: checks the <BODY> element for additional tags
1770 * In:
1771 * w: HTML widget to check
1772 * data: body element data.
1773 * Returns:
1774 * nothing, but the HTML widget is updated to reflect the body stuff.
1775 *****/
1776 static void
ParseBodyTags(XmHTMLWidget html,XmHTMLObject * data)1777 ParseBodyTags(XmHTMLWidget html, XmHTMLObject *data)
1778 {
1779 char *chPtr;
1780 Boolean bg_color_set = False; /* flag for bodyImage substitution */
1781
1782 /* Check for HTML4.0 body events */
1783 HTML_ATTR(body_events) = _XmHTMLCheckBodyEvents(html, data->attributes,
1784 &(HTML_ATTR(event_mask)));
1785
1786 /* check all body color tags */
1787 if(HTML_ATTR(body_colors_enabled) && data->attributes != NULL)
1788 {
1789 Boolean doit = True;
1790
1791 if((chPtr = _XmHTMLTagGetValue(data->attributes, "text")))
1792 {
1793 if(HTML_ATTR(strict_checking))
1794 doit = _XmHTMLConfirmColor32(chPtr);
1795
1796 if(doit)
1797 HTML_ATTR(body_fg) = _XmHTMLGetPixelByName(html, chPtr,
1798 HTML_ATTR(body_fg_save));
1799 free(chPtr);
1800
1801 /* also set as foreground for the entire widget */
1802 MGR_ATTR(foreground) = HTML_ATTR(body_fg);
1803 }
1804
1805 if(doit && (chPtr = _XmHTMLTagGetValue(data->attributes, "bgcolor")))
1806 {
1807 bg_color_set = True;
1808
1809 if(HTML_ATTR(strict_checking))
1810 doit = _XmHTMLConfirmColor32(chPtr);
1811
1812 /* only change if we had success */
1813 if(doit)
1814 {
1815 HTML_ATTR(body_bg) = _XmHTMLGetPixelByName(html, chPtr,
1816 HTML_ATTR(body_bg_save));
1817
1818 /* also set as background for the entire widget */
1819 CORE_ATTR(background_pixel) = HTML_ATTR(body_bg);
1820
1821 /* get new values for top, bottom & highlight */
1822 XmHTMLTkaRecomputeColors(html, HTML_ATTR(body_bg));
1823 }
1824
1825 free(chPtr);
1826 }
1827
1828 if(doit && (chPtr = _XmHTMLTagGetValue(data->attributes, "link")))
1829 {
1830 if(HTML_ATTR(strict_checking))
1831 doit = _XmHTMLConfirmColor32(chPtr);
1832
1833 if(doit)
1834 HTML_ATTR(anchor_fg) = _XmHTMLGetPixelByName(html, chPtr,
1835 HTML_ATTR(anchor_fg_save));
1836 free(chPtr);
1837 }
1838
1839 if(doit && (chPtr = _XmHTMLTagGetValue(data->attributes, "vlink")))
1840 {
1841 if(HTML_ATTR(strict_checking))
1842 doit = _XmHTMLConfirmColor32(chPtr);
1843 if(doit)
1844 HTML_ATTR(anchor_visited_fg) = _XmHTMLGetPixelByName(html,
1845 chPtr, HTML_ATTR(anchor_visited_fg_save));
1846 free(chPtr);
1847 }
1848
1849 if(doit && (chPtr = _XmHTMLTagGetValue(data->attributes, "alink")))
1850 {
1851 if(HTML_ATTR(strict_checking))
1852 doit = _XmHTMLConfirmColor32(chPtr);
1853 if(doit)
1854 HTML_ATTR(anchor_activated_fg) = _XmHTMLGetPixelByName(html,
1855 chPtr, HTML_ATTR(anchor_activated_fg_save));
1856 free(chPtr);
1857 }
1858 /*****
1859 * an invalid color spec, ignore them all together and revert to
1860 * saved settings
1861 *****/
1862 if(doit == False)
1863 {
1864 /* first check if we changed the background color */
1865 if(CORE_ATTR(background_pixel) != HTML_ATTR(body_bg_save))
1866 {
1867 HTML_ATTR(body_fg) = HTML_ATTR(body_fg_save);
1868 HTML_ATTR(body_bg) = HTML_ATTR(body_bg_save);
1869 MGR_ATTR(foreground) = HTML_ATTR(body_fg);
1870 CORE_ATTR(background_pixel) = HTML_ATTR(body_bg);
1871
1872 /* restore values for top, bottom & highlight */
1873 XmHTMLTkaRecomputeColors(html, HTML_ATTR(body_bg));
1874 }
1875
1876 HTML_ATTR(body_fg) = HTML_ATTR(body_fg_save);
1877 HTML_ATTR(body_bg) = HTML_ATTR(body_bg_save);
1878 HTML_ATTR(anchor_fg) = HTML_ATTR(anchor_fg_save);
1879 HTML_ATTR(anchor_visited_fg) = HTML_ATTR(anchor_visited_fg_save);
1880 HTML_ATTR(anchor_activated_fg)=HTML_ATTR(anchor_activated_fg_save);
1881 MGR_ATTR(foreground) = HTML_ATTR(body_fg);
1882 bg_color_set = False;
1883 }
1884 }
1885
1886 /* Check background image spec. First invalidate any existing body image */
1887 if(HTML_ATTR(body_image))
1888 HTML_ATTR(body_image->options) |= IMG_ORPHANED;
1889 HTML_ATTR(body_image) = (XmHTMLImage*)NULL;
1890 HTML_ATTR(body_image_url) = NULL;
1891
1892 /*
1893 * *ALWAYS* load the body image if we want the SetValues method to
1894 * behave itself.
1895 */
1896 if(data->attributes &&
1897 (chPtr = _XmHTMLTagGetValue(data->attributes, "background")))
1898 {
1899 _XmHTMLLoadBodyImage(html, chPtr);
1900 /* store document's body image location */
1901 if(HTML_ATTR(body_image))
1902 HTML_ATTR(body_image_url) = HTML_ATTR(body_image->url);
1903 free(chPtr);
1904 }
1905 /*
1906 * Use default body image if present *and* if no background color
1907 * has been set.
1908 */
1909 else if(!bg_color_set && HTML_ATTR(def_body_image_url))
1910 _XmHTMLLoadBodyImage(html, HTML_ATTR(def_body_image_url));
1911
1912 /*****
1913 * Now nullify it if we aren't to show the background image.
1914 * makes sense huh? (the list of images is a global widget resource so the
1915 * storage occupied by this unused image is freed when all document
1916 * data is freed).
1917 *****/
1918 if(!HTML_ATTR(images_enabled) || !HTML_ATTR(body_images_enabled))
1919 {
1920 if(HTML_ATTR(body_image))
1921 HTML_ATTR(body_image->options) |= IMG_ORPHANED;
1922 HTML_ATTR(body_image) = NULL;
1923 }
1924 /*****
1925 * When a body image is present it is very likely that a highlight
1926 * color based upon the current background actually makes an anchor
1927 * invisible when highlighting is selected. Therefore we base the
1928 * highlight color on the activated anchor background when we have a body
1929 * image, and on the document background when no body image is present.
1930 *****/
1931 if(HTML_ATTR(body_image))
1932 XmHTMLTkaRecomputeHighlightColor(html, HTML_ATTR(anchor_activated_fg));
1933 else
1934 XmHTMLTkaRecomputeHighlightColor(html, HTML_ATTR(body_bg));
1935 }
1936
1937 static void
FillBullet(XmHTMLWidget html,XmHTMLObjectTableElement owner,ToolkitAbstraction * tka)1938 FillBullet(XmHTMLWidget html, XmHTMLObjectTableElement owner,
1939 ToolkitAbstraction *tka)
1940 {
1941 Dimension radius;
1942 char number[128]; /* large enough buffer for a zillion numbers */
1943 XmHTMLfont *font = HTML_ATTR(default_font);
1944 String prefix;
1945
1946 /*****
1947 * x-offset for any marker and radius for a bullet or length of a
1948 * side for a square marker.
1949 *****/
1950 radius = (Dimension)(0.5*(font->m_lbearing + font->m_rbearing));
1951
1952 if(owner->marker == XmMARKER_DISC || owner->marker == XmMARKER_SQUARE ||
1953 owner->marker == XmMARKER_CIRCLE)
1954 {
1955 /* y-offset for this marker */
1956 owner->height = (Dimension)(0.5*font->lineheight + 0.25*radius);
1957 owner->width = radius;
1958 }
1959 else
1960 {
1961 /*****
1962 * If we have a word, this is an ordered list for which the index
1963 * should be propageted.
1964 *****/
1965 if(owner->words)
1966 prefix = owner->words[0].word;
1967 else
1968 prefix = "";
1969 switch(owner->marker)
1970 {
1971 case XmMARKER_ALPHA_LOWER:
1972 sprintf(number, "%s%s.", prefix,
1973 ToAsciiLower(owner->list_level));
1974 break;
1975 case XmMARKER_ALPHA_UPPER:
1976 sprintf(number, "%s%s.", prefix,
1977 ToAsciiUpper(owner->list_level));
1978 break;
1979 case XmMARKER_ROMAN_LOWER:
1980 sprintf(number, "%s%s.", prefix,
1981 ToRomanLower(owner->list_level));
1982 break;
1983 case XmMARKER_ROMAN_UPPER:
1984 sprintf(number, "%s%s.", prefix,
1985 ToRomanUpper(owner->list_level));
1986 break;
1987 case XmMARKER_ARABIC:
1988 default:
1989 sprintf(number, "%s%i.", prefix, owner->list_level);
1990 break;
1991 }
1992 owner->text = strdup(number);
1993 owner->len = strlen(number);
1994 owner->width = radius + tka->TextWidth(font, owner->text, owner->len);
1995 owner->height = HTML_ATTR(default_font)->height;
1996 }
1997 }
1998
1999 /*****
2000 * Name: CheckLineFeed
2001 * Return Type: int
2002 * Description: checks wether the requested newline is honored.
2003 * In:
2004 * op: newline to add.
2005 * force: add the requested newline anyway
2006 * text_data: current trailing/leading spacing
2007 * Returns:
2008 * computed vertical pixel offset.
2009 * Note:
2010 * any of CLEAR_NONE, CLEAR_SOFT or CLEAR_HARD
2011 *****/
2012 static int
CheckLineFeed(int op,Boolean force,Byte * text_data)2013 CheckLineFeed(int op, Boolean force, Byte *text_data)
2014 {
2015 static int prev_state = CLEAR_NONE;
2016 int ret_val = op;
2017
2018 /* anything except no-op clears spacing */
2019 if(op != CLEAR_NONE)
2020 {
2021 *text_data &= (~TEXT_SPACE_LEAD & ~TEXT_SPACE_TRAIL);
2022 *text_data |= TEXT_SPACE_NONE;
2023 }
2024
2025 if(force)
2026 {
2027 prev_state = op;
2028 return(ret_val);
2029 }
2030
2031 /* multiple soft and hard returns are never honored */
2032 switch(op)
2033 {
2034 case CLEAR_HARD:
2035 if(prev_state == CLEAR_SOFT)
2036 {
2037 ret_val = CLEAR_SOFT;
2038 prev_state = CLEAR_HARD;
2039 break;
2040 }
2041 if(prev_state == CLEAR_HARD)
2042 {
2043 /* unchanged */
2044 ret_val = CLEAR_NONE;
2045 break;
2046 }
2047 prev_state = ret_val = op;
2048 break;
2049 case CLEAR_SOFT:
2050 if(prev_state == CLEAR_SOFT)
2051 {
2052 ret_val = CLEAR_NONE;
2053 prev_state = CLEAR_SOFT;
2054 break;
2055 }
2056 if(prev_state == CLEAR_HARD)
2057 {
2058 /* unchanged */
2059 ret_val = CLEAR_NONE;
2060 break;
2061 }
2062 ret_val = prev_state = op;
2063 break;
2064 case CLEAR_NONE:
2065 ret_val = prev_state = op;
2066 break;
2067 }
2068 return(ret_val);
2069 }
2070
2071 /*****
2072 * Name: tableCheckProperties
2073 * Return Type: TableProperties
2074 * Description: scans a table element for common properties (border,
2075 * alignment, background and border styles).
2076 * In:
2077 * html: XmHTMLWidget id;
2078 * attributes: attributes to be checked;
2079 * parent: properties of parent table element. Properties not found
2080 * in the attributes are inherited from this parent.
2081 * Returns:
2082 * a new property.
2083 *****/
2084 static TableProperties*
tableCheckProperties(XmHTMLWidget html,String attributes,TableProperties * parent,Alignment halign,Pixel bg)2085 tableCheckProperties(XmHTMLWidget html, String attributes,
2086 TableProperties *parent, Alignment halign, Pixel bg)
2087 {
2088 TableProperties prop;
2089 static TableProperties *prop_ret;
2090 String chPtr;
2091
2092 prop_ret = (TableProperties*)malloc(sizeof(TableProperties));
2093 memset(prop_ret, 0, sizeof(TableProperties));
2094
2095 if(parent)
2096 memcpy(&prop, parent, sizeof(TableProperties));
2097 else
2098 {
2099 /* defaults assume a table without any borders */
2100 prop.border = -1;
2101 prop.halign = halign; /* unused */
2102 prop.valign = XmVALIGN_TOP; /* contents on top */
2103 prop.bg = bg; /* propagate */
2104 prop.bg_image = NULL;
2105 prop.framing = TFRAME_VOID; /* no border */
2106 prop.ruling = TRULE_NONE; /* no ruling */
2107 prop.nowrap = False; /* wrap long lines */
2108 }
2109
2110 /* Check for possible attributes */
2111 if(attributes)
2112 {
2113 /*****
2114 * Horizontal alignment is only inherited through the halign argument:
2115 * the align attribute on the table tag applies to the table in a whole,
2116 * not to any of it's members.
2117 *****/
2118 prop_ret->halign = _XmHTMLGetHorizontalAlignment(attributes, halign);
2119 prop_ret->valign = _XmHTMLGetVerticalAlignment(attributes,
2120 prop.valign);
2121 prop_ret->nowrap = _XmHTMLTagCheck(attributes, "nowrap");
2122
2123 /*****
2124 * Border value. If -1 is returned, check for the presence of the word
2125 * ``border'' in the attributes. If it exists, we assume a non-zero
2126 * border width.
2127 *****/
2128 prop_ret->border = _XmHTMLTagGetNumber(attributes, "border",
2129 prop.border);
2130 /* if the word ``border'' is present, use default border width */
2131 if(prop_ret->border == -1 && _XmHTMLTagCheck(attributes, "border"))
2132 prop_ret->border = XmHTML_DEFAULT_TABLE_BORDERWIDTH;
2133
2134 /*****
2135 * Framing applies per-table. If border is non-zero, the default is
2136 * to render a fully framed table.
2137 * If a TFRAME_VOID is returned, discard the border width.
2138 *****/
2139 prop_ret->framing = _XmHTMLGetFraming(attributes,
2140 prop_ret->border > 0 ? TFRAME_BOX : prop.framing);
2141 if(prop_ret->framing == TFRAME_VOID)
2142 prop_ret->border = 0;
2143
2144 /*****
2145 * Ruling applies per-cell. If border is non-zero, the default is to
2146 * render full borders around this cell.
2147 * If a TRULE_NONE is returned, discard the border width.
2148 *****/
2149 prop_ret->ruling = _XmHTMLGetRuling(attributes,
2150 prop_ret->border ? TRULE_ALL : prop.ruling);
2151
2152 if(prop_ret->ruling == TRULE_NONE)
2153 prop_ret->border = 0;
2154
2155 /*****
2156 * only pick up background color if we are allowed to honor this attrib
2157 *****/
2158 if(HTML_ATTR(allow_color_switching) &&
2159 (chPtr = _XmHTMLTagGetValue(attributes, "bgcolor")) != NULL)
2160 {
2161 Boolean doit = True;
2162 if(HTML_ATTR(strict_checking))
2163 doit = _XmHTMLConfirmColor32(chPtr);
2164 if(doit)
2165 prop_ret->bg = _XmHTMLGetPixelByName(html, chPtr, prop.bg);
2166 free(chPtr);
2167 }
2168 else
2169 prop_ret->bg = prop.bg;
2170
2171 /* table background image? */
2172 if((chPtr = _XmHTMLTagGetValue(attributes, "background")))
2173 {
2174 String buf;
2175 Dimension width, height;
2176 XmHTMLImage *image;
2177
2178 /* kludge so _XmHTMLNewImage recognizes it */
2179 buf = malloc(strlen(chPtr)+7);
2180 sprintf(buf, "src=\"%s\"", chPtr);
2181
2182 /* load it */
2183 if((image = _XmHTMLNewImage(html, buf, &width, &height)) != NULL)
2184 {
2185 /* animations are not allowed as background images */
2186 if(ImageIsAnim(image))
2187 image = NULL;
2188 /* and we sure won't have the default image as background */
2189 else if(ImageIsInternal(image))
2190 image = NULL;
2191 }
2192 prop_ret->bg_image = image;
2193 free(buf);
2194 free(chPtr);
2195 }
2196 else
2197 prop_ret->bg_image = prop.bg_image;
2198 }
2199 else
2200 {
2201 /* nop, no attributes, inherit from parent */
2202 prop_ret->halign = halign;
2203 prop_ret->valign = prop.valign;
2204 prop_ret->nowrap = False;
2205 prop_ret->border = prop.border;
2206 prop_ret->bg = prop.bg;
2207 prop_ret->bg_image = prop.bg_image;
2208 if((prop_ret->framing = prop_ret->border > 0 ?
2209 TFRAME_BOX : prop.framing) == TFRAME_VOID)
2210 prop_ret->border = 0;
2211 if((prop_ret->ruling = prop_ret->border ?
2212 TRULE_ALL : prop.ruling) == TRULE_NONE)
2213 prop_ret->border = 0;
2214 }
2215 return(prop_ret);
2216 }
2217
2218 /*****
2219 * Name: tableOpen
2220 * Return Type: XmHTMLTable
2221 * Description: opens a new table.
2222 * In:
2223 * html; XmHTMLWidget id;
2224 * parent: parent table. Storage for nested tables is always allocated
2225 * in this table. It is NULL when a truely new table is needed.
2226 * start: start of objects contained in this table.
2227 * obj: XmHTMLObject starting this table.
2228 * Returns:
2229 * a newly opened table.
2230 *****/
2231 static XmHTMLTable*
tableOpen(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement start,XmHTMLObject * obj,Alignment * halign,Pixel * bg)2232 tableOpen(XmHTMLWidget html, XmHTMLTable *parent,
2233 XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign,
2234 Pixel *bg)
2235 {
2236 static XmHTMLTable *table;
2237 XmHTMLTable *parent_table;
2238 XmHTMLObject *tmp;
2239 int nrows = 0; /* no of rows in table */
2240 int depth = 0;
2241 int nchilds = 0; /* no of table childs in this table */
2242 Alignment caption_position = XmVALIGN_TOP; /* where is the caption? */
2243 Boolean have_caption = False;
2244
2245 if(parent)
2246 {
2247 /* get to the absolute parent of this table */
2248 parent_table = parent;
2249 while(parent_table->parent != NULL)
2250 parent_table = parent_table->parent;
2251
2252 /* get correct ptr */
2253 parent_table = &(parent_table->childs[0]);
2254
2255 /* sanity check */
2256 if(parent_table->lastchild+1 == parent_table->nchilds)
2257 {
2258 _XmHTMLError(__WFUNC__(html, "tableOpen"),
2259 "Bad table count!!!");
2260 }
2261 /* get next available table */
2262 parent_table->lastchild++;
2263 table = &(parent_table->childs[parent_table->lastchild]);
2264 }
2265 else
2266 {
2267 table = (XmHTMLTable*)calloc(1, sizeof(XmHTMLTable));
2268 }
2269
2270 /*****
2271 * Get table attributes.
2272 * If attributes are present, set all default values to zero.
2273 * If no attributes are present, use the default values specified in
2274 * include/common/XmHTMLconf.h
2275 *****/
2276 if(obj->attributes)
2277 {
2278 table->width = _XmHTMLTagCheckNumber(obj->attributes, "width", 0);
2279 table->hmargin = _XmHTMLTagGetNumber(obj->attributes, "cellspacing",
2280 0);
2281 table->hpadding = _XmHTMLTagGetNumber(obj->attributes, "cellpadding",
2282 0);
2283 table->ncols = _XmHTMLTagGetNumber(obj->attributes, "cols", 0);
2284
2285 /* no negative margin, padding or columns */
2286 if(table->hmargin < 0)
2287 table->hmargin = 0;
2288 if(table->hpadding < 0)
2289 table->hpadding = 0;
2290 if(table->ncols < 0)
2291 table->ncols = 0;
2292
2293 /*****
2294 * Rowspacing and rowpadding are non-standard attributes. Use
2295 * them if they're present, use cell margins otherwise.
2296 *****/
2297 if(_XmHTMLTagCheck(obj->attributes, "rowspacing"))
2298 table->vmargin = _XmHTMLTagGetNumber(obj->attributes,
2299 "rowspacing", 0);
2300 else
2301 table->vmargin = table->hmargin;
2302 if(_XmHTMLTagCheck(obj->attributes, "rowpadding"))
2303 table->vpadding = _XmHTMLTagGetNumber(obj->attributes,
2304 "rowpadding", 0);
2305 else
2306 table->vpadding = table->hpadding;
2307 }
2308 else
2309 {
2310 table->width = 0;
2311 table->hmargin = XmHTML_DEFAULT_CELLSPACING;
2312 table->vmargin = XmHTML_DEFAULT_ROWSPACING;
2313 table->hpadding = XmHTML_DEFAULT_CELLPADDING;
2314 table->vpadding = XmHTML_DEFAULT_ROWPADDING;
2315 table->ncols = 0;
2316 }
2317 table->start = start; /* starting object */
2318 table->owner = start; /* owning object */
2319 table->parent = NULL; /* parent table */
2320
2321 /* Nested tables do not inherit their parent table properties */
2322 table->props = tableCheckProperties(html, obj->attributes,
2323 NULL, *halign, *bg);
2324
2325 /* set return alignment */
2326 *halign = table->props->halign;
2327
2328 /* set return background */
2329 *bg = table->props->bg;
2330
2331 /* count how many rows this table has */
2332 for(tmp = obj->next; tmp != NULL; tmp = tmp->next)
2333 {
2334 /* check for end of table and child tables */
2335 if(tmp->id == HT_TABLE)
2336 {
2337 if(tmp->is_end)
2338 {
2339 if(depth == 0)
2340 break;
2341 else
2342 depth--;
2343 }
2344 else /* new table opens */
2345 {
2346 depth++;
2347 nchilds++;
2348 }
2349 }
2350 /*****
2351 * only count a row when it belongs to the top-level table.
2352 * A caption is considered a special row that spans the entire table
2353 * and has only one cell: the row itself.
2354 *****/
2355 if((tmp->id == HT_TR || tmp->id == HT_CAPTION) && depth == 0 &&
2356 !tmp->is_end)
2357 {
2358 if(tmp->id == HT_CAPTION)
2359 {
2360 /*****
2361 * see where the caption should be inserted: as the first
2362 * or last row for this table.
2363 *****/
2364 String chPtr;
2365 if(tmp->attributes == NULL ||(chPtr =
2366 _XmHTMLTagGetValue(tmp->attributes, "align")) == NULL)
2367 caption_position = XmVALIGN_TOP;
2368 else
2369 {
2370 if(!(strcasecmp(chPtr, "bottom")))
2371 caption_position = XmVALIGN_BOTTOM;
2372 else
2373 caption_position = XmVALIGN_TOP;
2374 free(chPtr);
2375 }
2376 have_caption = True;
2377 }
2378 nrows++;
2379 }
2380 }
2381 /* sanity, should never happen */
2382 if(!nrows)
2383 {
2384 _XmHTMLWarning(__WFUNC__(html, "tableOpen"),
2385 "Got an empty table: no rows found");
2386 free(table->props);
2387 free(table);
2388 return(NULL);
2389 }
2390
2391 /* allocate all rows for this table */
2392 table->rows = (TableRow*)calloc(nrows, sizeof(TableRow));
2393 table->nrows = nrows;
2394 table->lastrow = 0;
2395
2396 /* set caption ptr. */
2397 if(have_caption)
2398 {
2399 if(caption_position == XmVALIGN_TOP)
2400 {
2401 table->caption = &(table->rows[0]);
2402 table->lastrow = 1;
2403 }
2404 else
2405 table->caption = &(table->rows[nrows-1]);
2406 }
2407
2408 /* The master table contains all tables */
2409 if(parent == NULL)
2410 {
2411 nchilds++;
2412 table->childs = (XmHTMLTable*)calloc(nchilds, sizeof(XmHTMLTable));
2413 table->nchilds = nchilds;
2414 table->childs[0] = *table; /* we are the first table */
2415 table->lastchild = 0;
2416 }
2417 else
2418 {
2419 table->childs = (XmHTMLTable*)NULL;
2420 table->nchilds = 0;
2421 table->lastchild = 0;
2422 /* set parent table */
2423 table->parent = parent;
2424 }
2425
2426 /* and set as table in the element given to us */
2427 start->table = table;
2428
2429 return(table);
2430 }
2431
2432 /*****
2433 * Name: tableClose
2434 * Return Type: int
2435 * Description: performs required table wrapup actions
2436 * In:
2437 * html: XmHTMLWidget id;
2438 * parent: parent table;
2439 * end: last object to be used by this table;
2440 * Returns:
2441 * -1 when this was the last table to be closed, a 0 or a positive integer
2442 * when there are still tables open.
2443 *****/
2444 static XmHTMLTable*
tableClose(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement end)2445 tableClose(XmHTMLWidget html, XmHTMLTable *parent,
2446 XmHTMLObjectTableElement end)
2447 {
2448 /* current table */
2449 XmHTMLTable *real_table, *table = parent;
2450 int i, ncols = 0;
2451
2452 /* sanity */
2453 if(parent == NULL)
2454 return(NULL);
2455
2456 #if 0
2457 /* bad hack */
2458 real_table = parent->owner->table;
2459 real_table->start = parent->owner->next;
2460 real_table->end = end;
2461 #endif
2462
2463 /* pick up correct ptr */
2464 if(table->parent == NULL)
2465 table = &(parent->childs[0]);
2466
2467 /* deal with empty tables */
2468 #if 0
2469 table->start = table->start->next ? table->start->next : end;
2470 #endif
2471 table->start = table->start->next ? table->start : end;
2472 table->end = end;
2473
2474 /*****
2475 * Sanity Check: check all cells in search of a rowspan attribute. If
2476 * we detect a rowspan in the *last* cell of a row, we must add a bogus
2477 * cell to this row. If we don't do this, any cells falling in this row
2478 * will be skipped, causing text to disappear (at the least, in the worst
2479 * case it will cause a crash).
2480 *****/
2481
2482 /* See how many columns we have (if not already set by the COLS attr.) */
2483 for(i = 0; i < table->nrows; i++)
2484 {
2485 if(ncols < table->rows[i].ncells)
2486 ncols = table->rows[i].ncells;
2487 }
2488 if(ncols > table->ncols)
2489 table->ncols = ncols;
2490
2491 /* move to current table */
2492 return(table->parent);
2493 }
2494
2495 /*****
2496 * Name: tableOpenCaption
2497 * Return Type: void
2498 * Description: adds a caption to the given table.
2499 * In:
2500 * html: XmHTMLWidget id;
2501 * parent: parent table;
2502 * start: start of objects contained in this caption.
2503 * obj: XmHTMLObject starting this caption.
2504 * Returns:
2505 * nothing, but upon return the current table will have a caption.
2506 *****/
2507 static void
tableOpenCaption(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement start,XmHTMLObject * obj,Pixel * bg)2508 tableOpenCaption(XmHTMLWidget html, XmHTMLTable *parent,
2509 XmHTMLObjectTableElement start, XmHTMLObject *obj,
2510 Pixel *bg)
2511 {
2512 XmHTMLTable *table = parent;
2513 TableRow *caption;
2514 TableCell *cell;
2515
2516 /* sanity */
2517 if(parent == NULL)
2518 return;
2519
2520 if(table->parent == NULL)
2521 table = &(parent->childs[0]);
2522
2523 /* get caption */
2524 caption = table->caption;
2525
2526 /* only one caption allowed */
2527 if(caption->lastcell)
2528 return;
2529
2530 /*****
2531 * Get properties for this caption.
2532 * The global table properties are *never* propagated since, officially,
2533 * a Caption doesn't have any attributes...
2534 *****/
2535 caption->props = tableCheckProperties(html, obj->attributes,
2536 NULL, HTML_ATTR(default_halign), *bg);
2537
2538 /* starting object */
2539 caption->start = start;
2540 caption->owner = start;
2541
2542 /* set parent table */
2543 caption->parent = table;
2544
2545 /* one cell: the caption itself */
2546 caption->cells = (TableCell*)calloc(1, sizeof(TableCell));
2547 caption->ncells = 1;
2548 caption->lastcell = 1;
2549
2550 /* fill in used fields */
2551 cell = &(caption->cells[0]);
2552
2553 cell->header = False; /* unused */
2554 cell->width = 0; /* unused */
2555 cell->height = 0; /* unused */
2556 cell->rowspan = 1; /* unused */
2557 cell->colspan = 0; /* spans the full table width */
2558
2559 /* get properties for this cell */
2560 cell->props = tableCheckProperties(html, obj->attributes,
2561 NULL, caption->props->halign,
2562 caption->props->bg);
2563 /* set return background */
2564 *bg = caption->props->bg;
2565
2566 /* starting object */
2567 cell->start = start;
2568 cell->owner = start;
2569
2570 /* ending object unknown */
2571 cell->end = NULL;
2572
2573 /* set parent caption */
2574 cell->parent = caption;
2575
2576 /* all done */
2577 }
2578
2579 /*****
2580 * Name: tableCloseCaption
2581 * Return Type: int
2582 * Description: performs required caption wrapup actions
2583 * In:
2584 * html: XmHTMLWidget id;
2585 * parent: parent table;
2586 * end: last object to be used by this caption;
2587 * Returns:
2588 * nothing.
2589 *****/
2590 static void
tableCloseCaption(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement end)2591 tableCloseCaption(XmHTMLWidget html, XmHTMLTable *parent,
2592 XmHTMLObjectTableElement end)
2593 {
2594 XmHTMLTable *table = parent;
2595 TableRow *caption;
2596 TableCell *cell;
2597
2598 /* sanity */
2599 if(parent == NULL)
2600 return;
2601
2602 if(table->parent == NULL)
2603 table = &(parent->childs[0]);
2604
2605 caption = table->caption;
2606
2607 /* sanity */
2608 if(caption->ncells == 0)
2609 return;
2610
2611 cell = &(caption->cells[0]);
2612
2613 /* deal with empty cells */
2614 cell->start = cell->start->next ? cell->start->next : end;
2615 cell->end = end;
2616 }
2617
2618 /*****
2619 * Name: tableOpenRow
2620 * Return Type: void
2621 * Description: adds a row to the current table.
2622 * In:
2623 * html: XmHTMLWidget id;
2624 * parent: parent table;
2625 * start: start of objects contained in this row.
2626 * obj: XmHTMLObject starting this row, used to compute no of cells
2627 * in a row.
2628 * Returns:
2629 * Nothing, but a new row has been prepared in the current table.
2630 *****/
2631 static void
tableOpenRow(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement start,XmHTMLObject * obj,Alignment * halign,Pixel * bg)2632 tableOpenRow(XmHTMLWidget html, XmHTMLTable *parent,
2633 XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign,
2634 Pixel *bg)
2635 {
2636 XmHTMLTable *table = parent;
2637 TableRow *row;
2638 XmHTMLObject *tmp;
2639 int ncells = 0;
2640 int depth = 0;
2641
2642 /* sanity */
2643 if(parent == NULL)
2644 return;
2645
2646 if(table->parent == NULL)
2647 table = &(parent->childs[0]);
2648
2649 /* sanity */
2650 if(table->lastrow == table->nrows)
2651 {
2652 _XmHTMLError(__WFUNC__(html, "tableRowOpen"),
2653 "Bad tablerow count!!!");
2654 }
2655
2656 /* get next available row in this table */
2657 row = &(table->rows[table->lastrow]);
2658
2659 /* get properties for this row */
2660 row->props = tableCheckProperties(html, obj->attributes,
2661 table->props, HTML_ATTR(default_halign), *bg);
2662 /* set return alignment */
2663 *halign = row->props->halign;
2664
2665 /* set return background */
2666 *bg = row->props->bg;
2667
2668 /* starting object */
2669 row->start = start;
2670 row->owner = start;
2671
2672 /* set parent table */
2673 row->parent = table;
2674
2675 /* count how many cells this row has */
2676 for(tmp = obj->next; tmp != NULL; tmp = tmp->next)
2677 {
2678 /* check for end of row and child rows (in child tables) */
2679 if(tmp->id == HT_TR)
2680 {
2681 if(tmp->is_end)
2682 {
2683 if(depth == 0)
2684 break;
2685 else
2686 depth--;
2687 }
2688 else /* new row opens */
2689 depth++;
2690 }
2691 /* only count a cell when it belongs to the top-level row */
2692 if((tmp->id == HT_TH || tmp->id == HT_TD) && depth == 0 && !tmp->is_end)
2693 ncells++;
2694 }
2695 /* empty rows don't have cells */
2696 if(ncells)
2697 row->cells = (TableCell*)calloc(ncells, sizeof(TableCell));
2698 else /* allocate an empty cell */
2699 row->cells = (TableCell*)calloc(1, sizeof(TableCell));
2700 row->ncells = ncells;
2701 row->lastcell = 0;
2702
2703 /* move to next available row */
2704 table->lastrow++;
2705 }
2706
2707 /*****
2708 * Name: tableCloseRow
2709 * Return Type: int
2710 * Description: performs required row wrapup actions
2711 * In:
2712 * html: XmHTMLWidget id;
2713 * parent: parent table;
2714 * end: last object to be used by this row;
2715 * Returns:
2716 * nothing.
2717 *****/
2718 static void
tableCloseRow(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement end)2719 tableCloseRow(XmHTMLWidget html, XmHTMLTable *parent,
2720 XmHTMLObjectTableElement end)
2721 {
2722 XmHTMLTable *table = parent;
2723 TableRow *row;
2724 int i, ncols = 0;
2725
2726 /* sanity */
2727 if(parent == NULL)
2728 return;
2729
2730 if(table->parent == NULL)
2731 table = &(parent->childs[0]);
2732
2733 /* sanity */
2734 if(table->lastrow == 0)
2735 {
2736 _XmHTMLWarning(__WFUNC__(html, "tableCloseRow"),
2737 "Internal Error: zero row count in table");
2738 row = &(table->rows[table->lastrow]);
2739 }
2740 else /* get current row in this table */
2741 row = &(table->rows[table->lastrow-1]);
2742
2743 /*****
2744 * Count how many columns this row has (including cells spanning multiple
2745 * columns).
2746 *****/
2747 for(i = 0; i < row->ncells; i++)
2748 ncols += row->cells[i].colspan;
2749
2750 if(ncols > table->ncols)
2751 table->ncols = ncols;
2752
2753 /*****
2754 * Empty rows (<tr> followed by another <tr> and only whitespace between
2755 * them) will loose their starting point as start->next will still be
2756 * empty: end has not yet been inserted in the ObjectTable.
2757 *****/
2758 row->start = (row->start->next ? row->start->next : end);
2759 row->end = end;
2760
2761 #ifdef DEBUG
2762 if(row->start == NULL)
2763 {
2764 _XmHTMLWarning(__WFUNC__(html, "tableCloseRow"), "Row %i lost"
2765 "it's starting point. (near line %i in input)\n",
2766 table->lastrow - 1, row->owner->object->line);
2767 }
2768 #endif
2769 }
2770
2771 /*****
2772 * Name: tableOpenCell
2773 * Return Type: void
2774 * Description: adds a cell to the current row in the current table.
2775 * In:
2776 * html: XmHTMLWidget id;
2777 * parent: parent table;
2778 * start: start of objects contained in this cell;
2779 * obj: XmHTMLObject starting this cell;
2780 * Returns:
2781 * Nothing, but a new cell has been prepared in the current row.
2782 *****/
2783 static void
tableOpenCell(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement start,XmHTMLObject * obj,Alignment * halign,Pixel * bg)2784 tableOpenCell(XmHTMLWidget html, XmHTMLTable *parent,
2785 XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign,
2786 Pixel *bg)
2787 {
2788 XmHTMLTable *table = parent;
2789 TableRow *row;
2790 TableCell *cell;
2791
2792 /* sanity */
2793 if(parent == NULL)
2794 return;
2795
2796 if(table->parent == NULL)
2797 table = &(parent->childs[0]);
2798
2799 /* more sanity */
2800 if(table->lastrow == 0)
2801 {
2802 _XmHTMLWarning(__WFUNC__(html, "tableOpenCell"),
2803 XMHTML_MSG_42, obj->line);
2804 return;
2805 }
2806
2807 /* get current row in this table */
2808 row = &(table->rows[table->lastrow-1]);
2809
2810 /* sanity */
2811 if(row->lastcell == row->ncells)
2812 {
2813 _XmHTMLError(__WFUNC__(html, "tableCellOpen"),
2814 "Internal Error: Bad tablerow cell count!!!");
2815 }
2816
2817 /* get next available cell in this row */
2818 cell = &(row->cells[row->lastcell]);
2819
2820 /* get cell-specific properties */
2821 cell->header = (obj->id == HT_TH ? True : False);
2822 if(obj->attributes)
2823 {
2824 cell->width = _XmHTMLTagCheckNumber(obj->attributes, "width", 0);
2825 cell->height = _XmHTMLTagCheckNumber(obj->attributes, "height", 0);
2826 cell->rowspan = _XmHTMLTagGetNumber(obj->attributes, "rowspan", 1);
2827 cell->colspan = _XmHTMLTagGetNumber(obj->attributes, "colspan", 1);
2828 }
2829 else
2830 {
2831 cell->width = 0;
2832 cell->height = 0;
2833 cell->rowspan = 1;
2834 cell->colspan = 1;
2835 }
2836
2837 /* [row/cell]span = 0 : span entire table in requested direction */
2838 if(cell->rowspan <= 0 || cell->rowspan > table->nrows)
2839 cell->rowspan = table->nrows;
2840
2841 /*****
2842 * colspan <= 0 gets handled in SetTable when we now how many columns
2843 * this table has
2844 *****/
2845
2846 /* get global properties for this cell */
2847 cell->props = tableCheckProperties(html, obj->attributes,
2848 row->props, row->props->halign, *bg);
2849 /* set return alignment */
2850 *halign = cell->props->halign;
2851
2852 /* set return background */
2853 *bg = cell->props->bg;
2854
2855 /* starting object */
2856 cell->start = start;
2857 cell->owner = start;
2858
2859 /* set parent row */
2860 cell->parent = row;
2861
2862 /* move to next available cell */
2863 row->lastcell++;
2864 }
2865
2866 /*****
2867 * Name: tableCloseCell
2868 * Return Type: int
2869 * Description: performs required cell wrapup actions
2870 * In:
2871 * html: XmHTMLWidget id;
2872 * parent: parent table;
2873 * end: last object to be used by this cell;
2874 * Returns:
2875 * nothing.
2876 *****/
2877 static void
tableCloseCell(XmHTMLWidget html,XmHTMLTable * parent,XmHTMLObjectTableElement end)2878 tableCloseCell(XmHTMLWidget html, XmHTMLTable *parent,
2879 XmHTMLObjectTableElement end)
2880 {
2881 XmHTMLTable *table = parent;
2882 TableRow *row;
2883 TableCell *cell;
2884
2885 /* sanity */
2886 if(parent == NULL)
2887 return;
2888
2889 if(table->parent == NULL)
2890 table = &(parent->childs[0]);
2891
2892 /* sanity */
2893 if(table->lastrow == 0)
2894 {
2895 _XmHTMLWarning(__WFUNC__(html, "tableCloseCell"),
2896 "Internal Error: zero row count in table");
2897 row = &(table->rows[table->lastrow]);
2898 }
2899 else /* get current row in this table */
2900 row = &(table->rows[table->lastrow-1]);
2901
2902 /* sanity */
2903 if(row->lastcell == 0)
2904 {
2905 _XmHTMLWarning(__WFUNC__(html, "tableCloseCell"),
2906 "Internal Error: zero row cell count (row %i in table)",
2907 table->lastrow-1);
2908 if((cell = &(row->cells[row->lastcell])) == NULL)
2909 return;
2910 }
2911 else /* get current cell in this row */
2912 cell = &(row->cells[row->lastcell-1]);
2913
2914 /*****
2915 * Empty cells (<td> followed by another <td> and only whitespace between
2916 * them) will loose their starting point as start->next will still be
2917 * empty: end has not yet been inserted in the ObjectTable.
2918 *****/
2919 cell->start = (cell->start->next ? cell->start->next : end);
2920 cell->end = end;
2921
2922 #ifdef DEBUG
2923 if(cell->start == NULL)
2924 {
2925 _XmHTMLWarning(__WFUNC__(html, "tableCloseCell"), "Cell %i (row %i) "
2926 "lost it's start point. (near line %i in input)\n",
2927 row->lastcell - 1, table->lastrow - 1, row->owner->object->line);
2928 }
2929 #endif
2930 }
2931
2932 #define PUSH_COLOR(WIDGET) { \
2933 char *chPtr; \
2934 /* check for color spec */ \
2935 StackPush(fg_color_stack, fg); \
2936 if(temp->attributes && \
2937 (chPtr = _XmHTMLTagGetValue(temp->attributes, "color")) != NULL) \
2938 { \
2939 Boolean doit = True; \
2940 if(HTML_ATTR(strict_checking)) \
2941 doit = _XmHTMLConfirmColor32(chPtr); \
2942 if(doit) fg = _XmHTMLGetPixelByName(html, chPtr, fg); \
2943 free(chPtr); \
2944 } \
2945 }
2946
2947 #define CHECK_LINE { \
2948 if(element_data & ELE_ANCHOR) { \
2949 if(element_data & ELE_ANCHOR_TARGET) \
2950 line_data = HTML_ATTR(anchor_target_line); \
2951 else if(element_data & ELE_ANCHOR_VISITED) \
2952 line_data = HTML_ATTR(anchor_visited_line); \
2953 else \
2954 line_data = HTML_ATTR(anchor_line); \
2955 } \
2956 /* ignore <u> for anchors */ \
2957 else { \
2958 if(element_data & ELE_UNDERLINE) \
2959 line_data = LINE_SOLID | LINE_UNDER; \
2960 } \
2961 /* check strikeout flag */ \
2962 if(element_data & ELE_STRIKEOUT) \
2963 line_data |= LINE_STRIKE; \
2964 }
2965
2966 /********
2967 ****** Private XmHTML Functions
2968 ********/
2969
2970 /*****
2971 * Name: _XmHTMLNewAnchor
2972 * Return Type: XmHTMLAnchor *
2973 * Description: allocates and fills an anchor object
2974 * In:
2975 * html: owning widget
2976 * object: raw anchor data
2977 * Returns:
2978 * the allocated object
2979 * Note:
2980 * this routine assumes that the <a> tag has attributes.
2981 *****/
2982 XmHTMLAnchor*
_XmHTMLNewAnchor(XmHTMLWidget html,XmHTMLObject * object)2983 _XmHTMLNewAnchor(XmHTMLWidget html, XmHTMLObject *object)
2984 {
2985 static XmHTMLAnchor *anchor;
2986
2987 anchor = (XmHTMLAnchor*)malloc(sizeof(XmHTMLAnchor));
2988
2989 /* set all fields to zero */
2990 (void)memset(anchor, 0, sizeof(XmHTMLAnchor));
2991
2992 /* anchors can be both named and href'd at the same time */
2993 anchor->name = _XmHTMLTagGetValue(object->attributes, "name");
2994
2995 /* get the url specs */
2996 parseHref(object->attributes, anchor);
2997
2998 /* get the url type */
2999 anchor->url_type = XmHTMLGetURLType(anchor->href);
3000
3001 /* promote to named if necessary */
3002 if(anchor->url_type == ANCHOR_UNKNOWN && anchor->name)
3003 anchor->url_type = ANCHOR_NAMED;
3004
3005 /* see if we need to watch any events for this anchor */
3006 if(object->attributes &&
3007 (HTML_ATTR(event_proc) || HTML_ATTR(event_callback)))
3008 {
3009 /* pick up possible HTML4.0 events */
3010 anchor->events = _XmHTMLCheckCoreEvents(html, object->attributes,
3011 &anchor->event_mask);
3012 }
3013
3014 #ifdef PEDANTIC
3015 if(anchor->url_type == ANCHOR_UNKNOWN)
3016 {
3017 _XmHTMLWarning(__WFUNC__(html, "_XmHTMLNewAnchor"),
3018 XMHTML_MSG_43, object->attributes, object->line);
3019 }
3020 #endif /* PEDANTIC */
3021
3022 /*
3023 * If we have a proc available for anchor testing, call it and
3024 * set the visited field.
3025 */
3026 if(HTML_ATTR(anchor_visited_proc))
3027 anchor->visited = HTML_ATTR(anchor_visited_proc)((Widget)html,
3028 anchor->href, HTML_ATTR(client_data));
3029
3030 /* insert in the anchor list */
3031 if(list_data.anchor_head)
3032 {
3033 /*****
3034 * We can't do anything about duplicate anchors. Removing them
3035 * would mess up the named anchor lookup table.
3036 *****/
3037 list_data.anchor_current->next = anchor;
3038 list_data.anchor_current = anchor;
3039 }
3040 else
3041 {
3042 list_data.anchor_head = list_data.anchor_current = anchor;
3043 }
3044 return(anchor);
3045 }
3046
3047 /*****
3048 * Name: _XmHTMLformatObjects
3049 * Return Type: XmHTMLObjectTable*
3050 * Description: creates a list of formatted HTML objects.
3051 * In:
3052 * old: previous XmHTMLWidget id (contains all items to be freed);
3053 * html: current XmHTMLWidget id, containing raw (parser) html object
3054 * data and will receive new formatted data.
3055 * Returns:
3056 * nothing, but list of formatted objects, anchors, tables (and images)
3057 * is updated upon return.
3058 *****/
3059 void
_XmHTMLformatObjects(XmHTMLWidget old,XmHTMLWidget html)3060 _XmHTMLformatObjects(XmHTMLWidget old, XmHTMLWidget html)
3061 {
3062 /* text level variables */
3063 String text;
3064 int linefeed, n_words, anchor_words, named_anchors;
3065 int x_offset = 0, y_offset = 0;
3066 Byte text_data, line_data; /* text and line data bits */
3067 unsigned long element_data = 0;
3068 XmHTMLWord *words;
3069 XmHTMLAnchor *anchor_data, *form_anchor_data;
3070 Boolean i18n = False; /* multibyte input text flag */
3071
3072 #ifdef DEBUG
3073 int num_ignore = 0;
3074 static String func = "_XmHTMLformatObjects";
3075 #endif
3076
3077 /* list variables */
3078 int ul_level, ol_level, ident_level, current_list;
3079 listStack list_stack[XmHTML_NESTED_LISTS_MAX];
3080 int in_dt = 0;
3081
3082 /* remaining object variables */
3083 Pixel fg, bg;
3084 Dimension width, height;
3085 Alignment halign, valign;
3086 ObjectType object_type;
3087 XmHTMLfont *font;
3088 #ifdef I18N
3089 XmHTMLfont *i18nfont;
3090 #endif /* I18N */
3091
3092 static XmHTMLObjectTableElement element;
3093 int basefont;
3094
3095 /* imagemap and area stuff */
3096 XmHTMLImageMap *imageMap = NULL;
3097
3098 /* local flags */
3099 Boolean ignore = False, in_pre = False;
3100
3101 /* HTML tables */
3102 XmHTMLTable *table = NULL;
3103 XmHTMLTable *current_table = NULL;
3104
3105 /* misc. variables */
3106 XmHTMLObject *temp;
3107 int i, new_anchors = 0;
3108 Boolean anchor_data_used = False;
3109 ToolkitAbstraction *tka = HTML_ATTR(tka);
3110 int fnheight; /* current font height, for linefeeding */
3111
3112 /* stacks */
3113 Stack align_stack, fg_color_stack, bg_color_stack, font_stack;
3114
3115 #ifdef DEBUG
3116 allocated = 0;
3117 #endif
3118
3119 /* Free any previous lists and initialize it */
3120 InitObjectTable(ATTR_HTML(old, formatted), ATTR_HTML(old, anchor_data));
3121 HTML_ATTR(formatted) = (XmHTMLObjectTable*)NULL;
3122 HTML_ATTR(anchor_data) = (XmHTMLAnchor*)NULL;
3123
3124 /* free table data */
3125 freeTables(ATTR_HTML(old, tables));
3126 HTML_ATTR(tables) = (XmHTMLTable*)NULL;
3127
3128 /*
3129 * Nothing to do, just return. Should only happen if we get called
3130 * from Destroy().
3131 */
3132 if(HTML_ATTR(elements) == NULL)
3133 {
3134 /* free top of list */
3135 if(list_data.head)
3136 free(list_data.head);
3137 list_data.head = NULL;
3138 return;
3139 }
3140
3141 /* Move to the body element */
3142 for(temp = HTML_ATTR(elements); temp != NULL && temp->id != HT_BODY;
3143 temp = temp->next);
3144
3145 /*
3146 * No <body> element found. This is an error since the parser will
3147 * *always* add a <body> element if none is present in the source
3148 * document.
3149 */
3150 if(temp == NULL)
3151 {
3152 /*****
3153 * The only exception is a document only containing plain text and no
3154 * BODY tag was present in the input. In this case, check the input and
3155 * start outputting at the end of the first text element not belonging
3156 * to the head of a document.
3157 * Fix 01/04/98-01, kdh
3158 *****/
3159 XmHTMLObject *last_obj = NULL;
3160
3161 for(temp = HTML_ATTR(elements); temp != NULL; temp = temp->next)
3162 {
3163 switch(temp->id)
3164 {
3165 case HT_DOCTYPE:
3166 case HT_BASE:
3167 /* these two have no ending tag */
3168 last_obj = temp;
3169 break;
3170 case HT_HTML:
3171 /* don't use the closing tag for this, it's the end... */
3172 if(!temp->is_end)
3173 last_obj = temp;
3174 break;
3175 case HT_HEAD:
3176 case HT_TITLE:
3177 case HT_SCRIPT:
3178 case HT_STYLE:
3179 case HT_PAGE:
3180 /* pick up the last closing tag */
3181 if(temp->is_end)
3182 last_obj = temp;
3183 default:
3184 break;
3185 }
3186 }
3187
3188 if(last_obj == NULL || last_obj->next == NULL)
3189 {
3190 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_44);
3191 return;
3192 }
3193 /* we move to the correct object a bit further down */
3194 temp = last_obj;
3195 }
3196
3197 /* initialize font stack */
3198 font = _XmHTMLSelectFontCache(html, False);
3199 basefont = 4;
3200 font_stack = StackCreateDouble((void*)font, (void*)basefont, NULL, NULL);
3201
3202 /* Reset anchor count */
3203 anchor_words = 0;
3204 named_anchors = 0;
3205 anchor_data = form_anchor_data = NULL;
3206
3207 /* initialize list variables */
3208 ul_level = 0;
3209 ol_level = 0;
3210 ident_level = 0;
3211 current_list = 0;
3212
3213 /* reset stacks */
3214 for(i = 0; i < XmHTML_NESTED_LISTS_MAX; i++)
3215 {
3216 list_stack[i].isindex = False;
3217 list_stack[i].marker = XmMARKER_NONE;
3218 list_stack[i].level = 0;
3219 list_stack[i].type = HT_ZTEXT;
3220 }
3221
3222 /* Initialize linefeeding mechanism */
3223 text_data = TEXT_SPACE_NONE;
3224 linefeed = CheckLineFeed(CLEAR_SOFT, True, &text_data);
3225
3226 /* Initialize alignment */
3227 halign = HTML_ATTR(default_halign);
3228 align_stack = StackCreate((void*)halign, NULL);
3229 valign = XmVALIGN_NONE;
3230 object_type = OBJ_NONE;
3231
3232 /* check for background stuff */
3233 ParseBodyTags(html, temp);
3234
3235 /* foreground color to use */
3236 fg = HTML_ATTR(body_fg);
3237 /* background color to use */
3238 bg = HTML_ATTR(body_bg);
3239
3240 /* Initialize color stacks */
3241 fg_color_stack = StackCreate((void*)fg, NULL);
3242 bg_color_stack = StackCreate((void*)bg, NULL);
3243
3244 /* move to the next element */
3245 temp = temp->next;
3246
3247 /*
3248 * Insert a dummy element at the head of the list to prevent incorrect
3249 * handling of the first real element to be rendered.
3250 * fix 12/14/97-01, kdh
3251 */
3252 NewTableElement(element,temp);
3253 element->object_type = OBJ_NONE;
3254 element->font = HTML_ATTR(default_font);
3255 InsertTableElement(element, False);
3256 text_data = TEXT_SPACE_NONE;
3257
3258 /*
3259 * Only elements between <BODY></BODY> elements are really interesting.
3260 * BUT: if the HTML verification/reparation routines in parse.c should
3261 * fail, we might have a premature </body> element, so we don't check on
3262 * it but walk thru every item found.
3263 */
3264 while(temp != HTML_ATTR(last_element))
3265 {
3266 /* create new element */
3267 if(!ignore)
3268 NewTableElement(element,temp);
3269 else
3270 {
3271 /*****
3272 * reuse current element if it wasn't needed on the previous
3273 * pass.
3274 * fix 11/12/97-01, kdh
3275 *****/
3276 (void)memset(element, 0, sizeof(XmHTMLObjectTable));
3277 /* fill in appropriate fields */
3278 element->object = temp;
3279 }
3280
3281 element->prev = list_data.current;
3282
3283 /* Initialize all fields changed in here */
3284 text = NULL;
3285 ignore = False;
3286 object_type = OBJ_NONE;
3287 n_words = 0;
3288 width = height = 0;
3289 words = NULL;
3290 linefeed = CLEAR_NONE;
3291 line_data = NO_LINE;
3292 fnheight = font->lineheight;
3293
3294 _XmHTMLDebug(2, ("format.c, _XmHTMLformatObjects, object data:\n"
3295 "\telement id: %s\n\tattributes: %s\n\tis_end: %s\n",
3296 html_tokens[temp->id],
3297 temp->attributes ? temp->attributes : "<none>",
3298 temp->is_end ? "Yes" : "No"));
3299
3300 switch(temp->id)
3301 {
3302 /* plain text */
3303 case HT_ZTEXT:
3304 object_type = OBJ_TEXT;
3305 /*****
3306 * CopyText
3307 * We do not want escape expansion if we are loading a plain
3308 * text document.
3309 *****/
3310 if((text = CopyText(html, temp->element, in_pre, &text_data,
3311 HTML_ATTR(mime_id) != XmNONE, &i18n)) == NULL)
3312 {
3313 /*****
3314 * named anchors can be empty, so keep them by inserting
3315 * a dummy word, but only do that once (prevents a margin
3316 * reset as well).
3317 *****/
3318 if((element_data & ELE_ANCHOR_INTERN) && !anchor_data_used)
3319 {
3320 words = MakeDummyWord(&height, font, line_data,
3321 element);
3322 n_words = 1;
3323 }
3324 else
3325 ignore = True; /* ignore empty text fields */
3326 break;
3327 }
3328 if(!in_pre)
3329 {
3330 CollapseWhiteSpace(text);
3331 /*****
3332 * If this turns out to be an empty block, ignore it,
3333 * but only if it's not an empty named anchor.
3334 *****/
3335 if(strlen(text) == 0)
3336 {
3337 if((element_data & ELE_ANCHOR_INTERN) &&
3338 !anchor_data_used)
3339 {
3340 object_type = OBJ_NONE;
3341 words = MakeDummyWord(&height, font, line_data,
3342 element);
3343 n_words = 1;
3344 }
3345 else
3346 ignore = True;
3347 free(text);
3348 break;
3349 }
3350 /* check line data */
3351 CHECK_LINE;
3352
3353 /* convert text to a number of words */
3354 words = TextToWords(text, &n_words, &height, font,
3355 line_data, text_data, element, tka);
3356
3357 /* Plain text does a hard reset */
3358 linefeed = CheckLineFeed(CLEAR_NONE, True, &text_data);
3359 }
3360 else
3361 {
3362 object_type = OBJ_PRE_TEXT;
3363 /* check line data */
3364 CHECK_LINE;
3365 /* convert text to a number of words, keep formatting. */
3366 words = TextToPre(text, &n_words, font, line_data, element,
3367 tka, HTML_ATTR(tabwidth));
3368
3369 /* Preformatted text does a soft reset */
3370 linefeed = CheckLineFeed(CLEAR_NONE, False, &text_data);
3371 }
3372 break;
3373
3374 /* images */
3375 case HT_IMG:
3376 /* release break bit */
3377 text_data &= ~TEXT_BREAK;
3378 text_data |= TEXT_IMAGE;
3379 if((words = ImageToWord(html, temp->attributes, &n_words,
3380 &height, element, in_pre, tka,
3381 (Boolean)(anchor_data != NULL), text_data)) == NULL)
3382 {
3383 /* loose image bit */
3384 text_data &= ~TEXT_IMAGE;
3385 ignore = True;
3386 break;
3387 }
3388 object_type = in_pre ? OBJ_PRE_TEXT : OBJ_TEXT;
3389
3390 /* No explicit returns for images, reset */
3391 linefeed = CheckLineFeed(CLEAR_NONE, False, &text_data);
3392
3393 break;
3394
3395 /* anchors */
3396 case HT_A:
3397 if(temp->is_end)
3398 {
3399 /*
3400 * this is a very sneaky hack: since empty named anchors
3401 * are allowed, we must store it somehow. And this is how
3402 * we do it: insert a dummy word (to prevent margin reset)
3403 * and back up one element.
3404 */
3405 if(!anchor_data_used && (element_data & ELE_ANCHOR_INTERN))
3406 {
3407 _XmHTMLDebug(2, ("format.c: _XmHTMLformatObjects, "
3408 "adding bogus named anchor %s\n",
3409 anchor_data->name));
3410
3411 /* insert a dummy word to prevent margin reset */
3412 words = MakeDummyWord(&height, font, line_data,
3413 element);
3414 n_words = 1;
3415 object_type = OBJ_TEXT;
3416 anchor_data_used = True;
3417 temp = temp->prev;
3418 break;
3419 }
3420 /* unset anchor bitfields */
3421 element_data &= ( ~ELE_ANCHOR & ~ELE_ANCHOR_TARGET &
3422 ~ELE_ANCHOR_VISITED & ~ELE_ANCHOR_INTERN);
3423 fg = (Pixel)StackPop(fg_color_stack);
3424
3425 anchor_data = NULL;
3426
3427 ignore = True; /* only need anchor data */
3428 _XmHTMLDebug(2,("format.c: _XmHTMLformatObjects: anchor "
3429 "end\n"));
3430 }
3431 else
3432 {
3433 /* allocate a new anchor */
3434 anchor_data = (temp->attributes ?
3435 _XmHTMLNewAnchor(html, temp) : NULL);
3436
3437 /* sanity check */
3438 if(!anchor_data)
3439 {
3440 ignore = True;
3441 break;
3442 }
3443 /* save current color */
3444 StackPush(fg_color_stack, fg);
3445
3446 new_anchors++;
3447 anchor_data_used = False;
3448
3449 /* set proper element bits */
3450
3451 /* maybe it's a named one */
3452 if(anchor_data->name)
3453 element_data |= ELE_ANCHOR_INTERN;
3454
3455 /*
3456 * maybe it's also a plain anchor. If so, see what
3457 * foreground color we have to use to render this
3458 * anchor.
3459 */
3460 if(anchor_data->href[0] != '\0')
3461 {
3462 element_data |= ELE_ANCHOR;
3463 fg = HTML_ATTR(anchor_fg);
3464
3465 /* maybe it's been visited */
3466 if(anchor_data->visited)
3467 {
3468 element_data |= ELE_ANCHOR_VISITED;
3469 fg = HTML_ATTR(anchor_visited_fg);
3470 }
3471 /* maybe it's a target */
3472 else if(anchor_data->target)
3473 {
3474 element_data |= ELE_ANCHOR_TARGET;
3475 fg = HTML_ATTR(anchor_target_fg);
3476 }
3477 }
3478 _XmHTMLDebug(2,("format.c: _XmHTMLformatObjects: anchor "
3479 "start\n"));
3480 ignore = True; /* only need anchor data */
3481 }
3482 break;
3483
3484 /* font changes */
3485 case HT_CITE: /* italic */
3486 case HT_I: /* italic */
3487 case HT_EM: /* italic */
3488 case HT_DFN: /* italic */
3489 case HT_STRONG: /* bold */
3490 case HT_B: /* bold */
3491 case HT_SAMP: /* fixed width */
3492 case HT_TT: /* fixed width */
3493 case HT_VAR: /* fixed width */
3494 case HT_CODE: /* fixed width */
3495 case HT_KBD: /* fixed width */
3496 if(temp->is_end)
3497 {
3498 if(HTML_ATTR(allow_color_switching))
3499 fg = (Pixel)StackPop(fg_color_stack);
3500 font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
3501 }
3502 else
3503 {
3504 if(HTML_ATTR(allow_color_switching))
3505 PUSH_COLOR(html);
3506 StackPushDouble(font_stack, font, basefont);
3507 font = _XmHTMLLoadFont(html, temp->id, basefont, font);
3508
3509 /* new height for linefeeds */
3510 fnheight = font->lineheight;
3511 }
3512 ignore = True; /* only need font data */
3513 break;
3514
3515 case HT_U:
3516 if(temp->is_end) /* unset underline bitfields */
3517 element_data &= (~ELE_UNDERLINE & ~ELE_UNDERLINE_TEXT);
3518 else
3519 element_data |= ELE_UNDERLINE;
3520 ignore = True; /* only need underline data */
3521 break;
3522
3523 case HT_STRIKE:
3524 if(temp->is_end) /* unset strikeout bitfields */
3525 element_data &= (~ELE_STRIKEOUT & ~ELE_STRIKEOUT_TEXT);
3526 else
3527 element_data |= ELE_STRIKEOUT;
3528 ignore = True; /* only need strikeout data */
3529 break;
3530
3531 case HT_BASEFONT:
3532 {
3533 basefont = temp->attributes ?
3534 _XmHTMLTagGetNumber(temp->attributes, "size", 0) : 0;
3535 /* take absolute value */
3536 basefont = Abs(basefont);
3537 if(basefont < 1 || basefont > 7)
3538 {
3539 if(HTML_ATTR(bad_html_warnings))
3540 _XmHTMLWarning(__WFUNC__(html, func),
3541 XMHTML_MSG_45, basefont, temp->line);
3542 basefont = 4;
3543 }
3544 }
3545 ignore = True; /* only need font data */
3546 break;
3547
3548 /*****
3549 * <font> is a big performance hit. We always need to push & pop
3550 * the font *even* if only the font color has been changed as we
3551 * can't keep track of what has actually been changed.
3552 *****/
3553 case HT_FONT:
3554 if(temp->is_end)
3555 {
3556 if(HTML_ATTR(allow_font_switching))
3557 font =(XmHTMLfont*)StackPopDouble(font_stack, basefont);
3558 if(HTML_ATTR(allow_color_switching))
3559 fg = (Pixel)StackPop(fg_color_stack);
3560 }
3561 else
3562 {
3563 char *chPtr;
3564 int size = xmhtml_basefont_sizes[basefont - 1];
3565
3566 if(HTML_ATTR(allow_color_switching))
3567 PUSH_COLOR(html);
3568
3569 if(HTML_ATTR(allow_font_switching))
3570 StackPushDouble(font_stack, font, basefont);
3571 else
3572 break;
3573
3574 /* can't use TagGetNumber: fontchange can be relative */
3575 chPtr = temp->attributes ?
3576 _XmHTMLTagGetValue(temp->attributes, "size") : NULL;
3577 if(chPtr != NULL)
3578 {
3579 int f_inc = atoi(chPtr);
3580
3581 /* check wether size is relative or not */
3582 if(chPtr[0] == '-' || chPtr[0] == '+')
3583 {
3584 f_inc = basefont + f_inc; /* + 1; */
3585 }
3586 /* sanity check */
3587 if(f_inc < 1 || f_inc > 7)
3588 {
3589 if(f_inc < 1)
3590 f_inc = 1;
3591 else
3592 f_inc = 7;
3593 }
3594
3595 basefont = f_inc;
3596 /* minus one: zero based array */
3597 size = xmhtml_basefont_sizes[f_inc-1];
3598 free(chPtr); /* fix 01/28/98-02, kdh */
3599 chPtr = NULL;
3600 }
3601 /*****
3602 * Font face changes only allowed when not in preformatted
3603 * text.
3604 * Only check when not being pedantic.
3605 *****/
3606 #ifndef PEDANTIC
3607 if(!in_pre)
3608 #endif
3609 chPtr = temp->attributes ?
3610 _XmHTMLTagGetValue(temp->attributes, "face") : NULL;
3611
3612 if(chPtr != NULL)
3613 {
3614 #ifdef PEDANTIC
3615 if(in_pre)
3616 {
3617 _XmHTMLWarning(__WFUNC__(html, func),
3618 XMHTML_MSG_41, "FONT FACE=xxx",
3619 html_tokens[HT_PRE], temp->line);
3620 /*****
3621 * Ignore face but must allow for size change.
3622 * (Font stack will get unbalanced otherwise!)
3623 *****/
3624 font = _XmHTMLLoadFont(html, HT_FONT, size, font);
3625 }
3626 else
3627 #endif
3628 font = _XmHTMLLoadFontWithFace(html, size, chPtr,
3629 font);
3630
3631 free(chPtr);
3632 }
3633 else
3634 font = _XmHTMLLoadFont(html, HT_FONT, size, font);
3635 }
3636 ignore = True; /* only need font data */
3637 break;
3638
3639 case HT_BIG:
3640 if(temp->is_end)
3641 font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
3642 else /* multiple big elements are not honoured */
3643 {
3644 StackPushDouble(font_stack, font, basefont);
3645 font = _XmHTMLLoadFont(html, HT_FONT,
3646 xmhtml_basefont_sizes[4], font);
3647 }
3648 ignore = True; /* only need font data */
3649 break;
3650
3651 case HT_SMALL:
3652 if(temp->is_end)
3653 font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
3654 else /* multiple small elements are not honoured */
3655 {
3656 StackPushDouble(font_stack, font, basefont);
3657 font = _XmHTMLLoadFont(html, HT_FONT,
3658 xmhtml_basefont_sizes[2], font);
3659 }
3660 ignore = True; /* only need font data */
3661 break;
3662
3663 case HT_SUB:
3664 case HT_SUP:
3665 if(temp->is_end)
3666 {
3667 /* restore vertical offset */
3668 y_offset = 0;
3669 x_offset = 0;
3670 font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
3671 }
3672 else /* multiple small elements are not honoured */
3673 {
3674 StackPushDouble(font_stack, font, basefont);
3675 font = _XmHTMLLoadFont(html, HT_FONT,
3676 xmhtml_basefont_sizes[2], font);
3677 y_offset = (temp->id == HT_SUB ?
3678 font->sub_yoffset : font->sup_yoffset);
3679 x_offset = (temp->id == HT_SUB ?
3680 font->sub_xoffset : font->sup_xoffset);
3681 }
3682 ignore = True; /* only need font data */
3683 break;
3684
3685 case HT_H1:
3686 case HT_H2:
3687 case HT_H3:
3688 case HT_H4:
3689 case HT_H5:
3690 case HT_H6:
3691 if(temp->is_end)
3692 {
3693 if(HTML_ATTR(allow_color_switching))
3694 fg = (Pixel)StackPop(fg_color_stack);
3695 halign = (Alignment)StackPop(align_stack);
3696 font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
3697 }
3698 else
3699 {
3700 /*****
3701 * <H> elements are sometimes used as spacers. This gives
3702 * *horrible* rendering. Look forward to the first text
3703 * element and check if it contains text. If it does, allow
3704 * it, else ignore this element.
3705 *****/
3706 XmHTMLObject *elePtr = temp->next;
3707 for(; elePtr != NULL && elePtr->id != HT_ZTEXT &&
3708 elePtr->id != temp->id; elePtr = elePtr->next);
3709
3710 /* this <h> only contains text, check it */
3711 if(elePtr && elePtr->id == HT_ZTEXT &&
3712 elePtr->next->id == temp->id)
3713 {
3714 String chPtr = elePtr->element;
3715 /*
3716 * found a text element. Check if it has valid
3717 * content.
3718 */
3719 while(*chPtr != '\0' && isspace(*chPtr))
3720 chPtr++;
3721 /* empty head. Ignore it */
3722 if(*chPtr == '\0')
3723 {
3724 _XmHTMLDebug(2, ("format.c: _XmHTMLformatObjects; "
3725 "empty element %s found at line %i in input, "
3726 "ignoring.\n",
3727 html_tokens[temp->id], temp->line));
3728
3729 /* set to closing <h> and ignore it */
3730 temp = elePtr->next;
3731 object_type = OBJ_NONE;
3732 ignore = True;
3733 break;
3734 }
3735 }
3736 if(HTML_ATTR(allow_color_switching))
3737 PUSH_COLOR(html);
3738
3739 StackPush(align_stack, halign);
3740 halign = (temp->attributes ?
3741 _XmHTMLGetHorizontalAlignment(temp->attributes, halign)
3742 : halign);
3743
3744 StackPushDouble(font_stack, font, basefont);
3745 font = _XmHTMLLoadFont(html, temp->id, basefont, font);
3746
3747 /* new height for linefeeds */
3748 fnheight = font->lineheight;
3749
3750 /*****
3751 * Need to update basefont size as well so font face changes
3752 * changes *inside* these elements use the correct font
3753 * size as well.
3754 * The sizes used by the headers are in reverse order.
3755 *****/
3756 basefont = (int)(HT_H6 - temp->id) + 1;
3757 }
3758 linefeed = CheckLineFeed(CLEAR_HARD, False, &text_data);
3759 object_type = OBJ_BLOCK;
3760 break;
3761
3762 /* lists. The COMPACT tag is ignored */
3763 case HT_UL:
3764 case HT_DIR:
3765 case HT_MENU:
3766 if(temp->is_end)
3767 {
3768
3769 ul_level--;
3770 ident_level--;
3771 if(ident_level < 0)
3772 {
3773 if(HTML_ATTR(bad_html_warnings))
3774 _XmHTMLWarning(__WFUNC__(html, func),
3775 XMHTML_MSG_46, temp->line);
3776 ident_level = 0;
3777 }
3778 current_list = (ident_level ? ident_level - 1 : 0);
3779 }
3780 else
3781 {
3782 int mark_id;
3783 /* avoid overflow of mark id array */
3784 mark_id = ul_level % UL_ARRAYSIZE;
3785
3786 /* set default marker & list start */
3787 list_stack[ident_level].marker = ul_markers[mark_id].type;
3788 list_stack[ident_level].level = 0;
3789 list_stack[ident_level].type = temp->id;
3790
3791 if(ident_level == XmHTML_NESTED_LISTS_MAX)
3792 {
3793 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_47,
3794 XmHTML_NESTED_LISTS_MAX, temp->line);
3795 ident_level = XmHTML_NESTED_LISTS_MAX-1;
3796 }
3797 current_list = ident_level;
3798
3799 if(temp->id == HT_UL)
3800 {
3801 char *chPtr;
3802 /* check if user specified a custom marker */
3803 chPtr = temp->attributes ?
3804 _XmHTMLTagGetValue(temp->attributes, "type") : NULL;
3805 if(chPtr != NULL)
3806 {
3807 /*
3808 * Walk thru the list of possible markers. If a
3809 * match is found, store it so we can switch back
3810 * to the correct marker once this list terminates.
3811 */
3812 for(i = 0 ; i < UL_ARRAYSIZE; i++)
3813 {
3814 if(!(strcasecmp(ul_markers[i].name, chPtr)))
3815 {
3816 list_stack[ident_level].marker =
3817 ul_markers[i].type;
3818 break;
3819 }
3820 }
3821 free(chPtr);
3822 }
3823 }
3824 ul_level++;
3825 ident_level++;
3826 }
3827 linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
3828 object_type = OBJ_BLOCK;
3829 break;
3830
3831 case HT_OL:
3832 if(temp->is_end)
3833 {
3834 /* must be reset properly, only possible for <ol> lists. */
3835 list_stack[current_list].isindex = False;
3836
3837 ol_level--;
3838 ident_level--;
3839 if(ident_level < 0)
3840 {
3841 if(HTML_ATTR(bad_html_warnings))
3842 _XmHTMLWarning(__WFUNC__(html, func),
3843 XMHTML_MSG_46, temp->line);
3844 ident_level = 0;
3845 }
3846 current_list = (ident_level ? ident_level - 1 : 0);
3847 }
3848 else
3849 {
3850 int mark_id;
3851 char *chPtr;
3852
3853 /* avoid overflow of mark id array */
3854 mark_id = ol_level % OL_ARRAYSIZE;
3855
3856 /* set default marker & list start */
3857 list_stack[ident_level].marker = ol_markers[mark_id].type;
3858 list_stack[ident_level].level = 0;
3859 list_stack[ident_level].type = temp->id;
3860
3861 if(ident_level == XmHTML_NESTED_LISTS_MAX)
3862 {
3863 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_47,
3864 XmHTML_NESTED_LISTS_MAX, temp->line);
3865 ident_level = XmHTML_NESTED_LISTS_MAX-1;
3866 }
3867 current_list = ident_level;
3868
3869 /* check if user specified a custom marker */
3870 chPtr = temp->attributes ?
3871 _XmHTMLTagGetValue(temp->attributes, "type") : NULL;
3872 if(chPtr != NULL)
3873 {
3874 /*
3875 * Walk thru the list of possible markers. If a
3876 * match is found, store it so we can switch back
3877 * to the correct marker once this list terminates.
3878 */
3879 for(i = 0 ; i < OL_ARRAYSIZE; i++)
3880 {
3881 if(!(strcmp(ol_markers[i].name, chPtr)))
3882 {
3883 list_stack[ident_level].marker =
3884 ol_markers[i].type;
3885 break;
3886 }
3887 }
3888 free(chPtr);
3889 }
3890
3891 /* see if a start tag exists */
3892 if(temp->attributes &&
3893 _XmHTMLTagCheck(temp->attributes, "start"))
3894 {
3895 /* pick up a start spec */
3896 list_stack[ident_level].level =
3897 _XmHTMLTagGetNumber(temp->attributes, "start", 0);
3898 list_stack[ident_level].level--;
3899 }
3900
3901 /* see if we have to propage the current index number */
3902 list_stack[ident_level].isindex = temp->attributes ?
3903 _XmHTMLTagCheck(temp->attributes, "isindex") : False;
3904 ol_level++;
3905 ident_level++;
3906 }
3907 linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
3908 object_type = OBJ_BLOCK;
3909 break;
3910
3911 case HT_LI:
3912 if(temp->is_end) /* optional termination */
3913 object_type = OBJ_BLOCK;
3914 else
3915 {
3916 char *chPtr;
3917
3918 /* increase list counter */
3919 list_stack[current_list].level++;
3920
3921 /* check if user specified a custom marker */
3922 chPtr = temp->attributes ?
3923 _XmHTMLTagGetValue(temp->attributes, "type") : NULL;
3924 if(chPtr != NULL)
3925 {
3926 /*
3927 * depending on current list type, check and set
3928 * the marker.
3929 */
3930 if(list_stack[current_list].type == HT_OL)
3931 {
3932 for(i = 0 ; i < OL_ARRAYSIZE; i++)
3933 {
3934 if(!(strcmp(ol_markers[i].name, chPtr)))
3935 {
3936 list_stack[current_list].marker =
3937 ol_markers[i].type;
3938 break;
3939 }
3940 }
3941 }
3942 else if(list_stack[current_list].type == HT_UL)
3943 {
3944 for(i = 0 ; i < UL_ARRAYSIZE; i++)
3945 {
3946 if(!(strcmp(ul_markers[i].name, chPtr)))
3947 {
3948 list_stack[current_list].marker =
3949 ul_markers[i].type;
3950 break;
3951 }
3952 }
3953 }
3954 else /* dir, menu, dt elements */
3955 list_stack[current_list].marker = XmMARKER_NONE;
3956 free(chPtr);
3957 }
3958 /* check if user specified a custom number for ol lists */
3959 if(list_stack[current_list].type == HT_OL &&
3960 temp->attributes &&
3961 _XmHTMLTagCheck(temp->attributes, "value"))
3962 {
3963 list_stack[current_list].level =
3964 _XmHTMLTagGetNumber(temp->attributes, "value", 0);
3965 }
3966 /*
3967 * If the current list is an index, create a prefix for
3968 * the current item
3969 */
3970 if(list_stack[current_list].isindex)
3971 {
3972 words = indexToWord(html, list_stack, current_list,
3973 element, in_pre);
3974 n_words = 1;
3975 }
3976 object_type = OBJ_BULLET;
3977 }
3978 linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
3979 break;
3980
3981 case HT_DL:
3982 if(temp->is_end)
3983 ident_level--;
3984 else
3985 ident_level++;
3986 linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
3987 object_type = OBJ_BLOCK;
3988 break;
3989
3990 case HT_DT:
3991 if(temp->is_end)
3992 in_dt--;
3993 else
3994 in_dt++;
3995 case HT_DD:
3996 object_type = OBJ_BLOCK;
3997 linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
3998 break;
3999
4000 /* block commands */
4001 case HT_ADDRESS:
4002 if(temp->is_end)
4003 {
4004 if(HTML_ATTR(allow_color_switching))
4005 fg = (Pixel)StackPop(fg_color_stack);
4006 font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
4007 }
4008 else
4009 {
4010 if(HTML_ATTR(allow_color_switching))
4011 PUSH_COLOR(html);
4012 StackPushDouble(font_stack, font, basefont);
4013 font = _XmHTMLLoadFont(html, temp->id, basefont, font);
4014
4015 /* new height for linefeeds */
4016 fnheight = font->lineheight;
4017 }
4018 linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
4019 object_type = OBJ_BLOCK;
4020 break;
4021
4022 /* <BR> is a special word */
4023 case HT_BR:
4024 #if 0
4025 {
4026 String chPtr;
4027
4028 if((chPtr = _XmHTMLTagGetValue(temp->attributes,
4029 "clear")) != NULL && locase(chPtr[0]) != 'n')
4030 {
4031 /* all clear attribs but none reset the linefeeder */
4032 (void)CheckLineFeed(CLEAR_HARD, True);
4033 linefeed = CLEAR_ALL;
4034
4035 if(locase(chPtr[0]) == 'l') /* clear = left */
4036 halign = XmHALIGN_LEFT;
4037 else if(locase(chPtr[0]) == 'r') /* clear = right */
4038 halign = XmHALIGN_RIGHT;
4039 /* no default */
4040
4041 free(chPtr);
4042 }
4043 else /* fix 01/20/97-02, kdh */
4044 linefeed = CheckLineFeed(CLEAR_SOFT, False);
4045 }
4046 #endif
4047 if((linefeed = CheckLineFeed(CLEAR_SOFT, False,
4048 &text_data)) != CLEAR_NONE)
4049 {
4050 words = BreakToWord(&height, font, linefeed, element);
4051 n_words = 1;
4052 object_type = OBJ_TEXT;
4053 text_data &= ~TEXT_SPACE_LEAD & ~TEXT_SPACE_TRAIL;
4054 text_data |= TEXT_BREAK; /* it's a linebreak */
4055 }
4056 else
4057 {
4058 /* linebreak follows a stronger linebreak, ignore */
4059 object_type = OBJ_NONE;
4060 ignore = True;
4061 }
4062 break;
4063
4064 case HT_TAB:
4065 {
4066 char *chPtr;
4067
4068 object_type = OBJ_TEXT;
4069
4070 element->len = 8; /* default tabsize */
4071
4072 /* see if we have a width spec */
4073 chPtr = temp->attributes ?
4074 _XmHTMLTagGetValue(temp->attributes, "size") : NULL;
4075 if(chPtr != NULL)
4076 {
4077 element->len = atoi(chPtr);
4078 free(chPtr);
4079 }
4080 n_words = 1;
4081 words = SetTab(element->len, &height, font, element, tka);
4082 }
4083 break;
4084
4085 case HT_PRE:
4086 if(temp->is_end)
4087 {
4088 if(HTML_ATTR(allow_color_switching))
4089 fg = (Pixel)StackPop(fg_color_stack);
4090 font = (XmHTMLfont*)StackPopDouble(font_stack, basefont);
4091 in_pre = False;
4092 }
4093 else
4094 {
4095 if(HTML_ATTR(allow_color_switching))
4096 PUSH_COLOR(html);
4097 StackPushDouble(font_stack, font, basefont);
4098 font = _XmHTMLLoadFont(html, temp->id, basefont, font);
4099 in_pre = True;
4100
4101 /* new height for linefeeds */
4102 fnheight = font->lineheight;
4103 }
4104 linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
4105 object_type = OBJ_BLOCK;
4106 break;
4107
4108 case HT_BLOCKQUOTE:
4109 if(temp->is_end)
4110 {
4111 ident_level--;
4112 if(HTML_ATTR(allow_color_switching))
4113 fg = (Pixel)StackPop(fg_color_stack);
4114 }
4115 else
4116 {
4117 ident_level++;
4118 if(HTML_ATTR(allow_color_switching))
4119 PUSH_COLOR(html);
4120 }
4121 linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
4122 object_type = OBJ_BLOCK;
4123 break;
4124
4125 /*****
4126 * <P> and <DIV> are equal all except for the amount of
4127 * vertical whitespace added: <P> causes a hard linebreak whereas
4128 * <DIV> causes a soft linebreak.
4129 *****/
4130 case HT_P:
4131 case HT_DIV:
4132 if(temp->is_end)
4133 {
4134 halign = (Alignment)StackPop(align_stack);
4135 /*
4136 * Paragraph ending also adds linespacing (natural flow
4137 * of text between paragraphs).
4138 */
4139 linefeed = CheckLineFeed(
4140 (temp->id == HT_P ? CLEAR_HARD : CLEAR_SOFT), False,
4141 &text_data);
4142
4143 /* do we have a color attrib? */
4144 if(HTML_ATTR(allow_color_switching))
4145 fg = (Pixel)StackPop(fg_color_stack);
4146 }
4147 else
4148 {
4149 StackPush(align_stack, halign);
4150 halign = temp->attributes ?
4151 _XmHTMLGetHorizontalAlignment(temp->attributes, halign)
4152 : halign;
4153 linefeed = CheckLineFeed(
4154 (temp->id == HT_P ? CLEAR_HARD : CLEAR_SOFT), False,
4155 &text_data);
4156 /* do we have a color attrib? */
4157 if(HTML_ATTR(allow_color_switching))
4158 PUSH_COLOR(html);
4159 }
4160 object_type = OBJ_BLOCK;
4161 break;
4162
4163 case HT_CENTER:
4164 if(temp->is_end)
4165 {
4166 halign = (Alignment)StackPop(align_stack);
4167 /* do we have a color attrib? */
4168 if(HTML_ATTR(allow_color_switching))
4169 fg = (Pixel)StackPop(fg_color_stack);
4170 }
4171 else
4172 {
4173 StackPush(align_stack, halign);
4174 halign = XmHALIGN_CENTER;
4175 /* do we have a color attrib? */
4176 if(HTML_ATTR(allow_color_switching))
4177 PUSH_COLOR(html);
4178 }
4179 linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
4180 object_type = OBJ_BLOCK;
4181 break;
4182
4183 case HT_HR:
4184 {
4185 /*
4186 * horizontal rules don't have an ending counterpart,
4187 * so the alignment is never pushed. If we should do that,
4188 * we would get an unbalanced stack.
4189 */
4190 if(temp->attributes)
4191 {
4192 element->halign =
4193 _XmHTMLGetHorizontalAlignment(temp->attributes,
4194 halign);
4195 /* see if we have a width spec */
4196 element->len = _XmHTMLTagCheckNumber(temp->attributes,
4197 "width", 0);
4198
4199 /* check height */
4200 height = _XmHTMLTagGetNumber(temp->attributes, "size",
4201 0);
4202 /* sanity check */
4203 if(height <= 0 )
4204 height = 2;
4205 /* y_offset is used as a flag for the NOSHADE attr. */
4206 element->y_offset =
4207 (int)_XmHTMLTagCheck(temp->attributes, "noshade");
4208 }
4209 else
4210 {
4211 element->halign = halign;
4212 element->len = 0;
4213 element->y_offset = 0;
4214 height = 0;
4215 }
4216
4217 /* do we have a color attrib? */
4218 if(HTML_ATTR(allow_color_switching) &&
4219 !HTML_ATTR(strict_checking))
4220 PUSH_COLOR(html);
4221 }
4222 /* horizontal rules always have a soft return */
4223 linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
4224 object_type = OBJ_HRULE;
4225 break;
4226
4227 /* forms */
4228 case HT_FORM:
4229 if(temp->is_end)
4230 _XmHTMLEndForm(html);
4231 else
4232 _XmHTMLStartForm(html, temp->attributes);
4233
4234 /* only need form data */
4235 ignore = True;
4236 break;
4237
4238 case HT_SELECT:
4239 /* this form component can only contain option tags */
4240 if((words = SelectToWord(html, temp, &n_words, &width, &height,
4241 element, in_pre)) == NULL)
4242 {
4243 ignore = True;
4244 break;
4245 }
4246 /* walk to the end of this select */
4247 temp = temp->next;
4248 for(; temp != NULL && temp->id != HT_SELECT;
4249 temp = temp->next);
4250
4251 text_data |= TEXT_FORM;
4252 object_type = in_pre ? OBJ_PRE_TEXT : OBJ_TEXT;
4253 break;
4254
4255 /*****
4256 * It's an error if we get this, SelectToWord deals with these
4257 * tags.
4258 *****/
4259 case HT_OPTION:
4260 if(!temp->is_end)
4261 {
4262 if(HTML_ATTR(bad_html_warnings))
4263 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_48,
4264 html_tokens[HT_OPTION], html_tokens[HT_SELECT],
4265 temp->line);
4266 }
4267 ignore = True;
4268 break;
4269
4270 case HT_TEXTAREA:
4271 if((words = TextAreaToWord(html, temp, &n_words, &width,
4272 &height, element, in_pre)) == NULL)
4273 {
4274 ignore = True;
4275 break;
4276 }
4277 /*
4278 * Walk to the end of this textarea. If there was any text
4279 * provided, we've already picked it up.
4280 */
4281 temp = temp->next;
4282 for(; temp != NULL && temp->id != HT_TEXTAREA;
4283 temp = temp->next);
4284
4285 text_data |= TEXT_FORM;
4286 object_type = in_pre ? OBJ_PRE_TEXT : OBJ_TEXT;
4287 break;
4288
4289 case HT_INPUT:
4290 if((words = InputToWord(html, temp->attributes, &n_words,
4291 &width, &height, element, in_pre)) == NULL)
4292 {
4293 ignore = True;
4294 break;
4295 }
4296 /* type=image is promoted to a true image */
4297 if(words->form->type == FORM_IMAGE)
4298 {
4299 text_data |= TEXT_IMAGE;
4300
4301 /* allocate a new anchor */
4302 if((form_anchor_data = temp->attributes ?
4303 _XmHTMLNewAnchor(html, temp): NULL) == NULL)
4304 break;
4305
4306 /* promote to internal form anchor */
4307 form_anchor_data->url_type = ANCHOR_FORM_IMAGE;
4308
4309 new_anchors++;
4310
4311 /* set proper element bits, we assume it's a plain one */
4312 element_data |= ELE_ANCHOR;
4313 }
4314 else
4315 {
4316 text_data |= TEXT_FORM;
4317 }
4318 object_type = in_pre ? OBJ_PRE_TEXT : OBJ_TEXT;
4319 break;
4320
4321 /* applets */
4322 case HT_APPLET:
4323 if(temp->is_end)
4324 {
4325 /*
4326 * INSERT CODE
4327 * to end this applet
4328 */
4329 }
4330 else
4331 {
4332 if(HTML_ATTR(bad_html_warnings))
4333 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_49,
4334 html_tokens[HT_APPLET]);
4335 /*
4336 * INSERT CODE
4337 * to start this applet
4338 */
4339 }
4340 object_type = OBJ_APPLET;
4341 ignore = True;
4342 break;
4343
4344 case HT_PARAM: /* applet params */
4345 if(HTML_ATTR(bad_html_warnings))
4346 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_49,
4347 html_tokens[HT_PARAM]);
4348 object_type = OBJ_APPLET;
4349 ignore = True;
4350 break;
4351
4352 case HT_MAP:
4353 if(temp->is_end)
4354 {
4355 _XmHTMLStoreImagemap(html, imageMap);
4356 imageMap = NULL;
4357 }
4358 else
4359 {
4360 String chPtr;
4361
4362 chPtr = temp->attributes ?
4363 _XmHTMLTagGetValue(temp->attributes, "name") : NULL;
4364 if(chPtr != NULL)
4365 {
4366 imageMap = _XmHTMLCreateImagemap(chPtr);
4367 free(chPtr);
4368 }
4369 else if(HTML_ATTR(bad_html_warnings))
4370 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_50,
4371 temp->line);
4372 }
4373 ignore = True; /* only need imagemap name */
4374 break;
4375
4376 case HT_AREA:
4377 if(imageMap)
4378 _XmHTMLAddAreaToMap(html, imageMap, temp);
4379 else if(HTML_ATTR(bad_html_warnings))
4380 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_48,
4381 html_tokens[HT_AREA], html_tokens[HT_MAP], temp->line);
4382 ignore = True; /* only need area data */
4383 break;
4384
4385 /* tables */
4386 case HT_TABLE:
4387 if(temp->is_end)
4388 {
4389 /* wrapup current table */
4390 table = tableClose(html, table, element);
4391
4392 halign = (Alignment)StackPop(align_stack);
4393 bg = (Pixel)StackPop(bg_color_stack);
4394 object_type = OBJ_NONE;
4395 }
4396 else
4397 {
4398 /*****
4399 * Tables start cause a hard break if it has no
4400 * parent, and a soft one if it has.
4401 *****/
4402 if(table)
4403 linefeed = CheckLineFeed(CLEAR_SOFT, False, &text_data);
4404 else
4405 linefeed = CheckLineFeed(CLEAR_HARD, True, &text_data);
4406
4407 StackPush(align_stack, halign);
4408 StackPush(bg_color_stack, bg);
4409
4410 /*****
4411 * Open a new table. Returns a parent or a child table.
4412 *****/
4413 table = tableOpen(html, table, element, temp, &halign, &bg);
4414
4415 if(table == NULL)
4416 break;
4417
4418 /* new master table, insert */
4419 if(table->parent == NULL)
4420 {
4421 /* insert this table in the list of tables */
4422 if(HTML_ATTR(tables))
4423 {
4424 current_table->next = table;
4425 current_table = table;
4426 }
4427 else
4428 {
4429 HTML_ATTR(tables) = table;
4430 current_table = table;
4431 }
4432 }
4433 object_type = OBJ_TABLE;
4434 }
4435 break;
4436
4437 case HT_CAPTION: /* table caption */
4438 if(temp->is_end)
4439 {
4440 /* close the caption */
4441 tableCloseCaption(html, table, element);
4442
4443 halign = (Alignment)StackPop(align_stack);
4444 bg = (Pixel)StackPop(bg_color_stack);
4445
4446 object_type = OBJ_NONE;
4447 }
4448 else
4449 {
4450 StackPush(align_stack, halign);
4451 StackPush(bg_color_stack, bg);
4452
4453 /* captions are always centered */
4454 halign = XmHALIGN_CENTER;
4455
4456 /* open the caption */
4457 tableOpenCaption(html, table, element, temp, &bg);
4458
4459 object_type = OBJ_TABLE_FRAME;
4460 }
4461 break;
4462
4463 case HT_TR: /* table row */
4464 if(temp->is_end) /* optional termination */
4465 {
4466 /* close current row */
4467 tableCloseRow(html, table, element);
4468
4469 halign = (Alignment)StackPop(align_stack);
4470 bg = (Pixel)StackPop(bg_color_stack);
4471 object_type = OBJ_NONE;
4472 }
4473 else
4474 {
4475 /* open a row */
4476 StackPush(align_stack, halign);
4477 StackPush(bg_color_stack, bg);
4478
4479 tableOpenRow(html, table, element, temp, &halign, &bg);
4480
4481 object_type = OBJ_TABLE_FRAME;
4482 }
4483 break;
4484
4485 case HT_TH: /* header cell */
4486 case HT_TD: /* regular cell */
4487 if(temp->is_end) /* optional termination */
4488 {
4489 /* header cell used a bold font, restore */
4490 if(temp->id == HT_TH)
4491 font = (XmHTMLfont*)StackPopDouble(font_stack,basefont);
4492
4493 /* close current cell */
4494 tableCloseCell(html, table, element);
4495
4496 halign = (Alignment)StackPop(align_stack);
4497 bg = (Pixel)StackPop(bg_color_stack);
4498
4499 object_type = OBJ_NONE;
4500 }
4501 else
4502 {
4503 /* header cell uses a bold font */
4504 if(temp->id == HT_TH)
4505 {
4506 StackPushDouble(font_stack, font, basefont);
4507 font = _XmHTMLLoadFont(html, HT_B, basefont, font);
4508
4509 /* new height for linefeeds */
4510 fnheight = font->lineheight;
4511 }
4512
4513 StackPush(align_stack, halign);
4514 StackPush(bg_color_stack, bg);
4515
4516 /* open a cell */
4517 tableOpenCell(html, table, element, temp, &halign, &bg);
4518
4519 /* table cell always resets linefeeding */
4520 (void)CheckLineFeed(CLEAR_SOFT, True, &text_data);
4521
4522 object_type = OBJ_TABLE_FRAME;
4523 }
4524 break;
4525
4526 /*****
4527 * According to HTML3.2, the following elements may not occur
4528 * inside the body content, but a *lot* of HTML documents are
4529 * in direct violation with this and the parser isn't always
4530 * successfully in removing them. So we need to handle these
4531 * elements as well and skip all data between the opening and
4532 * closing element.
4533 *****/
4534 case HT_STYLE:
4535 {
4536 htmlEnum end_id = temp->id;
4537 /* move past element */
4538 temp = temp->next;
4539 /* skip it entirely */
4540 for(; temp != NULL; temp = temp->next)
4541 if(temp->id == end_id && temp->is_end)
4542 break;
4543 ignore = True;
4544 }
4545 break;
4546 case HT_SCRIPT:
4547 {
4548 htmlEnum end_id = temp->id;
4549
4550 if(HTML_ATTR(script_proc))
4551 {
4552 static XmHTMLScriptData script;
4553 String data;
4554
4555 /* initialize */
4556 memset((void*)&script, 0, sizeof(script));
4557
4558 /* set script attributes */
4559 script.type = _XmHTMLTagGetValue(temp->attributes, "type");
4560 script.lang = _XmHTMLTagGetValue(temp->attributes, "language");
4561 script.src = _XmHTMLTagGetValue(temp->attributes, "src");
4562
4563 /* move past element */
4564 temp = temp->next;
4565
4566 data = malloc(sizeof(char));
4567 data[0] = '\0';
4568
4569 /* collect all enclosed text elements */
4570 for(; temp != NULL; temp = temp->next)
4571 {
4572 if(temp->id == end_id && temp->is_end)
4573 break;
4574
4575 if(temp->element != NULL)
4576 {
4577 data = realloc(data,
4578 (strlen(data) +
4579 strlen(temp->element))*sizeof(char));
4580 strcat(data, temp->element);
4581 }
4582 }
4583 script.script = data;
4584
4585 /* call user proc for this script */
4586 (void)HTML_ATTR(script_proc)((Widget)html,
4587 &script, HTML_ATTR(client_data));
4588
4589 /* release again */
4590 if(script.type)
4591 free(script.type);
4592 if(script.lang)
4593 free(script.lang);
4594 if(script.src)
4595 free(script.src);
4596 free(script.script);
4597 }
4598 else
4599 {
4600 /* move past element */
4601 temp = temp->next;
4602 /* skip it entirely */
4603 for(; temp != NULL; temp = temp->next)
4604 if(temp->id == end_id && temp->is_end)
4605 break;
4606 }
4607 ignore = True;
4608 }
4609 break;
4610
4611 default:
4612 _XmHTMLDebug(2, ("format.c: _XmHTMLformatObjects; "
4613 "Unused element %s.\n", temp->element));
4614 ignore = True;
4615 }
4616 if(!ignore)
4617 {
4618 /* unset break bit if this is not a text object */
4619 if(object_type != OBJ_TEXT)
4620 text_data &= ~TEXT_BREAK;
4621 else if(object_type == OBJ_BLOCK)
4622 {
4623 /* clear all spacing */
4624 text_data &= ~TEXT_SPACE_LEAD & ~TEXT_SPACE_TRAIL;
4625 text_data |= TEXT_SPACE_NONE;
4626 }
4627
4628 /* adjust anchor count */
4629 if(element_data & ELE_ANCHOR)
4630 {
4631 text_data |= TEXT_ANCHOR;
4632 anchor_words += n_words;
4633 anchor_data_used = True;
4634 }
4635 /* mark object as internal anchor */
4636 if(element_data & ELE_ANCHOR_INTERN)
4637 {
4638 text_data |= TEXT_ANCHOR_INTERN;
4639 anchor_data_used = True;
4640 /* add an anchor id */
4641 element->id = named_anchors;
4642 named_anchors++;
4643 }
4644 element->text = text;
4645 element->text_data = text_data;
4646 element->words = words;
4647 element->n_words = n_words;
4648 element->width = width;
4649 element->height = height;
4650 element->fg = fg;
4651 element->bg = bg;
4652 element->font = font;
4653 element->marker = list_stack[current_list].marker;
4654 element->list_level = list_stack[current_list].level;
4655 element->table = table;
4656 /*
4657 * <dt> elements have an identation one less than the current.
4658 * all identation must use the default font (consistency).
4659 */
4660 if(in_dt && ident_level)
4661 element->ident = (ident_level-1) * XmHTML_INDENT_SPACES *
4662 HTML_ATTR(default_font)->m_width;
4663 else
4664 element->ident = ident_level * XmHTML_INDENT_SPACES *
4665 HTML_ATTR(default_font)->m_width;
4666
4667 element->linefeed = (int)((1+linefeed)*fnheight);
4668
4669 /* stupid hack so HT_HR won't mess up alignment and color stack */
4670 if(temp->id != HT_HR)
4671 {
4672 element->halign = halign;
4673 element->y_offset = y_offset;
4674 element->x_offset = x_offset;
4675 }
4676 else if(HTML_ATTR(allow_color_switching) &&
4677 !HTML_ATTR(strict_checking))
4678 fg = (Pixel)StackPop(fg_color_stack);
4679
4680 element->object_type = object_type;
4681
4682 if(object_type == OBJ_BULLET)
4683 FillBullet(html, element, tka);
4684
4685 /*****
4686 * If we have a form component of type <input type="image">, we
4687 * have promoted it to an anchor. Set this anchor data as the
4688 * anchor for this element and, as it is used only once, reset
4689 * it to NULL. In all other case we have a plain anchor.
4690 *
4691 * Note: as form components are allowed inside anchors, this is
4692 * the only place in which we can possibly have nested anchors.
4693 * This is a problem we will have to live with...
4694 *****/
4695 if(form_anchor_data)
4696 {
4697 element->anchor = form_anchor_data;
4698 form_anchor_data = NULL;
4699 element_data &= ~ELE_ANCHOR;
4700 }
4701 else
4702 element->anchor = anchor_data;
4703
4704 /*****
4705 * Discard non-spacing bits so we only carry current spacing
4706 * to the next chunk of text. Break bit must be kept to carry
4707 * lack of leading space over to the next chunk of text.
4708 *****/
4709 text_data &= (~TEXT_ANCHOR & ~TEXT_ANCHOR_INTERN &
4710 ~TEXT_IMAGE & ~TEXT_FORM); /* & ~TEXT_BREAK); */
4711
4712 InsertTableElement(element, element_data & ELE_ANCHOR);
4713 }
4714 #ifdef DEBUG
4715 else /* element will be reused if it was ignored */
4716 num_ignore++;
4717 #endif
4718
4719 /* move to next element */
4720 temp = temp->next;
4721 }
4722 /* if there still is an open table, close it now */
4723 if(table)
4724 tableClose(html, table, element);
4725
4726 /*****
4727 * Insert a dummy element at the end of the list, saves some NULL tests
4728 * in the layout routines.
4729 *****/
4730 if(ignore)
4731 {
4732 /* reuse ignored element */
4733 memset(element, 0, sizeof(XmHTMLObjectTable));
4734 element->object_type = OBJ_NONE;
4735 }
4736 else
4737 NewTableElement(element,NULL);
4738
4739 /* each element must have a font with it */
4740 element->font = HTML_ATTR(default_font);
4741
4742 InsertTableElement(element, False);
4743
4744 /*
4745 * Some sucker forget to terminate a list and parser failed to repair it.
4746 * Spit out a warning.
4747 */
4748 if(HTML_ATTR(bad_html_warnings) && ident_level != 0)
4749 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_51);
4750
4751 /* clear all allocated stacks */
4752 #ifdef DEBUG
4753 if((i = StackDestroy(fg_color_stack)) != 0)
4754 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_52,
4755 "Foreground color", i);
4756 if((i = StackDestroy(bg_color_stack)) != 0)
4757 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_52,
4758 "Background color", i);
4759 if((i = StackDestroy(align_stack)) != 0)
4760 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_52,
4761 "Horizontal Alignment", i);
4762 if((i = StackDestroy(font_stack)) != 0)
4763 _XmHTMLWarning(__WFUNC__(html, func), XMHTML_MSG_52,
4764 "Font", i);
4765 #else
4766 (void)StackDestroy(fg_color_stack);
4767 (void)StackDestroy(bg_color_stack);
4768 (void)StackDestroy(align_stack);
4769 (void)StackDestroy(font_stack);
4770 #endif
4771
4772 /*
4773 * allocate memory for all anchor words in this document, gets filled in
4774 * paint.c
4775 */
4776 if(anchor_words)
4777 {
4778 HTML_ATTR(anchors)= (XmHTMLWord*)calloc(anchor_words+1,
4779 sizeof(XmHTMLWord));
4780
4781 _XmHTMLDebug(2,("_XmHTMLFormatObjects: anchors contain %i words\n",
4782 anchor_words));
4783 HTML_ATTR(anchor_words) = anchor_words;
4784 }
4785
4786 /* allocated memory for all named anchors. Gets filled in paint.c */
4787 if(named_anchors)
4788 {
4789 HTML_ATTR(named_anchors) =
4790 (XmHTMLObjectTable*)calloc(named_anchors+1,
4791 sizeof(XmHTMLObjectTable));
4792
4793 HTML_ATTR(num_named_anchors) = named_anchors;
4794 }
4795
4796 _XmHTMLDebug(2,("_XmHTMLFormatObjects: formatted %li elements of which %li"
4797 " anchors.\n", list_data.num_elements, list_data.num_anchors));
4798 _XmHTMLDebug(2,("_XmHTMLFormatObjects: found %i named anchors.\n",
4799 named_anchors));
4800 _XmHTMLDebug(2, ("_XmHTMLformatObjects, allocated %i elements and "
4801 "ignored %i objects.\n", allocated, num_ignore));
4802
4803 _XmHTMLDebug(2, ("_XmHTMLformatObjects, allocated %i XmHTMLAnchor "
4804 "objects\n", new_anchors));
4805
4806 /* store the anchor list */
4807 HTML_ATTR(anchor_data) = list_data.anchor_head;
4808
4809 /* Since the table head is a dummy element, we return the next one */
4810 list_data.current = list_data.head->next;
4811
4812 /* this is *required* */
4813 if(list_data.current)
4814 list_data.current->prev = NULL;
4815
4816 /* free top of the list */
4817 free(list_data.head);
4818 list_data.head = NULL;
4819
4820 /* store it */
4821 HTML_ATTR(formatted) = list_data.current;
4822
4823 /* all done! */
4824 }
4825
4826 /********
4827 ****** Public XmHTML Functions
4828 ********/
4829
4830 /* all url types we know about, 17 in total */
4831 static String anchor_tokens[] = {"about", "exec", "file", "ftp", "gopher",
4832 "help", "http", "https" ,"info", "mailto", "man", "news", "pipe", "telnet",
4833 "wais", "xexec", "zzz"};
4834
4835 /*****
4836 * Name: XmHTMLGetURLType
4837 * Return Type: URLType
4838 * Description: tries to figure out what type of url the given href is
4839 * In:
4840 * href: url specification
4841 * Returns:
4842 * type of url when we know it, ANCHOR_UNKNOWN otherwise.
4843 *****/
4844 URLType
XmHTMLGetURLType(String href)4845 XmHTMLGetURLType(String href)
4846 {
4847 char *chPtr;
4848
4849 if(href == NULL || *href == '\0')
4850 return(ANCHOR_UNKNOWN);
4851
4852 _XmHTMLDebug(2, ("format.c: XmHTMLGetURLType; checking url type of %s\n",
4853 href));
4854
4855 /* first pick up any leading url spec */
4856 if((chPtr = strstr(href, ":")) != NULL && (chPtr - href) < 7)
4857 {
4858 char token[7];
4859 URLType ret_val;
4860
4861 strncpy(token, href, chPtr - href);
4862 token[chPtr - href] = '\0'; /* NULL terminate */
4863
4864 ret_val = (URLType)stringToToken(token, anchor_tokens,
4865 (int)ANCHOR_UNKNOWN);
4866
4867 #ifdef DEBUG
4868 if(ret_val == ANCHOR_UNKNOWN)
4869 _XmHTMLDebug(2, ("format.c: XmHTMLGetURLType; unknown type %s\n",
4870 token));
4871 #endif
4872 return(ret_val);
4873 }
4874 return(href[0] == '#' ? ANCHOR_JUMP : ANCHOR_FILE_LOCAL);
4875 }
4876