/***** * format.c : XmHTML formatting routines: translates parsed HTML to info * required for displaying a HTML page. * * This file Version $Revision: 1.13 $ * * Creation date: Tue Nov 26 17:03:09 GMT+0100 1996 * Last modification: $Date: 1999/07/29 01:26:28 $ * By: $Author: sopwith $ * Current State: $State: Exp $ * * Author: newt * (C)Copyright 1995-1996 Ripley Software Development * All Rights Reserved * * This file is part of the XmHTML TWidget Library. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * *****/ /***** * ChangeLog * $Log: format.c,v $ * Revision 1.13 1999/07/29 01:26:28 sopwith * * * Fix all warnings. * * Revision 1.12 1999/06/02 01:00:39 unammx * * 1999-06-01 Akira Higuchi * * * libgnomeui/gnome-canvas-text.c: * * libgnomeui/gnome-icon-item.c: * * libgnomeui/gnome-less.c: Replace some gdk_font_load() calls with * gdk_fontset_load. Use a more open fontset rule to load the fonts. * * 1999-06-01 Akira Higuchi * * * gtk-xmhtml/XmHTMLP.h: Add three members lbearing, rbearing, * and width. These members are computed in allocFont(). * * * gtk-xmhtml/toolkit.h: Remove Toolkit_XFont() macro. * * * gtk-xmhtml/XmHTML.c: * * gtk-xmhtml/fonts.c: * * gtk-xmhtml/format.c: * * gtk-xmhtml/gtk-xmhtml.c: * * gtk-xmhtml/layout.c: * * gtk-xmhtml/paint.c: Add fontset support. We use gdk_fontset_load() * instead of gdk_font_load() iff a fontset is supplied for label * widgets. * * * gtk-xmhtml/test.c: Add gtk_set_locale() call before gtk_init(). * * Revision 1.11 1998/06/23 18:45:55 unammx * James Henstridge's signal fixes to GtkXmHTML * * Revision 1.10 1998/02/12 03:08:39 unammx * Merge to Koen's XmHTML 1.1.2 + following fixes: * * Wed Feb 11 20:27:19 1998 Miguel de Icaza * * * gtk-forms.c (freeForm): gtk_destroy_widget is no longer needed * with the refcounting changes. * * * gtk-xmhtml.c (gtk_xmhtml_remove): Only god knows why I was * adding the just removed widget. * * Revision 1.9 1998/01/14 05:49:51 unammx * Tue Jan 13 22:04:43 1998 Federico Mena * * * gtk-xmhtml.c (gtk_xmhtml_new): The widget starts up frozen and * thaws itself when it is realized. This fixes all of the problems * regarding realization, gc creation, and window background setting. * * (Federico and Miguel) * * Revision 1.8 1998/01/14 04:11:52 unammx * Tue Jan 13 22:04:43 1998 Federico Mena * * * Lots of changes all over the place to fix colors. Things are * *almost* working right now. I think I'm only missing setting the * window backgrounds appropriately. Several things were done: * * - Motif's color and gc fields from Core and XmManager were * replicated inside the GtkXmHTML widget structure. * * - Macros were created in toolkit.h to use these fields. * * - Instead of the old kludgy set_{fore,back}ground_internal * functions, we now set the window background directly. * This does not work perfectly; I'll look into it. * * - I created a shade_color() function in colors.c (ok, ok, * I stole it from gtkstyle.c) which mimics XmGetColors() * -- it calculates shaded colors for the 3D look. * * I hope to fix the remaining problems with window backgrounds real * soon now. * * Revision 1.7 1998/01/09 06:10:23 unammx * Fixed (?) background colors of the HTML widget. I'm not 100% sure I did it * the right way, but it seems to work. * * - Federico * * Revision 1.6 1997/12/29 22:16:25 unammx * This version does: * * - Sync with Koen to version Beta 1.1.2c of the XmHTML widget. * Includes various table fixes. * * - Callbacks are now properly checked for the Gtk edition (ie, * signals). * * Revision 1.5 1997/12/27 20:58:17 unammx * More access functions to the widget internals. I missed these * yesterday (ie, those that did not require SetValues validation * now have an explicit routine to change the values). * * Frame support depends on the client of the widget, we should catch * that signal and do something with it, I have not figured out exacly * how it works, but example_2 in the XmHTML-1.1.1 distribution has an * example of this working. * * Miguel. * * Revision 1.4 1997/12/25 01:34:11 unammx * Good news for the day: * * I have upgraded our XmHTML sources to XmHTML 1.1.1. * * This basically means that we got table support :-) * * Still left to do: * * - Set/Get gtk interface for all of the toys in the widget. * - Frame support is broken, dunno why. * - Form support (ie adding widgets to it) * * Miguel. * * Revision 1.3 1997/12/13 01:49:15 unammx * your daily dose of ported XmHTML code, non functional as usual -mig * * Revision 1.2 1997/12/11 21:20:21 unammx * Step 2: more gtk/xmhtml code, still non-working - mig * * Revision 1.1 1997/11/28 03:38:56 gnomecvs * Work in progress port of XmHTML; No, it does not compile, don't even try -mig * * Revision 1.17 1997/10/23 00:24:56 newt * XmHTML Beta 1.1.0 release * * Revision 1.16 1997/08/31 17:34:24 newt * renamed _rec structures to Rec. * * Revision 1.15 1997/08/30 00:55:24 newt * Completed
support. * Bugfix in _XmHTMLInitializeFontSizeLists, the default font is now properly * changed. * Made the font loading routines again robuster. * ParseBodyTags now always attempts to load a body image. * Made XmHTMLGetURLType a bit stricter. * * Revision 1.14 1997/08/01 13:00:21 newt * Bugfixes in font switching (.........) now * properly handler. Enhanced form support. * * Revision 1.13 1997/05/28 01:46:35 newt * Added support for the XmNbodyImage resource: it's now used but only if no * bgcolor resource has been set. * * Revision 1.12 1997/04/29 14:26:18 newt * HTML forms changes * * Revision 1.11 1997/04/03 05:34:25 newt * _XmHTMLLoadBodyImage added. * Placed a large number of warnings between a #ifdef PEDANTIC/#endif * * Revision 1.10 1997/03/28 07:12:43 newt * Fixed buffer overrun in TexToPre. * Fixed font resolution: x and y resolution are now always equal. * XmHTML now ignores the ending body tag. * * Revision 1.9 1997/03/20 08:10:04 newt * Split font cache in a true cache and a font stack. * Added stack checks when document has been formatted. * * Revision 1.8 1997/03/11 19:52:17 newt * added ImageToWord * * Revision 1.7 1997/03/04 00:59:26 newt * ? * * Revision 1.6 1997/03/02 23:17:46 newt * Way too many changes. Most important: font loading/switching scheme; anchor * treatment; image/imagemap treatment * * Revision 1.5 1997/02/11 02:08:44 newt * Way to many. Anchor treatment has been changed completely. * Bugfixes in anchor parsing. Potential buffer overruns eliminated. * * Revision 1.4 1997/02/04 02:56:49 newt * Bugfix in LoadQueryFont. * Added code to deal with the basefont element. * Changed the font element handling. * * Revision 1.3 1997/01/09 06:55:39 newt * expanded copyright marker * * Revision 1.2 1997/01/09 06:44:42 newt * lots of changes: linebreaking and changes related to changed XmHTMLWord * * Revision 1.1 1996/12/19 02:17:10 newt * Initial Revision * *****/ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include /* isspace, tolower */ /* Local includes */ #include "XmHTMLP.h" #include "XmHTMLfuncs.h" /*** External Function Prototype Declarations ***/ /*** Public Variable Declarations ***/ /*** Private Datatype Declarations ****/ #define MAX_NESTED_LISTS 26 /* maximum no of nested lists */ #define IDENT_SPACES 3 /* length of each indent, in no of spaces */ typedef struct{ String name; Marker type; /* Marker is an enumeration type defined in XmHTMLP.h */ }listMarkers; /* for nesting of ordered and unordered lists */ typedef struct{ Boolean isindex; /* propagate index numbers? */ int level; /* item number */ htmlEnum type; /* ol or ul, used for custom markers */ Marker marker; /* marker to use */ }listStack; /* for nesting of colors */ typedef struct colorRec{ unsigned long color; struct colorRec *next; }colorStack; typedef struct fontRec{ int size; XmHTMLfont *font; /* ptr to cached font */ struct fontRec *next; }fontStack; /* for nesting of alignments */ typedef struct alignRec{ Alignment align; struct alignRec *next; }alignStack; /*** Private Function Prototype Declarations ****/ /**** * Formatting routines *****/ /* Created a new element for the formatted element table */ static XmHTMLObjectTableElement NewTableElement(XmHTMLObject *data); /* Insert and element into the formatted element table */ static void InsertTableElement(XmHTMLWidget html, XmHTMLObjectTableElement element, Boolean is_anchor); /* Release the formatted element table */ static void FreeObjectTable(XmHTMLObjectTable *list); /* Free the given list of tables */ static void freeTables(XmHTMLTable *table); /* Initialize the formatted element table */ static void InitObjectTable(XmHTMLObjectTable *list, XmHTMLAnchor *anchors); /* push/pop a font on font stack */ static void PushFont(XmHTMLfont *font, int size); static XmHTMLfont *PopFont(int *size); /* copy given text into an internal buffer */ static String CopyText(XmHTMLWidget html, String text, Boolean formatted, Byte *text_data, Boolean expand_escapes); /* collapse all consecutive whitespace into a single space */ static void CollapseWhiteSpace(String text); /* Split raw text into an array of words */ static XmHTMLWord* TextToWords(String text, int *num_words, Dimension *height, XmHTMLfont *font, Byte line_data, Byte text_data, XmHTMLObjectTableElement owner); /* Split an image into an array of words ;-) */ static XmHTMLWord *ImageToWord(XmHTMLWidget html, String attributes, int *num_words, Dimension *height, XmHTMLObjectTableElement owner, Boolean formatted); static XmHTMLWord *allocFormWord(XmHTMLForm *form, Dimension *width, Dimension *height, XmHTMLObjectTableElement owner, Boolean formatted); static XmHTMLWord *InputToWord(XmHTMLWidget html, String attributes, int *num_words, Dimension *width, Dimension *height, XmHTMLObjectTableElement owner, Boolean formatted); static XmHTMLWord *SelectToWord(XmHTMLWidget html, XmHTMLObject *start, int *num_words, Dimension *width, Dimension *height, XmHTMLObjectTableElement owner, Boolean formatted); static XmHTMLWord *TextAreaToWord(XmHTMLWidget html, XmHTMLObject *start, int *num_words, Dimension *width, Dimension *height, XmHTMLObjectTableElement owner, Boolean formatted); static XmHTMLWord *BreakToWord(Dimension *height, XmHTMLfont *font, int linefeed, XmHTMLObjectTableElement owner); static XmHTMLWord *MakeDummyWord(Dimension *height, XmHTMLfont *font, Byte line_data, XmHTMLObjectTableElement owner); /* Split raw text into a chunk of preformatted lines */ static XmHTMLWord *TextToPre(String text, int *num_words, XmHTMLfont *font, Byte line_data, XmHTMLObjectTableElement owner); /* Insert a horizontal tab */ static XmHTMLWord* SetTab(int size, Dimension *height, XmHTMLfont *font, XmHTMLObjectTableElement owner); /* Initialize a bullet (a real bullet or some number) */ static void FillBullet(XmHTMLWidget html, XmHTMLObjectTableElement owner); /* get properties of a table element */ static TableProperties *tableCheckProperties(XmHTMLWidget html, String attributes, TableProperties *parent, Alignment halign, Pixel bg); /* open a new table */ static XmHTMLTable *tableOpen(XmHTMLWidget html, XmHTMLTable *parent, XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign, Pixel *bg); /* close the current table */ static XmHTMLTable *tableClose(XmHTMLWidget html, XmHTMLTable *parent, XmHTMLObjectTableElement end); /* open a caption in the current table */ static void tableOpenCaption(XmHTMLWidget html, XmHTMLTable *parent, XmHTMLObjectTableElement start, XmHTMLObject *obj, Pixel *bg); /* close the current caption */ static void tableCloseCaption(XmHTMLWidget html, XmHTMLTable *parent, XmHTMLObjectTableElement end); /* open a row in the current table */ static void tableOpenRow(XmHTMLWidget html, XmHTMLTable *parent, XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign, Pixel *bg); /* close the current row */ static void tableCloseRow(XmHTMLWidget html, XmHTMLTable *parent, XmHTMLObjectTableElement end); /* open a cell in the current row */ static void tableOpenCell(XmHTMLWidget html, XmHTMLTable *parent, XmHTMLObjectTableElement start, XmHTMLObject *obj, Alignment *halign, Pixel *bg); /* close the current cell in the current row */ static void tableCloseCell(XmHTMLWidget html, XmHTMLTable *parent, XmHTMLObjectTableElement end); /* Parse body tags and update the TWidget */ static void ParseBodyTags(XmHTMLWidget html, XmHTMLObject *data); /* Check whether a linefeed is required or not */ static int CheckLineFeed(int this, Boolean force); /* push/pop a color on the color stack */ static void PushFGColor(Pixel color); static void PushBGColor(Pixel color); static Pixel PopFGColor(void); static Pixel PopBGColor(void); /* push/pop an alignment on/from the alignment stack */ static void PushAlignment(Alignment align); static Alignment PopAlignment(void); /* split the given anchor spec into a href, target and other stuff */ static void parseHref(String text, XmHTMLAnchor *anchor); /*** Private Variable Declarations ***/ /* Element data bits */ #define ELE_ANCHOR (1<<1) #define ELE_ANCHOR_TARGET (1<<2) #define ELE_ANCHOR_VISITED (1<<3) #define ELE_ANCHOR_INTERN (1<<4) #define ELE_UNDERLINE (1<<5) #define ELE_UNDERLINE_TEXT (1<<6) #define ELE_STRIKEOUT (1<<7) #define ELE_STRIKEOUT_TEXT (1<<8) /* Private formatted element table data */ static struct{ unsigned long num_elements; unsigned long num_anchors; XmHTMLObjectTableElement head; XmHTMLObjectTableElement current; XmHTMLAnchor *anchor_head; XmHTMLAnchor *anchor_current; }list_data; /* color and alignment stacks */ static colorStack fg_color_base, *fg_color_stack; static colorStack bg_color_base, *bg_color_stack; static alignStack align_base, *align_stack; static fontStack font_base, *font_stack; /* Marker information for HTML lists, ordered list. */ #define OL_ARRAYSIZE 5 static listMarkers ol_markers[OL_ARRAYSIZE] = { {"1", XmMARKER_ARABIC}, {"a", XmMARKER_ALPHA_LOWER}, {"A", XmMARKER_ALPHA_UPPER}, {"i", XmMARKER_ROMAN_LOWER}, {"I", XmMARKER_ROMAN_UPPER}, }; /* Unordered list. */ #define UL_ARRAYSIZE 3 static listMarkers ul_markers[UL_ARRAYSIZE] = { {"disc", XmMARKER_DISC}, {"square", XmMARKER_SQUARE}, {"circle", XmMARKER_CIRCLE}, }; #ifdef DEBUG static int allocated; #endif /***** * Name: NewTableElement * Return Type: XmHTMLObjectTableElement * Description: creates a ObjectTableElement and fills it. * In: * data: raw data for this element. * Returns: * the newly created element. *****/ static XmHTMLObjectTableElement NewTableElement(XmHTMLObject *data) { static XmHTMLObjectTableElement element = NULL; element = (XmHTMLObjectTableElement)malloc(sizeof(XmHTMLObjectTable)); /* initialise to zero */ (void)memset(element, 0, sizeof(XmHTMLObjectTable)); /* fill in appropriate fields */ element->object = data; #ifdef DEBUG allocated++; #endif return(element); } /***** * Name: InsertTableElement * Return Type: void * Description: inserts a given formatted element in the list of elements. * In: * w: XmHTMLWidget to which this element belongs. * element: element to add * is_anchor: true if this element is an anchor. * Returns: * nothing. *****/ static void InsertTableElement(XmHTMLWidget html, XmHTMLObjectTableElement element, Boolean is_anchor) { /* attach prev and next ptrs to the appropriate places */ element->prev = list_data.current; list_data.current->next = element; list_data.current = element; /* increment element counter */ list_data.num_elements++; #ifdef DEBUG if(is_anchor) list_data.num_anchors++; #endif } /***** * Name: parseHref * Return Type: void * Description: returns the url specification found in the given anchor. * In: * text: full anchor spec. * href: url found in given anchor. Filled upon return. * target: any target attribute found. Filled upon return. * extra: any additional attributes found. Filled upon return. * Returns: * nothing. *****/ static void parseHref(String text, XmHTMLAnchor *anchor) { if(text == NULL || (anchor->href = _XmHTMLTagGetValue(text, "href")) == NULL) { /* allocate empty href field so later strcmps won't explode */ anchor->href = (char *)malloc(1); anchor->href[0] = '\0'; /* fix 02/03/97-05, kdh */ /* * Could be a named anchor with a target spec. Rather impossible but * allow for it anyway (I can imagine this to be true for a * split-screen display). */ if(text == NULL) return; } /* Check if there is a target specification */ anchor->target= _XmHTMLTagGetValue(text, "target"); /* Also check for rel, rev and title */ anchor->rel = _XmHTMLTagGetValue(text, "rel"); anchor->rev = _XmHTMLTagGetValue(text, "rev"); anchor->title = _XmHTMLTagGetValue(text, "title"); } /***** * Name: FreeObjectTable * Return Type: void * Description: releases all memory occupied by the formatted list of elements. * In: * list: previous list to be released. * Returns: * nothing. * Note: * Images are freed in XmHTML.c, which calls XmHTMLFreeAllImages to do the * job. *****/ static void FreeObjectTable(XmHTMLObjectTable *list) { XmHTMLObjectTableElement temp; #ifdef DEBUG int i = 0, j = 0; #endif /* free all parsed objects */ while(list != NULL) { temp = list->next; if(list->text) /* space occupied by text to display */ free(list->text); /* free list of words. Can't be done above,
 doesn't have this! */
		if(list->n_words)
		{
			/* 
			* only the first word contains a valid ptr, all others point to
			* some char in this buffer, so freeing them *will* cause a
			* segmentation fault eventually.
			*/
			free(list->words[0].word);
			/* Free raw word data */
			free(list->words);
		}
		free(list);
		list = temp;
#ifdef DEBUG
		i++;
#endif
	}
	_XmHTMLDebug(2, ("format.c: FreeObjectTable End, freed %i elements and "
		"%i anchors.\n", i, j));
}

/*****
* Name:			freeTables
* Return Type: 	void
* Description: 	frees all data allocated for HTML table support.
* In: 
*	table:		list of tables to be freed.
* Returns:
*	nothing.
*****/
static void
freeTables(XmHTMLTable *table)
{
	XmHTMLTable *tab, *tmp = table;
	TableRow *row;
	int i, j, k;

	while(table)
	{
		tmp = table->next;

		/*****
		* Free all child tables (first table in the childs array is the
		* table itself)
		*****/
		for(i = 0; i < table->nchilds; i++)
		{
			tab = &table->childs[i];

			/* free all rows */
			for(j = 0; j < tab->nrows; j++)
			{
				row = &tab->rows[j];
				/* free all cells in this row */
				for(k = 0; k < row->ncells; k++)
				{
					free(row->cells[k].properties);
				}
				free(row->cells);
				free(row->properties);
			}
			free(tab->rows);
			free(tab->properties);
		}
		free(table->childs);
		free(table);
		table = tmp;
	}
}

/*****
* Name: 		FreeAnchors
* Return Type: 	void
* Description: 	frees the memory occupied by the anchor data
* In: 
*	anchors:	list of anchors to be freed
* Returns:
*	nothing.
*****/
static void
FreeAnchors(XmHTMLAnchor *anchors)
{
	XmHTMLAnchor *tmp;
	int i = 0;

	while(anchors)
	{
		tmp = anchors->next;
		/* href field is always allocated */
		free(anchors->href);
		if(anchors->target)
			free(anchors->target);
		if(anchors->rel)
			free(anchors->rel);
		if(anchors->rev)
			free(anchors->rev);
		if(anchors->title)
			free(anchors->title);
		if(anchors->name)		/* fix 07/09/97-01, kdh */
			free(anchors->name);
		if(anchors->events)
			free(anchors->events);
		free(anchors);
		anchors = NULL;
		anchors = tmp;
		i++;
	}
	_XmHTMLDebug(2, ("format.c: FreeAnchors, freed %i XmHTMLAnchor objects\n", 
		i));
}

/*****
* Name: 		InitObjectTable
* Return Type: 	void
* Description: 	initializes the list of formatted elements.
* In: 
*	list:		previous list to be released.
* Returns:
*	nothing
* Note:
*	The list head is a dummy element and is never used. It is done to gain
*	some performance (a test on an empty head is not required now in the
*	InsertTableElement routine).
*****/
static void
InitObjectTable(XmHTMLObjectTable *list, XmHTMLAnchor *anchors)
{
	if(list != NULL)
	{
		FreeObjectTable(list);
		list = NULL;
	}

	if(anchors != NULL)
	{
		FreeAnchors(anchors);
		anchors = NULL;
	}
	if(list_data.head)
		free(list_data.head);
	list_data.head = NewTableElement(NULL);
	list_data.current = list_data.head;
	list_data.anchor_head = (XmHTMLAnchor*)NULL;
	list_data.anchor_current = (XmHTMLAnchor*)NULL;
	list_data.num_elements = 1;
	list_data.num_anchors  = 0;
}

/*****
* Name: 		CollapseWhiteSpace
* Return Type: 	void
* Description: 	collapses whitespace in the given text
* In: 
*	text:		text for which multiple whitespace has to be collapsed.
* Returns:
*	nothing, but text is updated when this function returns.
*****/
static void
CollapseWhiteSpace(String text)
{
	register char *outPtr = text;

	/* 
	* We only collapse valid text and text that contains more than whitespace
	* only. This should never be true since CopyText will filter these
	* things out. It's just here for sanity.
	*/
	if(*text == '\0' || !strlen(text))
		return;

	_XmHTMLDebug(2, ("format.c: CollapseWhiteSpace, text in is:\n%s\n", text));

	/*
	* Now collapse each occurance of multiple whitespaces.
	* This may produce different results on different systems since
	* isspace() might not produce the same on each and every platform.
	*/
	while(True)
	{
		switch(*text)
		{
			case '\f':
			case '\n':
			case '\r':
			case '\t':
			case '\v':
				*text = ' ';	/* replace by a single space */
				/* fall through */
			case ' ':
				/* skip past first space */
				*(outPtr++) = *(text++);	
				/* collapse every space following */
				while(*text != '\0' && isspace(*text))
					*text++ = '\0';
				break;
			default:
				*(outPtr++) = *(text++);
				break;
		}
		if(*text == 0)
		{
			*outPtr = '\0';
			return;
		}
	}
}

/*****
* Name: 		TextToWords
* Return Type: 	XmHTMLWord*
* Description: 	splits the given text into an array of words.
* In: 
*	text:		text to split
*	num_words:	number of words in the given text. Filled upon return;
*	font:		font to use for this text.
* Returns:
*	an array of words. When allocation fails, this routine exits.
*****/
static XmHTMLWord* 
TextToWords(String text, int *num_words, Dimension *height, XmHTMLfont *font, 
	Byte line_data, Byte text_data, XmHTMLObjectTableElement owner)
{
	int n_words, len, i;
	char *start;
	static XmHTMLWord *words;
	static char *raw;
	register int j;
	register char *chPtr;

	/* sanity check */
	if(text == NULL)
	{
		*height = *num_words = 0;
		return(NULL);
	}

	_XmHTMLFullDebug(2, ("format.c: TextToWords, text in is:\n%s\n", text));

	/* compute how many words we have */
	n_words = 0;
	for(chPtr = text; *chPtr != '\0'; chPtr++)
		if(*chPtr == ' ')
			n_words++;
	/* also pick up the last word */
	n_words++;

	/* copy text */
	raw = strdup(text);

	/* allocate memory for all words */
	words = (XmHTMLWord*)calloc(n_words, sizeof(XmHTMLWord));

	/* Split the text in words and fill in the appropriate fields */
	*height = font->height;
	chPtr = start = raw;

	for(i = 0, j = 0, len = 0; ; chPtr++, len++, j++)
	{
		/* also pick up the last word! */
		if(*chPtr == ' ' || *chPtr == '\0')
		{
			if(*chPtr)
			{
				chPtr++;			/* nuke the space */
				raw[j++] = '\0';
			}
			/* fill in required fields */
			words[i].self      = &words[i];
			words[i].word      = start;
			words[i].len       = len;
			words[i].height    = *height;
			words[i].width     = Toolkit_Text_Width(font->xfont, words[i].word, len);
			words[i].owner     = owner;
			words[i].font      = font;
			words[i].spacing   = TEXT_SPACE_LEAD | TEXT_SPACE_TRAIL;
			words[i].type      = OBJ_TEXT;
			words[i].line_data = line_data;

			_XmHTMLFullDebug(2, ("format.c: TextToWords, word is %s, len is "
				"%i, width is %i, height is %i\n", words[i].word, words[i].len,
				words[i].width, words[i].height));

			start = chPtr;
			i++;
			len = 0;
		}
		if(*chPtr == '\0')
			break;
	}
	/* 
	* when there is more than one word in this block, the first word
	* _always_ has a trailing space.
	* Likewise, the last word always has a leading space.
	*/
	if(n_words > 1)
	{
		/* unset nospace bit */
		Byte spacing = text_data & ~TEXT_SPACE_NONE;
		words[0].spacing = spacing | TEXT_SPACE_TRAIL;
		words[n_words-1].spacing = spacing | TEXT_SPACE_LEAD;
	}
	else
		words[0].spacing = text_data;

	_XmHTMLFullDebug(2, ("format.c: TextToWords counted %i words\n", n_words));

	*num_words = i; /* n_words */;
	return(words);	
}

/*****
* Name: 		ImageToWord
* Return Type: 	XmHTMLWord*
* Description: 	converts an image to a word
* In: 
*	w:			XmHTMLWidget id
*	attributes:	raw  specification
*	height:		object height, updated upon return
*	owner:		owning object
*	formatted:	True when this image is part of a block of 
 text.
* Returns:
*	a word representing the image
*****/
static XmHTMLWord*
ImageToWord(XmHTMLWidget html, String attributes, int *num_words, 
	Dimension *height, XmHTMLObjectTableElement owner, Boolean formatted)
{
	static XmHTMLWord *word;
	static XmHTMLImage *image;
	Dimension width = 0;

	*num_words = 0;

	/* sanity check */
	if(attributes == NULL || 
		(image = _XmHTMLNewImage(html, attributes, &width, height)) == NULL)
	{
		*height = 0;
		return(NULL);
	}

	_XmHTMLFullDebug(2, ("format.c: ImageToWord, image in is: %s\n",
		image->url));

	word = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));

	/* required for image anchoring/replace/update */
	image->owner = owner;

	/* fill in required fields */
	word->self   = word;
	word->word   = strdup(image->alt);	/* we always have this */
	word->len    = strlen(image->alt);
	word->width  = width + 2*image->hspace + 2*image->border;
	word->height = *height + 2*image->vspace + 2*image->border;
	word->owner  = owner;
	word->font   = font_base.font;		/* always use the default font */
	/*****
	* if image support is disabled, add width of the alt text to the
	* image width (either from default image or specified in the doc).
	* This is required for proper exposure handling when images are disabled.
	*****/
	if(!html->html.images_enabled)
		word->width += Toolkit_Text_Width(word->font->xfont, word->word, word->len);

	/*****
	* No spacing if part of a chunk of 
 text
	* Fix 07/24/97, kdh
	*****/
	word->spacing = formatted ? 0 : TEXT_SPACE_LEAD | TEXT_SPACE_TRAIL;
	word->type = OBJ_IMG;
	word->line_data = NO_LINE;	/* no underlining for images */
	word->image = image;

	_XmHTMLFullDebug(2, ("format.c: TextToWords, word is %s, len is %i, "
		"width is %i, height is %i\n", word->word, word->len,
		word->width, word->height));

	*num_words = 1;
	return(word);
}

/*****
* Name:			allocFormWord
* Return Type: 	XmHTMLWord*
* Description: 	allocates a default XmHTMLWord for use within a HTML form.
* In: 
*	form:		form entry for which this word should be allocated;
*	*width:		object's width, updated upon return;
*	*height:	object's height, updated upon return;
*	owner:		owning object.
*	formatted:	true when allocating a form component present in 

* Returns:
*	a newly allocated word.
*****/
static XmHTMLWord*
allocFormWord(XmHTMLForm *form, Dimension *width, Dimension *height,
	XmHTMLObjectTableElement owner, Boolean formatted)
{
	static XmHTMLWord *word;

	/* allocate new entry */
	word = (XmHTMLWord*)calloc(1, sizeof(XmHTMLWord));

	/* fill in required fields */
	word->self    = word;
	word->word    = strdup(form->name); 	/* we always have this */
	word->len     = strlen(form->name);
	word->height  = *height = form->height;
	word->width   = *width  = form->width;
	word->owner   = owner;
	word->font    = font_base.font; 		/* always use default font */
	word->spacing = formatted ? 0 : TEXT_SPACE_LEAD | TEXT_SPACE_TRAIL;
	word->type    = OBJ_FORM;
	word->form    = form;

	return(word);
}

/*****
* Name: 		InputToWord
* Return Type: 	XmHTMLWord*
* Description: 	converts a HTML form  element to a word
* In: 
*	w:			XmHTMLWidget id
*	attributes:	raw form element specification
*	width:		object width, updated upon return
*	height:		object height, updated upon return
*	owner:		owning object
*	formatted:	true when this form component is placed in a 
 tag.
* Returns:
*	a word representing the image
*****/
static XmHTMLWord*
InputToWord(XmHTMLWidget html, String attributes, int *num_words, 
	Dimension *width, Dimension *height, XmHTMLObjectTableElement owner,
	Boolean formatted)
{
	static XmHTMLForm *form_entry;
	XmHTMLWord *word;

	*num_words = 0;

	/* sanity check */
	if(attributes == NULL ||
		(form_entry = _XmHTMLFormAddInput(html, attributes)) == NULL)
		return(NULL);

	/* save owner, we need it in the paint routines */
	form_entry->data = owner;

	/* image buttons are treated as anchored images */
	if(form_entry->type == FORM_IMAGE)
	{
		word = ImageToWord(html, attributes, num_words, height, owner,
				formatted);
		/* remove alt text */
		free(word->word);
		/* use form member name instead */
		word->word = strdup(form_entry->name);
		word->len  = strlen(form_entry->name);
		word->form = form_entry;

		_XmHTMLFullDebug(2, ("format.c: InputToWord, word is %s, len is %i, "
			"width is %i, height is %i (type = image)\n", word->word,
			word->len, word->width, word->height));

		return(word);
	}

	/* allocate new word for this form member */
	word = allocFormWord(form_entry, width, height, owner, formatted);

	_XmHTMLFullDebug(2, ("format.c: InputToWord, word is %s, len is %i, "
		"width is %i, height is %i\n", word->word, word->len,
		word->width, word->height));

	*num_words = 1;
	return(word);
}

/*****
* Name:			SelectToWord
* Return Type: 	XmHTMLWord*
* Description:	converts a HTML form  to a HTMLWord.
*				Also processes any  items within this select.
* In: 
*	html:		XmHTMLWidget id;
*	start:		object at which