Milán Major

netscape-revival

1 branch
Code

lib/layout/laytext.c

/* -*- Mode: C; tab-width: 4; tabs-indent-mode: t -*- */

#include "xp.h"
#include "pa_tags.h"
#include "layout.h"
#include "libi18n.h"
#include "edt.h"

#ifdef XP_MAC
	#ifdef XP_TRACE
		#undef XP_TRACE
	#endif
	#define XP_TRACE(X)
#else
#ifndef XP_TRACE
	#define XP_TRACE(X) fprintf X
#endif
#endif /* XP_MAC */

#ifdef TEST_16BIT
#define XP_WIN16
#endif /* TEST_16BIT */

#ifdef XP_WIN16
#define SIZE_LIMIT              32000
#endif /* XP_WIN16 */

#define TEXT_CHUNK_LIMIT	500


#define DEF_TAB_WIDTH		8
#define NON_BREAKING_SPACE	160

#ifdef PROFILE
#pragma profile on
#endif

static int32 lo_correct_text_element_width(LO_TextInfo *);


void
lo_SetDefaultFontAttr(lo_DocState *state, LO_TextAttr *tptr,
	MWContext *context)
{
	tptr->size = DEFAULT_BASE_FONT_SIZE;
	tptr->fontmask = 0;
	tptr->no_background = TRUE;
	tptr->attrmask = 0;
	tptr->fg.red =   STATE_DEFAULT_FG_RED(state);
	tptr->fg.green = STATE_DEFAULT_FG_GREEN(state);
	tptr->fg.blue =  STATE_DEFAULT_FG_BLUE(state);
	tptr->bg.red =   STATE_DEFAULT_BG_RED(state);
	tptr->bg.green = STATE_DEFAULT_BG_GREEN(state);
	tptr->bg.blue =  STATE_DEFAULT_BG_BLUE(state);
	tptr->charset = INTL_DefaultTextAttributeCharSetID(context);
	tptr->font_face = NULL;
	tptr->FE_Data = NULL;
}


/*************************************
 * Function: lo_DefaultFont
 *
 * Description: This function sets up the text attribute
 *      structure for the default text drawing font.
 *      This is the font that sits at the bottom of the font
 *	stack, and can never be popped off.
 *
 * Params: Document state
 *
 * Returns: A pointer to a lo_FontStack structure.
 *	Returns a NULL on error (such as out of memory);
 *************************************/
lo_FontStack *
lo_DefaultFont(lo_DocState *state, MWContext *context)
{
	LO_TextAttr tmp_attr;
	lo_FontStack *fptr;
	LO_TextAttr *tptr;

	/*
	 * Fill in default font information.
	 */
	lo_SetDefaultFontAttr(state, &tmp_attr, context);
	tptr = lo_FetchTextAttr(state, &tmp_attr);

	fptr = XP_NEW(lo_FontStack);
	if (fptr == NULL)
	{
		return(NULL);
	}
	fptr->tag_type = P_UNKNOWN;
	fptr->text_attr = tptr;
	fptr->next = NULL;

	return(fptr);
}


void
lo_PushAlignment(lo_DocState *state, PA_Tag *tag, int32 alignment)
{
	lo_AlignStack *aptr;

	aptr = XP_NEW(lo_AlignStack);
	if (aptr == NULL)
	{
		state->top_state->out_of_memory = TRUE;
		return;
	}

	aptr->type = tag->type;
	aptr->alignment = alignment;

	aptr->next = state->align_stack;
	state->align_stack = aptr;
}


lo_AlignStack *
lo_PopAlignment(lo_DocState *state)
{
	lo_AlignStack *aptr;

	aptr = state->align_stack;
	if (aptr != NULL)
	{
		state->align_stack = aptr->next;
		aptr->next = NULL;
	}
	return(aptr);
}


/*************************************
 * Function: lo_FreshText
 *
 * Description: This function clears out the information from the
 *	document state that refers to text in the midst of being
 *	laid out.  It is called to clear the layout state before
 *	Laying out new text potentially in a new font.
 *
 * Params: Document state structure.
 *
 * Returns: Nothing
 *************************************/
void
lo_FreshText(lo_DocState *state)
{
	state->text_info.max_width = 0;
	state->text_info.ascent = 0;
	state->text_info.descent = 0;
	state->text_info.lbearing = 0;
	state->text_info.rbearing = 0;
	state->line_buf_len = 0;
	state->break_pos = -1;
	state->break_width = 0;
	state->last_char_CR = FALSE;
#ifdef EDITOR
	if( !state->edit_force_offset )
	{
		state->edit_current_offset = 0;
	}
#endif
}


/*************************************
 * Function: lo_new_text_element
 *
 * Description: Create a new text element structure based
 *	on the current text information stored in the document
 *	state.
 *
 * Params: Document state
 *
 * Returns: A pointer to a LO_TextStruct structure.
 *	Returns a NULL on error (such as out of memory);
 *************************************/
static LO_TextStruct *
lo_new_text_element(MWContext *context, lo_DocState *state, ED_Element *edit_element,
		intn edit_offset )
{
	LO_TextStruct *text_ele = NULL;
#ifdef DEBUG
	assert (state);
#endif

	if (state == NULL)
	{
		return(NULL);
	}

	text_ele = (LO_TextStruct *)lo_NewElement(context, state, LO_TEXT, 
			edit_element, edit_offset);
	if (text_ele == NULL)
	{
#ifdef DEBUG
		assert (state->top_state->out_of_memory);
#endif
		return(NULL);
	}

	text_ele->type = LO_TEXT;
	text_ele->ele_id = NEXT_ELEMENT;
	text_ele->x = state->x;
	text_ele->x_offset = 0;
	text_ele->y = state->y;
	text_ele->y_offset = 0;
	text_ele->width = 0;
	text_ele->height = 0;
	text_ele->next = NULL;
	text_ele->prev = NULL;

	if (state->line_buf_len > 0)
	{
		text_ele->text = PA_ALLOC((state->line_buf_len + 1) *
			sizeof(char));
		if (text_ele->text != NULL)
		{
			char *text_buf;
			char *line;

			PA_LOCK(line, char *, state->line_buf);
			PA_LOCK(text_buf, char *, text_ele->text);
			XP_BCOPY(line, text_buf, state->line_buf_len);
			text_buf[state->line_buf_len] = '\0';
			PA_UNLOCK(text_ele->text);
			PA_UNLOCK(state->line_buf);
			text_ele->text_len = (int16)state->line_buf_len;
		}
		else
		{
			state->top_state->out_of_memory = TRUE;
			/*
			 * free text element && return; it's no different
			 * than if we had run out of memory allocating
			 * the text element
			 */
			lo_FreeElement(context, (LO_Element *)text_ele, FALSE);
			return(NULL);
		}
	}
	else
	{
		text_ele->text = NULL;
		text_ele->text_len = 0;
	}

	text_ele->anchor_href = state->current_anchor;

	if (state->font_stack == NULL)
	{
		text_ele->text_attr = NULL;
	}
	else
	{
		text_ele->text_attr = state->font_stack->text_attr;
	}

	text_ele->ele_attrmask = 0;
	if (state->breakable != FALSE)
	{
		text_ele->ele_attrmask |= LO_ELE_BREAKABLE;
	}

	text_ele->sel_start = -1;
	text_ele->sel_end = -1;

	return(text_ele);
}


/*
 * A bogus an probably too expensive function to fill in the
 * textinfo for whatever font is now on the font stack.
 */
static void
lo_fillin_text_info(MWContext *context, lo_DocState *state)
{
	LO_TextStruct tmp_text;
	PA_Block buff;
	char *str;

	memset (&tmp_text, 0, sizeof (tmp_text));
	buff = PA_ALLOC(1);
	if (buff == NULL)
	{
		return;
	}
	PA_LOCK(str, char *, buff);
	str[0] = ' ';
	PA_UNLOCK(buff);
	tmp_text.text = buff;
	tmp_text.text_len = 1;
	tmp_text.text_attr = state->font_stack->text_attr;
	FE_GetTextInfo(context, &tmp_text, &(state->text_info));
	PA_FREE(buff);
}


/*************************************
 * Function: lo_NewLinefeed
 *
 * Description: Create a new linefeed element structure based
 *	on the current information stored in the document
 *	state.
 *
 * Params: Document state
 *
 * Returns: A pointer to a LO_LinefeedStruct structure.
 *	Returns a NULL on error (such as out of memory);
 *************************************/
LO_LinefeedStruct *
lo_NewLinefeed(lo_DocState *state, MWContext * context)
{
	LO_LinefeedStruct *linefeed = NULL;

	if (state == NULL)
	{
		return(NULL);
	}

	linefeed = (LO_LinefeedStruct *)lo_NewElement(context, state, LO_LINEFEED, NULL, 0);
#ifdef DEBUG
	assert (state);
#endif
	if (linefeed == NULL)
	{
#ifdef DEBUG
		assert (state->top_state->out_of_memory);
#endif
		state->top_state->out_of_memory = TRUE;
		return(NULL);
	}

	linefeed->type = LO_LINEFEED;
	linefeed->ele_id = NEXT_ELEMENT;
	linefeed->x = state->x;
	linefeed->x_offset = 0;
	linefeed->y = state->y;
	linefeed->y_offset = 0;
	linefeed->width = state->right_margin - state->x;
	if (linefeed->width < 0)
	{
		linefeed->width = 0;
	}
	linefeed->height = state->line_height;
	/*
	 * if this linefeed has a zero height, make it the height
	 * of the current font.
	 */
	if (linefeed->height == 0)
	{
		linefeed->height = state->text_info.ascent +
			state->text_info.descent;
		if ((linefeed->height <= 0)&&(state->font_stack != NULL)&&
			(state->font_stack->text_attr != NULL))
		{
			lo_fillin_text_info(context, state);
			linefeed->height = state->text_info.ascent +
				state->text_info.descent;
		}
		/*
		 * This should never happen, but we have it
		 * covered just in case it does :-)
		 */
		if (linefeed->height <= 0)
		{
			linefeed->height = state->default_line_height;
		}
	}
	linefeed->line_height = linefeed->height;

	linefeed->FE_Data = NULL;
	linefeed->anchor_href = state->current_anchor;

	if (state->font_stack == NULL)
	{
		LO_TextAttr tmp_attr;
		LO_TextAttr *tptr;

		/*
		 * Fill in default font information.
		 */
		lo_SetDefaultFontAttr(state, &tmp_attr, context);
		tptr = lo_FetchTextAttr(state, &tmp_attr);
		linefeed->text_attr = tptr;
	}
	else
	{
		linefeed->text_attr = state->font_stack->text_attr;
	}

	linefeed->baseline = state->baseline;

	linefeed->ele_attrmask = 0;

	linefeed->sel_start = -1;
	linefeed->sel_end = -1;

	linefeed->next = NULL;
	linefeed->prev = NULL;
	linefeed->break_type = LO_LINEFEED_BREAK_SOFT;

	return(linefeed);
}

#if 0
   static void
lo_MarkHardBreak(MWContext *context, lo_DocState *state)
{
	LO_Element *pSearch, *pLinefeed;
	/*
	 * LTNOTE: some editor voodo here.  We just closed a paragraph or it was 
	 *  already closed.  Search backwards and mark the first linefeed as the
	 *  one that closed the 'end_paragraph'
	 *  linefeed.
     * (By "Paragraph" we mean any paragraph-like unit, including pargraphs,
     * headings, hard breaks, and list items.
	*/
	pSearch = state->end_last_line;
	pLinefeed = NULL;
	while( pSearch && pSearch->type == LO_LINEFEED )
	{
		pLinefeed = pSearch;
		pSearch = pSearch->lo_any.prev;
	}

	/* 
	 * if we found it.  Mark it as the end of a paragraph.
	*/
	if( pLinefeed )
	{
		pLinefeed->lo_linefeed.break_type = LO_LINEFEED_BREAK_PARAGRAPH;
	}
}
#endif

/*************************************
 * Function: lo_HardLineBreak
 *
 * Description: This function forces a linebreak at this time.
 *	Forcing the current text insetion point down one line
 *	and back to the left margin.
 *	It also maintains the linefeed state:
 *		0 - middle of a line.
 *		1 - at left margin below a line of text.
 *		2 - at left margin below a blank line.
 *
 * Params: Window context, Document state and a boolean
 *	That describes whether this is a breaking linefeed or not.
 *	(Breaking linefeed is one inserted just to break a formatted
 *	 line to the current document width.)
 *
 * Returns: Nothing
 *************************************/
void
lo_HardLineBreak(MWContext *context, lo_DocState *state, Bool breaking)
{
	int32 line_width;
	Bool scroll_at_bottom;

	scroll_at_bottom = FALSE;
	/*
	 * If this is an auto-scrolling doc, we will be scrolling
	 * later if we are at the bottom now.
	 */
	if ((state->is_a_subdoc == SUBDOC_NOT)&&
		(state->display_blocked == FALSE)&&
		(state->top_state->auto_scroll > 0))
	{
		int32 doc_x, doc_y;

		FE_GetDocPosition(context, FE_VIEW, &doc_x, &doc_y);
		if ((doc_y + state->win_height + state->win_bottom) >= state->y)
		{
			scroll_at_bottom = TRUE;
		}
	}

	/*
	 * This line is done, flush it into the line table and
	 * display it to the front end.
	 */
	lo_FlushLineList(context, state, breaking);
	if (state->top_state->out_of_memory != FALSE)
	{
		return;
	}

	/*
	 * if this linefeed has a zero height, make it the height
	 * of the current font.
	 */
	if (state->line_height == 0)
	{
		state->line_height = state->text_info.ascent +
			state->text_info.descent;
		if ((state->line_height <= 0)&&(state->font_stack != NULL)&&
			(state->font_stack->text_attr != NULL))
		{
			lo_fillin_text_info(context, state);
			state->line_height = state->text_info.ascent +
				state->text_info.descent;
		}
		/*
		 * This should never happen, but we have it
		 * covered just in case it does :-)
		 */
		if (state->line_height <= 0)
		{
			state->line_height = state->default_line_height;
		}
	}

	if (state->end_last_line != NULL)
	{
		line_width = state->end_last_line->lo_any.x + state->win_right;
	}
	else
	{
		line_width = state->x + state->win_right;
	}

	if (line_width > state->max_width)
	{
		state->max_width = line_width;
	}

	state->x = state->left_margin;
	state->y = state->y + state->line_height;
	state->width = 0;
	state->at_begin_line = TRUE;
	state->trailing_space = FALSE;
	state->line_height = 0;
	state->break_holder = state->x;

	state->linefeed_state++;
	if (state->linefeed_state > 2)
	{
		state->linefeed_state = 2;
	}

	if (state->is_a_subdoc == SUBDOC_NOT)
	{
		int32 percent;

		if (state->top_state->total_bytes < 1)
		{
			percent = -1;
		}
		else
		{
			percent = (100 * state->top_state->layout_bytes) /
				state->top_state->total_bytes;
			if (percent > 100)
			{
				percent = 100;
			}
		}
		if ((percent == 100)||(percent < 0)||
			(percent > (state->top_state->layout_percent + 1)))
		{
			if(!state->top_state->is_binary)
				FE_SetProgressBarPercent(context, percent);
			state->top_state->layout_percent = (intn)percent;
		}
	}

	/*
	 * Tell the front end how big the document is right now.
	 * Only do this for the top level document.
	 */
	if ((state->is_a_subdoc == SUBDOC_NOT)
        &&(state->display_blocked == FALSE)
#ifdef EDITOR
		&&(!state->edit_relayout_display_blocked)
#endif
        )
	{
		FE_SetDocDimension(context, FE_VIEW,
			state->max_width, state->y);
	}

	if ((state->display_blocked != FALSE)&&
#ifdef EDITOR
		(!state->edit_relayout_display_blocked)&&
#endif
	   (state->display_blocking_element_y > 0)&&
	   (state->y > (state->display_blocking_element_y + state->win_height)))
	{
		int32 y;

		state->display_blocked = FALSE;
		y = state->display_blocking_element_y;
		state->display_blocking_element_y = 0;
		FE_SetDocDimension(context, FE_VIEW,
			state->max_width, state->y);
		FE_SetDocPosition(context, FE_VIEW, 0, y);
		lo_RefreshDocumentArea(context, state, 0, y,
			state->win_width, state->win_height);
	}

	/*
	 * Reset the left and right margins
	 */
	lo_FindLineMargins(context, state);
	state->x = state->left_margin;

	if ((state->is_a_subdoc == SUBDOC_NOT)&&
	    (state->top_state->auto_scroll > 0)&&
	    ((state->line_num - 1) > state->top_state->auto_scroll))
	{
		Bool redraw;
		int32 old_y, dy, new_y;
		int32 doc_x, doc_y;

		old_y = state->y;
		FE_GetDocPosition(context, FE_VIEW, &doc_x, &doc_y);
		lo_ClipLines(context, state, 1);
		/*
		 * Calculate how much of the top was clipped
		 * off, and if any of that area was in the users
		 * window we need to redraw their window.
		 */
		redraw = FALSE;
		dy = old_y - state->y;
		if (dy >= doc_y)
		{
			redraw = TRUE;
		}
		new_y = doc_y - dy;
		if (new_y < 0)
		{
			new_y = 0;
		}

		FE_SetDocPosition(context, FE_VIEW, doc_x, new_y);
		FE_SetDocDimension(context, FE_VIEW,
			state->max_width, state->y);
		if (redraw != FALSE)
		{
			FE_ClearView(context, FE_VIEW );
			lo_RefreshDocumentArea(context, state, 0, new_y,
				state->win_width, state->win_height);
		}
	}

	if ((state->is_a_subdoc == SUBDOC_NOT)&&
		(state->display_blocked == FALSE)&&
		(state->top_state->auto_scroll > 0))
	{
		int32 doc_x, doc_y;

		FE_GetDocPosition(context, FE_VIEW, &doc_x, &doc_y);
		if (((doc_y + state->win_height) < state->y)&&
			(scroll_at_bottom != FALSE))
		{
			int32 y;

			y = state->y - state->win_height + state->win_bottom;
			if (y < 0)
			{
				y = 0;
			}
			else
			{
#if defined(XP_UNIX)
				FE_ScrollDocTo(context, FE_VIEW, 0, y);
#else
				FE_SetDocPosition(context, FE_VIEW, 0, y);
#endif /* XP_UNIX */
			}
		}
	}
#if 0
    if ( ! breaking ) {
        lo_MarkHardBreak(context, state);
    }
#endif
}


/*************************************
 * Function: lo_SoftLineBreak
 *
 * Description: This function compares the current linefeed
 *	state to the desired linefeed state, and if it is
 *	not already there, it forces linefeeds until we get there.
 *	Linefeed states:
 *		0 - middle of a line.
 *		1 - at left margin below a line of text.
 *		2 - at left margin below a blank line.
 *
 * Params: Window context, Document state, a boolean that
 *	describes if this is a breaking linefeed, and the desired
 *	linefeed state.
 *
 * Returns: Nothing
 *************************************/
void
lo_SoftLineBreak(MWContext *context, lo_DocState *state, Bool breaking, intn linefeed_state)
{
	/*
	 * Softlinefeeds are partially disabled if we are placing
	 * preformatted text.
	 */
	if (state->preformatted != PRE_TEXT_NO)
	{
		if ((breaking == FALSE)&&(state->linefeed_state < 1)&&
			(state->top_state->out_of_memory == FALSE))
		{
			lo_HardLineBreak(context, state, breaking);
		}
		return;
	}

	if (linefeed_state > 2)
	{
		linefeed_state = 2;
	}

	while ((state->linefeed_state < linefeed_state)&&
		(state->top_state->out_of_memory == FALSE))
	{
		lo_HardLineBreak(context, state, breaking);
	}
}


/*************************************
 * Function: lo_InsertWordBreak
 *
 * Description: This insert a word break into the laid out
 *	element chain for the current line.  A word break is
 *	basically an empty text element structure.  All
 *	word break elements should probably be removed
 *	from the layout chain once the line list for this
 *	line is flushed.
 *
 * Params: Window context and document state.
 *
 * Returns: Nothing
 *************************************/
void
lo_InsertWordBreak(MWContext *context, lo_DocState *state)
{
	LO_TextStruct *text_ele = NULL;
	char *text_buf;

	if (state == NULL)
	{
		return;
	}

	text_ele = (LO_TextStruct *)lo_NewElement(context, state, LO_TEXT, NULL, 0);
	if (text_ele == NULL)
	{
#ifdef DEBUG
		assert (state->top_state->out_of_memory);
#endif
		return;
	}

	text_ele->type = LO_TEXT;
	text_ele->ele_id = NEXT_ELEMENT;
	text_ele->x = state->x;
	text_ele->x_offset = 0;
	text_ele->y = state->y;
	text_ele->y_offset = 0;
	text_ele->width = 0;
	text_ele->height = 0;
	text_ele->next = NULL;
	text_ele->prev = NULL;

	text_ele->text = PA_ALLOC(sizeof(char));
	if (text_ele->text != NULL)
	{
		PA_LOCK(text_buf, char *, text_ele->text);
		text_buf[0] = '\0';
		PA_UNLOCK(text_ele->text);
	}
	else
	{
		state->top_state->out_of_memory = TRUE;
	}
	text_ele->text_len = 0;
	text_ele->anchor_href = state->current_anchor;

	if (state->font_stack == NULL)
	{
		text_ele->text_attr = NULL;
	}
	else
	{
		text_ele->text_attr = state->font_stack->text_attr;
	}

	text_ele->ele_attrmask = 0;
	if (state->breakable != FALSE)
	{
		text_ele->ele_attrmask |= LO_ELE_BREAKABLE;
	}

	text_ele->sel_start = -1;
	text_ele->sel_end = -1;

	lo_AppendToLineList(context, state, (LO_Element *)text_ele, 0);

	state->old_break = text_ele;
	state->old_break_pos = 0;
	state->old_break_width = 0;
}


#ifdef NOT_USED
/*************************************
 * Function: lo_calc_baseline_and_height
 *
 * Description: We have a disconnected tail half of a line list.
 *	It may have a baseline and line height the same or equal
 *	to what it used to be.  If less, find its new value,
 *	and return the difference between the old and the new.
 *	Also set the new baseline and line height into state.
 *
 * Params: Document state and the LO_Element list pointer.
 *
 * Returns: amount that all the y_offsets should be reduced by.
 *************************************/
static int32
lo_calc_baseline_and_height(MWContext *context, lo_DocState *state,
				LO_Element *eptr)
{
	int32 baseline, old_baseline;
	int32 correction;
	int32 new_height;
	LO_TextInfo text_info;

	baseline = 0;
	old_baseline = 0;
	correction = 0;
	new_height = 0;

	state->baseline = 0;
	state->line_height = 0;

	while (eptr != NULL)
	{
		switch (eptr->type)
		{
			case LO_TEXT:
				FE_GetTextInfo(context, (LO_TextStruct *)eptr,
					&text_info);
				if (text_info.ascent > baseline)
				{
					baseline = text_info.ascent;
				}
				if ((text_info.ascent + text_info.descent) >
					new_height)
				{
					new_height = text_info.ascent +
						text_info.descent;
				}
				old_baseline = eptr->lo_text.y_offset +
					text_info.ascent;
				break;
			case LO_FORM_ELE:
				if (eptr->lo_form.baseline > baseline)
				{
					baseline = eptr->lo_form.baseline;
				}
				if (eptr->lo_any.height > new_height)
				{
					new_height = eptr->lo_any.height;
				}
				old_baseline = eptr->lo_any.y_offset +
					eptr->lo_form.baseline;
				break;
			case LO_HRULE:
			case LO_BULLET:
			case LO_IMAGE:
				if (eptr->lo_any.height > new_height)
				{
					new_height = eptr->lo_any.height;
				}
				break;
			default:
				break;
		}
		eptr = eptr->lo_any.next;
	}

	if (baseline == 0)
	{
		baseline = new_height - 1;
	}

	state->line_height = (intn) new_height;
	state->baseline = (intn) baseline;
	if (old_baseline > baseline)
	{
		correction = old_baseline - baseline;
	}
	return(correction);
}
#endif /* NOT_USED */


static int32
lo_baseline_adjust(MWContext *context, lo_DocState *state, LO_Element *ele_list,
			int32 old_baseline, int32 old_line_height)
{
	LO_Element *eptr;
	int32 baseline;
	int32 new_height;
	int32 baseline_adjust;
	LO_TextInfo text_info;

	baseline = 0;
	new_height = 0;
	eptr = ele_list;
	while (eptr != NULL)
	{
		switch (eptr->type)
		{
			case LO_TEXT:
				FE_GetTextInfo(context, (LO_TextStruct *)eptr,
					&text_info);
				if (text_info.ascent > baseline)
				{
					baseline = text_info.ascent;
				}
				if ((text_info.ascent + text_info.descent) >
					new_height)
				{
					new_height = text_info.ascent +
						text_info.descent;
				}
				break;
			case LO_FORM_ELE:
				if (eptr->lo_form.baseline > baseline)
				{
					baseline = eptr->lo_form.baseline;
				}
				if (eptr->lo_any.height > new_height)
				{
					new_height = eptr->lo_any.height;
				}
				break;
			case LO_IMAGE:
				if ((old_baseline - eptr->lo_any.y_offset) >
					baseline)
				{
					baseline = old_baseline -
						eptr->lo_any.y_offset;
				}
				if ((eptr->lo_image.height +
					(2 * eptr->lo_image.border_width) +
					(2 * eptr->lo_image.border_vert_space))
					> new_height)
				{
					new_height = eptr->lo_image.height +
					 (2 * eptr->lo_image.border_width) +
					 (2 * eptr->lo_image.border_vert_space);
				}
				break;
			case LO_HRULE:
			case LO_BULLET:
				if ((old_baseline - eptr->lo_any.y_offset) >
					baseline)
				{
					baseline = old_baseline -
						eptr->lo_any.y_offset;
				}
				if (eptr->lo_any.height > new_height)
				{
					new_height = eptr->lo_any.height;
				}
				break;
			default:
				break;
		}
		eptr = eptr->lo_any.next;
	}
	baseline_adjust = old_baseline - baseline;
	return(baseline_adjust);
}


/*************************************
 * Function: lo_BreakOldElement
 *
 * Description: This function goes back to a previous
 *	element in the element chain, that is still on the
 *	same line, and breaks the line there, putting the
 *	remaining elements (if any) on the next line.
 *
 * Params: Window context and document state.
 *
 * Returns: Nothing
 *************************************/
void
lo_BreakOldElement(MWContext *context, lo_DocState *state)
{
	char *text_buf;
	char *break_ptr;
	char *word_ptr;
	char *new_buf;
	PA_Block new_block;
	intn word_len;
	int32 save_width;
	int32 old_baseline;
	int32 old_line_height;
	int32 adjust;
	LO_TextStruct *text_data;
	LO_TextStruct *new_text_data;
	LO_Element *tptr;
	int16 charset;
	int multi_byte;
#ifdef LOCAL_DEBUG
XP_TRACE(("lo_BreakOldElement, flush text.\n"));
#endif /* LOCAL_DEBUG */

	if (state == NULL)
	{
		return;
	}

	charset = state->font_stack->text_attr->charset;
	multi_byte = (INTL_CharSetType(charset) != SINGLEBYTE);

	/*
	 * Move to the element we will break
	 */
	text_data = state->old_break;

	/*
	 * If there is no text there to break
	 * it is an error.
	 */
	if ((text_data == NULL)||(text_data->text == NULL))
	{
		return;
	}

	/*
	 * Later operations will trash the width field.
	 * So save it now to restore later.
	 */
	save_width = state->width;

	PA_LOCK(text_buf, char *, text_data->text);
	/*
	 * If this buffer is just an empty string, then we are
	 * breaking at a previously inserted word break element.
	 * Knowing that, the breaking is easier, so it is special 
	 * cased here.
	 */
	if (*text_buf == '\0')
	{
		/*
		 * Back the state up to this element's
		 * location, break off the rest of the elements
		 * and save them for later.
		 * Flush this line, and insert a linebreak.
		 */
		state->x = text_data->x;
		state->y = text_data->y;
		tptr = text_data->next;
		text_data->next = NULL;
		PA_UNLOCK(text_data->text);
		state->width = text_data->width;
		state->x += state->width;
		lo_HardLineBreak(context, state, TRUE);

		/*
		 * The remaining elements go on the next line.
		 */
		state->line_list = tptr;
		if (tptr != NULL)
		{
			tptr->lo_any.prev = NULL;
		}

		/*
		 * If there are no elements to place on the next
		 * line, and we have new text buffered,
		 * remove any spaces at the start of that new
		 * text since new lines are not allowed to begin
		 * with whitespace.
		 */
		if ((tptr == NULL)&&(state->line_buf_len != 0))
		{
			char *cptr;
			int32 wlen;

			PA_LOCK(text_buf, char *, state->line_buf);
			cptr = text_buf;
			wlen = 0;
			while ((XP_IS_SPACE(*cptr))&&(*cptr != '\0'))
			{
				cptr++;
				wlen++;
			}
			if (wlen)
			{
				LO_TextStruct tmp_text;

				memset (&tmp_text, 0, sizeof (tmp_text));

				/*
				 * We removed space, move the string up
				 * and recalculate its current width.
				 */
				XP_BCOPY(cptr, text_buf,
					(state->line_buf_len - wlen + 1));
				state->line_buf_len -= (intn) wlen;
				PA_UNLOCK(state->line_buf);
				tmp_text.text = state->line_buf;
				tmp_text.text_len = (int16)state->line_buf_len;
				tmp_text.text_attr =
					state->font_stack->text_attr;
				FE_GetTextInfo(context, &tmp_text,
					&(state->text_info));

				/*
				 * Override the saved width since we did want
				 * to change the real width in this case.
				 */
				save_width = lo_correct_text_element_width(
					&(state->text_info));
			}
			else
			{
				PA_UNLOCK(state->line_buf);
			}
		}
	}
	/*
	 * We are breaking somewhere in the middle of an old
	 * text element, this will mean splitting it into 2 text
	 * elements, and putting a line break between them.
	 */
	else
	{
		LO_TextInfo text_info;
		int32 base_change;

		/*
		 * Locate our break location, and a pointer to
		 * the remaining word (with its preceeding space removed).
		 */
		break_ptr = (char *)(text_buf + state->old_break_pos);
		/*
		 * On multibyte, we often break on character which is 
		 * not space.
		 */ 
		if (multi_byte == FALSE || XP_IS_SPACE(*break_ptr))
		{
			*break_ptr = '\0';
			word_ptr = (char *)(break_ptr + 1);
		}
		else
		{
			word_ptr = (char *)break_ptr;
		}

		/*
		 * Copy the remaining word into its own block.
		 */
		word_len = XP_STRLEN(word_ptr);
		new_block = PA_ALLOC((word_len + 1));
		if (new_block == NULL)
		{
			state->top_state->out_of_memory = TRUE;
			return;
		}
		PA_LOCK(new_buf, char *, new_block);
		XP_BCOPY(word_ptr, new_buf, (word_len + 1));
		new_buf[word_len] = '\0';

		/*
		 * Back the state up to this element's
		 * location, break off the rest of the elements
		 * and save them for later.
		 * Flush this line, and insert a linebreak.
		 */
		state->x = text_data->x;
		state->y = text_data->y;
		tptr = text_data->next;
		text_data->next = NULL;
		if (word_ptr != break_ptr)
			text_data->text_len = text_data->text_len - word_len - 1;
		else
			text_data->text_len = text_data->text_len - word_len;
		FE_GetTextInfo(context, text_data, &text_info);
		state->width = lo_correct_text_element_width(&text_info);
		PA_UNLOCK(text_data->text);
		state->x += state->width;

		/*
		 * Make the split element know its new width.
		 */
		text_data->width = state->width;

		/*
		 * If the element that caused this break has a different
		 * baseline than the element we are breaking, we need to
		 * preserve that difference after the break.
		 */
		base_change = state->baseline -
			(text_data->y_offset + text_info.ascent);

		old_baseline = state->baseline;
		old_line_height = state->line_height;

		/*
		 * Reset element_id so they are still sequencial.
		 */
		state->top_state->element_id = text_data->ele_id + 1;

		/*
		 * If we are breaking an anchor, we need to make sure the
		 * linefeed gets its anchor href set properly.
		 */
		if (text_data->anchor_href != NULL)
		{
			LO_AnchorData *tmp_anchor;

			tmp_anchor = state->current_anchor;
			state->current_anchor = text_data->anchor_href;
			lo_HardLineBreak(context, state, TRUE);
			state->current_anchor = tmp_anchor;
		}
		else
		{
			lo_HardLineBreak(context, state, TRUE);
		}

		adjust = lo_baseline_adjust(context, state, tptr,
			old_baseline, old_line_height);
		state->baseline = old_baseline - adjust;
		state->line_height = (intn) old_line_height - adjust;

		/*
		 * If there was really no remaining word, free the
		 * unneeded buffer.
		 */
		if (word_len == 0)
		{
			LO_Element *eptr;

			PA_UNLOCK(new_block);
			PA_FREE(new_block);
			state->line_list = tptr;
			if (tptr != NULL)
			{
				tptr->lo_any.prev = NULL;
			}
			state->width = 0;

			eptr = tptr;
			while (eptr != NULL)
			{
				eptr->lo_any.ele_id = NEXT_ELEMENT;
				eptr->lo_any.y_offset -= adjust;
				eptr = eptr->lo_any.next;
			}
		}
		/*
		 * Else create a new text element for the remaining word.
		 * and stick it in the begining of the next line of
		 * text elements.
		 */
		else
		{
			LO_Element *eptr;
			int32 baseline_inc;
			LO_TextInfo text_info;
			baseline_inc = -1 * adjust;
			new_text_data = lo_new_text_element(context, state, 
					text_data->edit_element,
                    text_data->edit_offset+text_data->text_len+1 );
			if (new_text_data == NULL)
			{
				return;
			}
			if (new_text_data->text != NULL)
			{
				PA_FREE(new_text_data->text);
				new_text_data->text = NULL;
				new_text_data->text_len = 0;
			}
			new_text_data->anchor_href = text_data->anchor_href;
			new_text_data->text_attr = text_data->text_attr;
			new_text_data->x = state->x;
			new_text_data->y = state->y;
			
#ifdef LOCAL_DEBUG
XP_TRACE(("lo_BreakOldElement, left over word (%s)\n", new_buf));
#endif /* LOCAL_DEBUG */

			PA_UNLOCK(new_block);
			new_text_data->text = new_block;
			new_text_data->text_len = word_len;
			FE_GetTextInfo(context, new_text_data, &text_info);
			new_text_data->width =
				lo_correct_text_element_width(&text_info);

			/*
			 * Some fonts (particulatly italic ones with curly
			 * tails on letters like 'f') have a left bearing
			 * that extends back into the previous character.
			 * Since in this case the previous character is
			 * probably not in the same font, we move forward
			 * to avoid overlap.
			 */
			if (text_info.lbearing < 0)
			{
				new_text_data->x_offset =
					text_info.lbearing * -1;
			}

			/*
			 * The baseline of the text element just inserted in
			 * the line may be less than or greater than the
			 * baseline of the rest of the line due to font
			 * changes.  If the baseline is less, this is easy,
			 * we just increase y_offest to move the text down
			 * so the baselines line up.  For greater baselines,
			 * we can't move the text up to line up the baselines
			 * because we will overlay the previous line, so we
			 * have to move all rest of the elements in this line
			 * down.
			 *
			 * If the baseline is zero, we are the first element
			 * on the line, and we get to set the baseline.
			 */
			if (state->baseline == 0)
			{
				state->baseline = text_info.ascent;
			}
			else if (text_info.ascent < state->baseline)
			{
				new_text_data->y_offset = state->baseline -
						text_info.ascent;
			}
			else
			{
				baseline_inc = baseline_inc +
					(text_info.ascent - state->baseline);
				state->baseline =
					text_info.ascent;
			}

			/*
			 * Now that we have broken, and added the new
			 * element, we need to move it down to restore the
			 * baseline difference that previously existed.
			 */
			new_text_data->y_offset -= base_change;

			/*
			 * Calculate the height of this new
			 * text element.
			 */
			new_text_data->height = text_info.ascent +
				text_info.descent;
			state->x += new_text_data->width;

			/*
			 * Stick this new text element at the beginning
			 * of the remaining line elements
			 */
			state->line_list = (LO_Element *)new_text_data;
			new_text_data->prev = NULL;
			new_text_data->next = tptr;
			if (tptr != NULL)
			{
				tptr->lo_any.prev = (LO_Element *)new_text_data;
			}
			eptr = tptr;

			while (eptr != NULL)
			{
				eptr->lo_any.ele_id = NEXT_ELEMENT;
				eptr->lo_any.y_offset += baseline_inc;
				eptr = eptr->lo_any.next;
			}

			if ((new_text_data->y_offset + new_text_data->height) > 
				state->line_height)
			{
				state->line_height = (intn) new_text_data->y_offset +
					new_text_data->height;
			}

			state->at_begin_line = FALSE;
		}
	}

	/*
	 * If we are at the beginning of a line, and there is
	 * remaining text to place here, remove leading space
	 * which is not allowed at the start of lines.
	 * ERIC, make a test case for this, I suspect right now
	 * this code is never being executed and may contain an error.
	 */
	if ((state->at_begin_line != FALSE)&&(tptr != NULL)&&
		(tptr->type == LO_TEXT))
	{
		char *cptr;
		int32 wlen;
		LO_TextStruct *tmp_text;
		LO_TextInfo text_info;

		tmp_text = (LO_TextStruct *)tptr;

		PA_LOCK(text_buf, char *, tmp_text->text);
		cptr = text_buf;
		wlen = 0;
		while ((XP_IS_SPACE(*cptr))&&(*cptr != '\0'))
		{
			cptr++;
			wlen++;
		}
		if (wlen)
		{
			XP_BCOPY(cptr, text_buf,
				(tmp_text->text_len - wlen + 1));
			tmp_text->text_len -= (intn) wlen;

			PA_UNLOCK(tmp_text->text);
			FE_GetTextInfo(context, tmp_text, &text_info);
			tmp_text->width = lo_correct_text_element_width(
				&text_info);
		}
		else
		{
			PA_UNLOCK(tmp_text->text);
		}
	}

	/*
	 * Upgrade forward the x and y text positions in the document
	 * state.
	 */
	while (tptr != NULL)
	{
		tptr->lo_any.x = state->x;
		tptr->lo_any.y = state->y;
		state->x = state->x + tptr->lo_any.width;
		tptr = tptr->lo_any.next;
	}

	state->at_begin_line = FALSE;
	state->width = save_width;
}


/*************************************
 * Function: lo_correct_text_element_width
 *
 * Description: Calculate the correct width of this text element
 *	if it is a complete element surrounded by elements of potentially
 *	different fonts, so we have to take care not to truncate
 *	any slanted characters at either end of the element.
 *
 * Params: LO_TextInfo structure for this text element's text string.
 *
 * Returns: The width this element would have if it stood alone.
 *************************************/
static int32
lo_correct_text_element_width(LO_TextInfo *text_info)
{
	int32 x_offset;
	int32 width;

	width = text_info->max_width;
	x_offset = 0;

	/*
	 * For text that leans into the previous character.
	 */
	if (text_info->lbearing < 0)
	{
		x_offset = text_info->lbearing * -1;
		width += x_offset;
	}

	/*
	 * For text that leans right into the following characters.
	 */
	if (text_info->rbearing > text_info->max_width)
	{
		width += (text_info->rbearing - text_info->max_width);
	}

	return(width);
}


/*************************************
 * Function: lo_textinfo_copy
 *
 * Description: Simple structure copy, I'm certain there is a faster
 *	and more efficient way to do this.
 *
 * Params: src and dest LO_TextInfo pointers.
 *
 * Returns: Nothing
 *************************************/
#if 0
static void
lo_textinfo_copy(LO_TextInfo *src_tinfo, LO_TextInfo *dest_tinfo)
{
	dest_tinfo->max_width = src_tinfo->max_width;
	dest_tinfo->ascent = src_tinfo->ascent;
	dest_tinfo->descent = src_tinfo->descent;
	dest_tinfo->lbearing = src_tinfo->lbearing;
	dest_tinfo->rbearing = src_tinfo->rbearing;
}
#endif


int32
lo_characters_in_line(lo_DocState *state)
{
	int32 cnt;
	LO_Element *eptr;

	cnt = 0;
	eptr = state->line_list;
	while (eptr != NULL)
	{
		if (eptr->type == LO_TEXT)
		{
			cnt += eptr->lo_text.text_len;
		}
		eptr = eptr->lo_any.next;
	}
	return(cnt);
}


void
lo_PreformatedText(MWContext *context, lo_DocState *state, char *text)
{
	char *tptr;
	char *w_start;
	char *w_end;
	char *text_buf;
	char tchar1;
	Bool have_CR;
	Bool line_break;
	Bool white_space;
	LO_TextStruct text_data;
	char *tmp_buf;
	PA_Block tmp_block;
	int32 tab_count, ignore_cnt, line_length;
	int16 charset;
	int multi_byte;

	/*
	 * Initialize the structures to 0 (mark)
	 */
	memset (&text_data, 0, sizeof (LO_TextStruct));

	/*
	 * Error conditions
	 */
	if ((state == NULL)||(state->cur_ele_type != LO_TEXT)||(text == NULL))
	{
		return;
	}

	charset = state->font_stack->text_attr->charset;
	multi_byte = (INTL_CharSetType(charset) != SINGLEBYTE);

	/*
	 * Move through this text fragment, expand tabs, honor LF/CR.
	 */
	have_CR = state->last_char_CR;
	tptr = text;
	while ((*tptr != '\0')&&(state->top_state->out_of_memory == FALSE))
	{
		Bool has_nbsp;
		Bool in_word;
		Bool wrap_break;

		/*
		 * white_space is a tag to tell us if the current word
		 * ends in whitespace.
		 */
		white_space = FALSE;
		line_break = FALSE;
		has_nbsp = FALSE;

		/*
		 * Find the end of the line, counting tabs.
		 */
		tab_count = 0;
		ignore_cnt = 0;
		line_length = 0;
		w_start = tptr;

		/*
		 * If the last character processed was a CR, and the next
		 * char is a LF, ignore it.  Otherwise we know we
		 * can break the line on the first CR or LF found.
		 */
		if ((have_CR != FALSE)&&(*tptr == LF))
		{
			ignore_cnt++;
			tptr++;
		}
		have_CR = FALSE;

		in_word = FALSE;
		wrap_break = FALSE;
#ifdef XP_WIN16
		while ((*tptr != CR)&&(*tptr != LF)&&(*tptr != '\0')&&
#ifdef TEXT_CHUNK_LIMIT
		    ((line_length + (tab_count * DEF_TAB_WIDTH)) < TEXT_CHUNK_LIMIT)&&
#endif /* TEXT_CHUNK_LIMIT */
		    (((state->line_buf_len + line_length + (tab_count * DEF_TAB_WIDTH))) < SIZE_LIMIT))
#else
		while ((*tptr != CR)&&(*tptr != LF)&&(*tptr != '\0')
#ifdef TEXT_CHUNK_LIMIT
		    &&((line_length + (tab_count * DEF_TAB_WIDTH)) < TEXT_CHUNK_LIMIT)
#endif /* TEXT_CHUNK_LIMIT */
			)
#endif /* XP_WIN16 */
		{
			/*
			 * In the special wrapping preformatted text
			 * we need to chunk by word instead of by
			 * line.
			 */
			if (state->preformatted == PRE_TEXT_WRAP)
			{
				if ((in_word == FALSE)&&(!XP_IS_SPACE(*tptr)))
				{
					in_word = TRUE;
				}
				else if ((in_word != FALSE)&&
					(XP_IS_SPACE(*tptr)))
				{
					wrap_break = TRUE;
					break;
				}
			}

			if ((*tptr == FF)||(*tptr == VTAB))
			{
				/*
				 * Ignore the form feeds
				 * thrown in by some platforms.
				 * Ignore vertical tabs since we don't know
				 * what else to do with them.
				 */
				ignore_cnt++;
			}
			else if (*tptr == TAB)
			{
				tab_count++;
			}
			else if (!multi_byte && (unsigned char)*tptr == NON_BREAKING_SPACE)
			{
				/* *tptr = ' '; Replace this later */
				has_nbsp = TRUE;
				line_length++;
			}
			else
			{
				line_length++;
			}
			tptr++;
		}
		line_length = line_length + (DEF_TAB_WIDTH * tab_count);

#ifdef TEXT_CHUNK_LIMIT
		if ((state->line_buf_len + line_length) > TEXT_CHUNK_LIMIT)
		{
			lo_FlushLineBuffer(context, state);
			if (state->cur_ele_type != LO_TEXT)
			{
				lo_FreshText(state);
				state->cur_ele_type = LO_TEXT;
			}
		}
#endif /* TEXT_CHUNK_LIMIT */

#ifdef XP_WIN16
		if ((state->line_buf_len + line_length) >= SIZE_LIMIT)
		{
			line_break = TRUE;
		}
#endif /* XP_WIN16 */

		/*
		 * Terminate the word, saving the char we replaced
		 * with the terminator so it can be restored later.
		 */
		w_end = tptr;
		tchar1 = *w_end;
		*w_end = '\0';

		tmp_block = PA_ALLOC(line_length + 1);
		if (tmp_block == NULL)
		{
			*w_end = tchar1;
			state->top_state->out_of_memory = TRUE;
			break;
		}
		PA_LOCK(tmp_buf, char *, tmp_block);

		if ((tab_count)||(ignore_cnt))
		{
			char *cptr;
			char *text_ptr;
			int32 cnt;

			text_ptr = tmp_buf;
			cptr = w_start;
			cnt = lo_characters_in_line(state);
			cnt += state->line_buf_len;
			while (*cptr != '\0')
			{
				if ((*cptr == LF)||(*cptr == FF)||
					(*cptr == VTAB))
				{
					/*
					 * Ignore any linefeeds that must have
					 * been after CR, and form feeds.
					 * Ignore vertical tabs since we
					 * don't know what else to do with them.
					 */
					cptr++;
				}
				else if (*cptr == TAB)
				{
					int32 i, tab_pos;

					tab_pos = ((cnt / DEF_TAB_WIDTH) +
						1) * DEF_TAB_WIDTH;
					for (i=0; i<(tab_pos - cnt); i++)
					{
						*text_ptr++ = ' ';
					}
					cnt = tab_pos;
					cptr++;
				}
				else
				{
					*text_ptr++ = *cptr++;
					cnt++;
				}
			}
			*text_ptr = *cptr;
		}
		else
		{
			XP_BCOPY(w_start, tmp_buf, line_length + 1);
		}

		/*
		 * Now we catch those nasty non-breaking space special
		 * characters and make them spaces.
		 */
		if (has_nbsp != FALSE)
		{
			char *tmp_ptr;

			tmp_ptr = tmp_buf;
			while (*tmp_ptr != '\0')
			{
#ifndef XP_MAC
/*	Don't change NON_BREAKING_SPACE to SPACE in Mac See Bug # 6640 */

				if ((unsigned char)*tmp_ptr ==
					NON_BREAKING_SPACE)
				{
					*tmp_ptr = ' ';
				}
#endif
				tmp_ptr++;
			}
		}

		/* don't need this any more since we're converting
		 * elsewhere -- erik
		tmp_buf = FE_TranslateISOText(context, charset, tmp_buf);
		 */
		line_length = XP_STRLEN(tmp_buf);

		if ((line_length > 0)&&(XP_IS_SPACE(tmp_buf[line_length - 1])))
		{
			state->trailing_space = TRUE;
		}
#ifdef LOCAL_DEBUG
XP_TRACE(("Found Preformatted text (%s)\n", tmp_buf));
#endif /* LOCAL_DEBUG */
		PA_UNLOCK(tmp_block);

#if WHAT
		/*
		 * If this is an empty string, just throw it out
		 * and move on
		 */
		if (*w_start == '\0')
		{
			*w_end = tchar1;
#ifdef LOCAL_DEBUG
XP_TRACE(("Throwing out empty string!\n"));
#endif /* LOCAL_DEBUG */
			continue;
		}
#endif

		/*
		 * If we have extra text, Append it to the line buffer.
		 * It may be necessary to expand the line
		 * buffer.
		 */
		if (*w_start != '\0')
		{
			int32 old_len;
			Bool old_begin_line;

			old_len = state->line_buf_len;
			old_begin_line = state->at_begin_line;

			if ((state->line_buf_len + line_length + 1) >
				state->line_buf_size)
			{
				state->line_buf = PA_REALLOC(
					state->line_buf, (state->line_buf_size +
					line_length + LINE_BUF_INC));
				if (state->line_buf == NULL)
				{
					*w_end = tchar1;
					state->top_state->out_of_memory = TRUE;
					break;
				}
				state->line_buf_size += (line_length +
					LINE_BUF_INC);
			}
			PA_LOCK(text_buf, char *, state->line_buf);
			PA_LOCK(tmp_buf, char *, tmp_block);

			XP_BCOPY(tmp_buf,
			    (char *)(text_buf + state->line_buf_len),
			    (line_length + 1));
			state->line_buf_len += (intn) line_length;
			PA_UNLOCK(state->line_buf);
			PA_UNLOCK(tmp_block);

			/*
			 * Having added text, we cannot be at the start
			 * of a line
			 */
			state->cur_ele_type = LO_TEXT;
			state->at_begin_line = FALSE;

			/*
			 * Most common case is appending to the same line.
			 * assume that is what we are doing here.
			 */
			text_data.text = state->line_buf;
			text_data.text_len = (int16)state->line_buf_len;
			text_data.text_attr = state->font_stack->text_attr;
			FE_GetTextInfo(context, &text_data,
				&(state->text_info));
			state->width =
				lo_correct_text_element_width(
				&(state->text_info));

			/*
			 * If this is a special wrapping pre, and we would
			 * wrap here, break before this, and strip all
			 * following whitespace so there is none at
			 * the start of the next line.
			 * If we were at the beginning of the line before
			 * this, then obviously trying to wrap here will
			 * be pointless, and will in fact cause an
			 * infinite loop.
			 */
			if ((state->preformatted == PRE_TEXT_WRAP)&&
			    (old_begin_line == FALSE)&&
			    ((state->x + state->width) > state->right_margin))
			{
				PA_LOCK(text_buf, char *, state->line_buf);
				text_buf[old_len] = '\0';
				PA_UNLOCK(state->line_buf);
				state->line_buf_len = old_len;

				*w_end = tchar1;
				tptr = w_start;
				while ((XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
				{
					tptr++;
				}
				line_break = TRUE;
				w_start = tptr;
				w_end = tptr;
				tchar1 = *w_end;
			}
		}

		if (tchar1 == LF)
		{
			line_break = TRUE;
		}
		else if (tchar1 == CR)
		{
			line_break = TRUE;
			have_CR = TRUE;
		}

		/*
		 * If we are breaking the line here, flush the
		 * line_buf, and then insert a linebreak.
		 */
		if (line_break != FALSE)
		{
#ifdef LOCAL_DEBUG
XP_TRACE(("LineBreak, flush text.\n"));
#endif /* LOCAL_DEBUG */

			/*
			 * Flush the line and insert the linebreak.
			 */
			PA_LOCK(text_buf, char *, state->line_buf);
			text_data.text = state->line_buf;
			text_data.text_len = (int16)state->line_buf_len;
			text_data.text_attr = state->font_stack->text_attr;
			FE_GetTextInfo(context, &text_data,&(state->text_info));
			PA_UNLOCK(state->line_buf);
			state->width = lo_correct_text_element_width(
				&(state->text_info));
			lo_FlushLineBuffer(context, state);
#ifdef EDITOR
	/* LTNOTE: do something here like: */
			/*state->edit_current_offset += (word_ptr - text_buf);*/
#endif
			if (state->top_state->out_of_memory != FALSE)
			{
				PA_FREE(tmp_block);
				return;
			}
			/*
			 * Put on a linefeed element.
			 * This line is finished and will be added
			 * to the line array.
			 */
			lo_HardLineBreak(context, state, TRUE);

			state->line_buf_len = 0;
			state->width = 0;

			/*
			 * having just broken the line, we have no break
			 * position.
			 */
			state->break_pos = -1;
			state->break_width = 0;
		}

		*w_end = tchar1;
		if ((*tptr == CR)||(*tptr == LF))
		{
			tptr++;
		}
		PA_FREE(tmp_block);
	}

	/*
	 * Because we just might get passed text broken between the
	 * CR and the LF, we need to save this state.
	 */
	if ((tptr > text)&&(*(tptr - 1) == CR))
	{
		state->last_char_CR = TRUE;
	}
}




/*************************************
 * Function: lo_FormatText
 *
 * Description: This function formats text by breaking it into lines
 *	at word boundries.  Word boundries are whitespace, or special
 *	word break tags.
 *
 * Params: Window context and document state., and the text to be formatted.
 *
 * Returns: Nothing
 *************************************/
void
lo_FormatText(MWContext *context, lo_DocState *state, char *text)
{
	char *tptr;
	char *w_start;
	char *w_end;
	char *text_buf;
	char tchar1;
	int32 word_len;
	Bool line_break;
	Bool word_break;
	Bool prev_word_breakable;
	Bool white_space;
	int16 charset;
	Bool multi_byte;
	LO_TextStruct text_data;

	/*
	 * Initialize the structures to 0 (mark)
	 */
	memset (&text_data, 0, sizeof (LO_TextStruct));

	/*
	 * Error conditions
	 */
	if ((state == NULL)||(state->cur_ele_type != LO_TEXT)||(text == NULL))
	{
		return;
	}

	charset = state->font_stack->text_attr->charset;
	if ((INTL_CharSetType(charset) == SINGLEBYTE) ||
		(INTL_CharSetType(charset) & CS_SPACE))
	{
		multi_byte = FALSE;
	}
	else
	{
		multi_byte = TRUE;
	}

	/*
	 * Move through this text fragment, breaking it up into
	 * words, and then grouping the words into lines.
	 */
	tptr = text;
	prev_word_breakable = FALSE;
	while ((*tptr != '\0')&&(state->top_state->out_of_memory == FALSE))
	{
		Bool has_nbsp;
#ifdef TEXT_CHUNK_LIMIT
		int32 w_char_cnt;
#endif /* TEXT_CHUNK_LIMIT */
#ifdef XP_WIN16
		int32 ccnt;
#endif /* XP_WIN16 */
		Bool mb_sp;  /* Allow space between multibyte */

		/*
		 * white_space is a tag to tell us if the currenct word
		 * contains nothing but whitespace.
		 * word_break tells us if there was whitespace
		 * before this word so we know we can break it.
		 */
		white_space = FALSE;
		word_break = FALSE;
		line_break = FALSE;
		has_nbsp = FALSE;
		mb_sp = FALSE;

		if (multi_byte == FALSE)
		{
			/*
			 * Find the start of the word, skipping whitespace.
			 */
			w_start = tptr;
			while ((XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
			{
				tptr++;
			}

			/*
			 * if tptr has been moved at all, that means
			 * there was some whitespace to skip, which means
			 * we are allowed to put a linebreak before this
			 * word if we want to.
			 */
			if (tptr != w_start)
			{
				int32 new_break_holder;
				int32 min_width;

				w_start = tptr;
				word_break = TRUE;

				new_break_holder = state->x + state->width;
				min_width = new_break_holder - state->break_holder + 1;
				if (min_width > state->min_width)
				{
					state->min_width = min_width;
				}
				state->break_holder = new_break_holder;
			}

			/*
			 * Find the end of the word.
			 * Terminate the word, saving the char we replaced
			 * with the terminator so it can be restored later.
			 */
#ifdef TEXT_CHUNK_LIMIT
			w_char_cnt = 0;
#endif /* TEXT_CHUNK_LIMIT */
#ifdef XP_WIN16
			ccnt = state->line_buf_len;
			while ((!XP_IS_SPACE(*tptr))&&(*tptr != '\0')&&(ccnt < SIZE_LIMIT))
			{
				if ((unsigned char)*tptr == NON_BREAKING_SPACE)
				{
					has_nbsp = TRUE;
				}
				tptr++;
#ifdef TEXT_CHUNK_LIMIT
				w_char_cnt++;
#endif /* TEXT_CHUNK_LIMIT */
				ccnt++;
			}
			if (ccnt >= SIZE_LIMIT)
			{
				line_break = TRUE;
			}
#else
			while ((!XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
			{
				if ((unsigned char)*tptr == NON_BREAKING_SPACE)
				{
					/* *tptr = ' '; Replace this later */
					has_nbsp = TRUE;
				}
				tptr++;
#ifdef TEXT_CHUNK_LIMIT
				w_char_cnt++;
#endif /* TEXT_CHUNK_LIMIT */
			}
#endif /* XP_WIN16 */
		}
		else
		{
			/*
			 * Find the start of the word, skipping whitespace.
			 */
			w_start = tptr;
			while ((XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
			{
				tptr = INTL_NextChar(charset, tptr);
			}
			if (w_start != tptr)
				mb_sp = TRUE;

			/*
			 * if tptr has been moved at all, that means
			 * there was some whitespace to skip, which means
			 * we are allowed to put a linebreak before this
			 * word if we want to.
			 */
			/*
			 * If this char is a two-byte thing, we can break
			 * before it.
			 */
			if ((tptr != w_start)||((unsigned char)*tptr > 127))
			{
				int32 new_break_holder;
				int32 min_width;

				/* If it's multibyte character, it always be able to break */
				if (tptr == w_start)
					prev_word_breakable = TRUE;
				w_start = tptr;
				word_break = TRUE;

				new_break_holder = state->x + state->width;
				min_width = new_break_holder - state->break_holder + 1;
				if (min_width > state->min_width)
				{
					state->min_width = min_width;
				}
				state->break_holder = new_break_holder;
			}
			else if (prev_word_breakable)
			{
				int32 new_break_holder;
				int32 min_width;

				prev_word_breakable = FALSE;
				w_start = tptr;
				word_break = TRUE;

				new_break_holder = state->x + state->width;
				min_width = new_break_holder - state->break_holder + 1;
				if (min_width > state->min_width)
				{
					state->min_width = min_width;
				}
				state->break_holder = new_break_holder;
			}

			/*
			 * Find the end of the word.
			 * Terminate the word, saving the char we replaced
			 * with the terminator so it can be restored later.
			 */
#ifdef TEXT_CHUNK_LIMIT
			w_char_cnt = 0;
#endif /* TEXT_CHUNK_LIMIT */
#ifdef XP_WIN16
			ccnt = state->line_buf_len;
			while (((unsigned char)*tptr < 128)&&(!XP_IS_SPACE(*tptr))&&(*tptr != '\0')&&(ccnt < SIZE_LIMIT))
			{
				intn c_len;
				char *tptr2;

				tptr2 = INTL_NextChar(charset, tptr);
				c_len = (intn)(tptr2 - tptr);
				tptr = tptr2;
#ifdef TEXT_CHUNK_LIMIT
				w_char_cnt += c_len;
#endif /* TEXT_CHUNK_LIMIT */
				ccnt += c_len;
			}
			if (ccnt >= SIZE_LIMIT)
			{
				line_break = TRUE;
			}
#else
			while (((unsigned char)*tptr < 128)&&(!XP_IS_SPACE(*tptr))&&(*tptr != '\0'))
			{
				intn c_len;
				char *tptr2;

				tptr2 = INTL_NextChar(charset, tptr);
				c_len = (intn)(tptr2 - tptr);
				tptr = tptr2;
#ifdef TEXT_CHUNK_LIMIT
				w_char_cnt += c_len;
#endif /* TEXT_CHUNK_LIMIT */
			}
#endif /* XP_WIN16 */
		}  /* multi byte */

#ifdef TEXT_CHUNK_LIMIT
		if (w_char_cnt > TEXT_CHUNK_LIMIT)
		{
			tptr = (char *)(tptr - (w_char_cnt - TEXT_CHUNK_LIMIT));
			w_char_cnt = TEXT_CHUNK_LIMIT;
		}

		if ((state->line_buf_len + w_char_cnt) > TEXT_CHUNK_LIMIT)
		{
			lo_FlushLineBuffer(context, state);
			if (state->top_state->out_of_memory != FALSE)
			{
				return;
			}
			if (state->cur_ele_type != LO_TEXT)
			{
				lo_FreshText(state);
				state->cur_ele_type = LO_TEXT;
			}
		}
#endif /* TEXT_CHUNK_LIMIT */
		if (multi_byte != FALSE)
		{
			if ((w_start == tptr)&&((unsigned char)*tptr > 127))
			{
				tptr = INTL_NextChar(charset, tptr);
			}
		}
		w_end = tptr;
		tchar1 = *w_end;
		*w_end = '\0';

		/*
		 * If the "word" is just an empty string, this
		 * is just whitespace that we may wish to compress out.
		 */
		if (*w_start == '\0')
		{
			white_space = TRUE;
		}

		/*
		 * compress out whitespace if the last word added was also
		 * whitespace.
		 */
		if ((white_space != FALSE)&&(state->trailing_space != FALSE))
		{
			*w_end = tchar1;
#ifdef LOCAL_DEBUG
XP_TRACE(("Discarding(%s)\n", w_start));
#endif /* LOCAL_DEBUG */
			continue;
		}

		/*
		 * This places the preceeding space in front of
		 * separate words on a line.
		 * Unecessary if last item was trailng space.
		 *
		 * If there was a word break before this word, so we know it
		 * was supposed to be separate, and if we are not at the
		 * beginning of the line, and if the
		 * preceeding word is not already whitespace, then add
		 * a space before this word.
		 */
		if ((word_break != FALSE)&&
			(state->at_begin_line == FALSE)&&
			(state->trailing_space == FALSE))
		{
			/*
			 * Since word_break is true, we know
			 * we skipped some spaces previously
			 * so we know there is space to back up
			 * the word pointer inside the buffer.
			 */
		    if ((multi_byte == FALSE)||mb_sp)
		    {
				w_start--;
				*w_start = ' ';
		    }

			/*
			 * If we are formatting breakable text
			 * set break position to be just before this word.
			 * This is where we will break this line if the
			 * new word makes it too long.
			 */
			if (state->breakable != FALSE)
			{
				state->break_pos = state->line_buf_len;
				state->break_width = state->width;
			}
		}

#ifdef LOCAL_DEBUG
XP_TRACE(("Found Word (%s)\n", w_start));
#endif /* LOCAL_DEBUG */
		/*
		 * If this is an empty string, just throw it out
		 * and move on
		 */
		if (*w_start == '\0')
		{
			*w_end = tchar1;
#ifdef LOCAL_DEBUG
XP_TRACE(("Throwing out empty string!\n"));
#endif /* LOCAL_DEBUG */
			continue;
		}

		/*
		 * Now we catch those nasty non-breaking space special
		 * characters and make them spaces.  Yuck, so that 
		 * relayout in tables will still see the non-breaking
		 * spaces, we need to copy the buffer here.
		 */
		if (has_nbsp != FALSE)
		{
			char *tmp_ptr;
			char *to_ptr;
			char *tmp_buf;
			PA_Block tmp_block;

			tmp_block = PA_ALLOC(XP_STRLEN(w_start) + 1);
			if (tmp_block == NULL)
			{
				*w_end = tchar1;
				state->top_state->out_of_memory = TRUE;
				break;
			}
			PA_LOCK(tmp_buf, char *, tmp_block);

			tmp_ptr = w_start;
			to_ptr = tmp_buf;
			while (*tmp_ptr != '\0')
			{
				*to_ptr = *tmp_ptr;
#ifndef XP_MAC	
/*	Don't change NON_BREAKING_SPACE to SPACE in Mac See Bug # 6640 */
				if ((unsigned char)*to_ptr ==
					NON_BREAKING_SPACE)
				{
					*to_ptr = ' ';
				}
#endif
				to_ptr++;
				tmp_ptr++;
			}
			*w_end = tchar1;
			w_start = tmp_buf;
			w_end = to_ptr;
			*w_end = '\0';
		}

		/*
		 * Make this Front End specific text, and count
		 * the length of the word.
		 */
		/* don't need this any more since we're converting
		 * elsewhere -- erik
		w_start = FE_TranslateISOText(context, charset, w_start);
		 */
		word_len = XP_STRLEN(w_start);

		/*
		 * Append this word to the line buffer.
		 * It may be necessary to expand the line
		 * buffer.
		 */
		if ((state->line_buf_len + word_len + 1) >
			state->line_buf_size)
		{
			state->line_buf = PA_REALLOC( state->line_buf,
				(state->line_buf_size +
				word_len + LINE_BUF_INC));
			if (state->line_buf == NULL)
			{
				*w_end = tchar1;
				state->top_state->out_of_memory = TRUE;
				break;
			}
			state->line_buf_size += (word_len + LINE_BUF_INC);
		}
		PA_LOCK(text_buf, char *, state->line_buf);
		XP_BCOPY(w_start,
		    (char *)(text_buf + state->line_buf_len),
		    (word_len + 1));
		state->line_buf_len += word_len;
		PA_UNLOCK(state->line_buf);

		/*
		 * Having added a word, we cannot be at the start of a line
		 */
		state->cur_ele_type = LO_TEXT;
		state->at_begin_line = FALSE;

		/*
		 * Most common case is appending to the same line.
		 * assume that is what we are doing here.
		 */
		text_data.text = state->line_buf;
		text_data.text_len = (int16)state->line_buf_len;
		text_data.text_attr = state->font_stack->text_attr;
		FE_GetTextInfo(context, &text_data, &(state->text_info));
		state->width =
			lo_correct_text_element_width(&(state->text_info));

		/*
		 * Set line_break based on document window width
		 */
#ifdef XP_WIN16
		if (((state->x + state->width) > state->right_margin)||(line_break != FALSE))
#else
		if ((state->x + state->width) > state->right_margin)
#endif /* XP_WIN16 */
		{
			/*  
			 * INTL kinsoku line break, some of characters are not allowed to put 
			 * in the end of line or beginning of line
			 */
			if (multi_byte && (state->break_pos != -1))
			{
				int cur_wordtype, pre_wordtype, pre_break_pos;
				cur_wordtype = INTL_KinsokuClass(charset, (unsigned char *) w_start);

				PA_LOCK(text_buf, char *, state->line_buf);
				pre_break_pos = INTL_PrevCharIdx(charset, 
					(unsigned char *)text_buf, state->break_pos);
				pre_wordtype = INTL_KinsokuClass(charset, 
					(unsigned char *)(text_buf + pre_break_pos));

				if (pre_wordtype == PROHIBIT_END_OF_LINE ||
				    (cur_wordtype == PROHIBIT_BEGIN_OF_LINE && 
					 XP_IS_ALPHA(*(text_buf+pre_break_pos)) == FALSE))
					state->break_pos = pre_break_pos;

				PA_UNLOCK(state->line_buf);
			}
			line_break = TRUE;
		}
		else
		{
			line_break = FALSE;
		}

		/*
		 * We cannot break a line if we have no break positions.
		 * Usually happens with a single line of unbreakable text.
		 */
		if ((line_break != FALSE)&&(state->break_pos == -1))
		{
			/*
			 * It may be possible to break a previous
			 * text element on the same line.
			 */
			if (state->old_break_pos != -1)
			{
				lo_BreakOldElement(context, state);
				line_break = FALSE;
			}
#ifdef XP_WIN16
			else if (ccnt >= SIZE_LIMIT)
			{
				state->break_pos = state->line_buf_len - 1;
			}
			else
			{
				line_break = FALSE;
			}
#else
			else
			{
				line_break = FALSE;
			}
#endif /* XP_WIN16 */
		}

		/*
		 * If we are breaking the line here, flush the
		 * line_buf, and then insert a linebreak.
		 */
		if (line_break != FALSE)
		{
			char *break_ptr;
			char *word_ptr;
			char *new_buf;
			PA_Block new_block;
#ifdef LOCAL_DEBUG
XP_TRACE(("LineBreak, flush text.\n"));
#endif /* LOCAL_DEBUG */

			/*
			 * Find the breaking point, and the pointer
			 * to the remaining word without its leading
			 * space.
			 */
			PA_LOCK(text_buf, char *, state->line_buf);
			break_ptr = (char *)(text_buf + state->break_pos);
/*			word_ptr = (char *)(break_ptr + 1); */
			word_ptr = break_ptr;

		    if ((multi_byte == FALSE)||mb_sp)
		    {
				word_ptr++;
		    }

			/*
			 * Copy the remaining word into its
			 * own buffer.
			 */
			word_len = XP_STRLEN(word_ptr);
			new_block = PA_ALLOC((word_len + 1) *
				sizeof(char));
			if (new_block == NULL)
			{
				PA_UNLOCK(state->line_buf);
				*w_end = tchar1;
				state->top_state->out_of_memory = TRUE;
				break;
			}
			PA_LOCK(new_buf, char *, new_block);
			XP_BCOPY(word_ptr, new_buf, (word_len + 1));

			*break_ptr = '\0';
			state->line_buf_len = state->line_buf_len -
				word_len;
			if ((multi_byte == FALSE)||(word_ptr != break_ptr))
		    {
				state->line_buf_len--;
		    }
			text_data.text = state->line_buf;
			text_data.text_len = (int16)state->line_buf_len;
			text_data.text_attr = state->font_stack->text_attr;
			FE_GetTextInfo(context, &text_data,&(state->text_info));
			PA_UNLOCK(state->line_buf);
			state->width = lo_correct_text_element_width(
				&(state->text_info));
			lo_FlushLineBuffer(context, state);
#ifdef EDITOR
			state->edit_current_offset += (word_ptr - text_buf);
#endif
			if (state->top_state->out_of_memory != FALSE)
			{
				PA_UNLOCK(new_block);
				PA_FREE(new_block);
				return;
			}

			/*
			 * Put on a linefeed element.
			 * This line is finished and will be added
			 * to the line array.
			 */
			lo_HardLineBreak(context, state, TRUE);

			/*
			 * If there was no remaining word, free up
			 * the unnecessary buffer, and empty out
			 * the line buffer.
			 */
			if (word_len == 0)
			{
				PA_UNLOCK(new_block);
				PA_FREE(new_block);
				state->line_buf_len = 0;
				state->width = 0;
			}
			else
			{
				PA_LOCK(text_buf, char *,state->line_buf);
				XP_BCOPY(new_buf, text_buf, (word_len + 1));
				PA_UNLOCK(state->line_buf);
				PA_UNLOCK(new_block);
				PA_FREE(new_block);
				state->line_buf_len = word_len;
				text_data.text = state->line_buf;
				text_data.text_len = (int16)state->line_buf_len;
				text_data.text_attr =
					state->font_stack->text_attr;
				FE_GetTextInfo(context, &text_data,
					&(state->text_info));
				state->width = lo_correct_text_element_width(
					&(state->text_info));

				/*
				 * Having added text, we are no longer at the
				 * start of the line.
				 */
				state->at_begin_line = FALSE;
				state->cur_ele_type = LO_TEXT;
			}


			/*
			 * having just broken the line, we have no break
			 * position.
			 */
			state->break_pos = -1;
			state->break_width = 0;
		}
		else
		{
			if (white_space != FALSE)
			{
				state->trailing_space = TRUE;
			}
			else
			{
				state->trailing_space = FALSE;
			}
		}

		*w_end = tchar1;
	}
	/*  
	 * if last char is multibyte, break position need to be set to end of string
	 */
	if (multi_byte != FALSE && *tptr == '\0' && prev_word_breakable != FALSE)
		state->break_pos = state->line_buf_len;
}


/*************************************
 * Function: lo_FlushLineBuffer
 *
 * Description: Flush out the current line buffer of text
 *	into a new text element, and add that element to
 *	the end of the line list of elements.
 *
 * Params: Window context and document state.
 *
 * Returns: Nothing
 *************************************/
void
lo_FlushLineBuffer(MWContext *context, lo_DocState *state)
{
	LO_TextStruct *text_data;
	int32 baseline_inc;
	int32 line_inc;

	baseline_inc = 0;
	line_inc = 0;
#ifdef DEBUG
	assert (state);
#endif

	/* 
	 * LTNOTE: probably should be grabbing state edit_element and offset from
	 * state.
	*/
    text_data = lo_new_text_element(context, state, NULL, 0);

	if (text_data == NULL)
	{
		state->top_state->out_of_memory = TRUE;
		return;
	}
	state->linefeed_state = 0;

	/*
	 * Some fonts (particulatly italic ones with curly tails
	 * on letters like 'f') have a left bearing that extends
	 * back into the previous character.  Since in this case the
	 * previous character is probably not in the same font, we
	 * move forward to avoid overlap.
	 *
	 * Those same funny fonts can extend past the last character,
	 * and we also have to catch that, and advance the following text
	 * to eliminate cutoff.
	 */
	if (state->text_info.lbearing < 0)
	{
		text_data->x_offset = state->text_info.lbearing * -1;
	}
	text_data->width = state->width;

	/*
	 * The baseline of the text element just added to the line may be
	 * less than or greater than the baseline of the rest of the line
	 * due to font changes.  If the baseline is less, this is easy,
	 * we just increase y_offest to move the text down so the baselines
	 * line up.  For greater baselines, we can't move the text up to
	 * line up the baselines because we will overlay the previous line,
	 * so we have to move all the previous elements in this line down.
	 *
	 * If the baseline is zero, we are the first element on the line,
	 * and we get to set the baseline.
	 */
	if (state->baseline == 0)
	{
		state->baseline = state->text_info.ascent;
		if (state->line_height < 
			(state->baseline + state->text_info.descent))
		{
			state->line_height = state->baseline +
				state->text_info.descent;
		}
	}
	else if (state->text_info.ascent < state->baseline)
	{
		text_data->y_offset = state->baseline -
					state->text_info.ascent;
		if ((text_data->y_offset + state->text_info.ascent +
			state->text_info.descent) > state->line_height)
		{
			line_inc = text_data->y_offset +
				state->text_info.ascent +
				state->text_info.descent -
				state->line_height;
		}
	}
	else
	{
		baseline_inc = state->text_info.ascent - state->baseline;
		if ((text_data->y_offset + state->text_info.ascent +
			state->text_info.descent - baseline_inc) >
				state->line_height)
		{
			line_inc = text_data->y_offset +
				state->text_info.ascent +
				state->text_info.descent -
				state->line_height - baseline_inc;
		}
	}

	lo_AppendToLineList(context, state,
		(LO_Element *)text_data, baseline_inc);

	state->baseline += (intn) baseline_inc;
	state->line_height += (intn) (baseline_inc + line_inc);
	text_data->height = state->text_info.ascent +
		state->text_info.descent;

	/*
	 * If the element we just flushed had a breakable word
	 * position in it, save that position in case we have
	 * to go back and break this element before we finish
	 * the line.
	 */
	if (state->break_pos != -1)
	{
		state->old_break = text_data;
		state->old_break_pos = state->break_pos;
		state->old_break_width = state->break_width;
	}

	state->line_buf_len = 0;
	state->x += state->width;
	state->width = 0;
	state->cur_ele_type = LO_NONE;
}


void
lo_RecolorText(lo_DocState *state)
{
	lo_FontStack *fptr;
	LO_TextAttr *attr;

	fptr = state->font_stack;
	while (fptr != NULL)
	{
		attr = fptr->text_attr;
		attr->fg.red =   STATE_DEFAULT_FG_RED(state);
		attr->fg.green = STATE_DEFAULT_FG_GREEN(state);
		attr->fg.blue =  STATE_DEFAULT_FG_BLUE(state);
		attr->bg.red =   STATE_DEFAULT_BG_RED(state);
		attr->bg.green = STATE_DEFAULT_BG_GREEN(state);
		attr->bg.blue =  STATE_DEFAULT_BG_BLUE(state);
		fptr = fptr->next;
	}
}


/*************************************
 * Function: lo_PushFont
 *
 * Description: Push the text attribute information for a new
 *	font onto the font stack.  Also save the type of the
 *	tag that caused the change.
 *
 * Params: Document state, tag type, and the text attribute
 *	structure for the new font.
 *
 * Returns: Nothing
 *************************************/
void
lo_PushFont(lo_DocState *state, intn tag_type, LO_TextAttr *attr)
{
	lo_FontStack *fptr;

	fptr = XP_NEW(lo_FontStack);
	if (fptr == NULL)
	{
		return;
	}
	fptr->tag_type = tag_type;
	fptr->text_attr = attr;
	fptr->next = state->font_stack;
	state->font_stack = fptr;;
}


/*************************************
 * Function: lo_PopFontStack
 *
 * Description: This function pops the next font
 *	off the font stack, and return the text attribute of the
 *	previous font.
 *	The last font on the font stack cannot be popped off.
 *
 * Params: Document state, and the tag type that caused the change.
 *
 * Returns: The LO_TextAttr structure of the font just passed.
 *************************************/
LO_TextAttr *
lo_PopFontStack(lo_DocState *state, intn tag_type)
{
	LO_TextAttr *attr;
	lo_FontStack *fptr;

	if (state->font_stack->next == NULL)
	{
#ifdef LOCAL_DEBUG
XP_TRACE(("Popped too many fonts!\n"));
#endif /* LOCAL_DEBUG */
		return(NULL);
	}

	fptr = state->font_stack;
	attr = fptr->text_attr;
	if (fptr->tag_type != tag_type)
	{
#ifdef LOCAL_DEBUG
XP_TRACE(("Warning:  Font popped by different TAG than pushed it %d != %d\n", fptr->tag_type, tag_type));
#endif /* LOCAL_DEBUG */
	}
	state->font_stack = fptr->next;
	XP_DELETE(fptr);

	return(attr);
}


LO_TextAttr *
lo_PopFont(lo_DocState *state, intn tag_type)
{
	LO_TextAttr *attr;
	lo_FontStack *fptr;

	/*
	 * This should never happen, but we are patching a
	 * more serious problem that causes us to be called
	 * here after the font stack has been freed.
	 */
	if ((state->font_stack == NULL)||(state->font_stack->next == NULL))
	{
#ifdef LOCAL_DEBUG
XP_TRACE(("Popped too many fonts!\n"));
#endif /* LOCAL_DEBUG */
		return(NULL);
	}

	fptr = state->font_stack;
	attr = NULL;

	if (fptr->tag_type != P_ANCHOR)
	{
		attr = fptr->text_attr;
		if (fptr->tag_type != tag_type)
		{
#ifdef LOCAL_DEBUG
XP_TRACE(("Warning:  Font popped by different TAG than pushed it %d != %d\n", fptr->tag_type, tag_type));
#endif /* LOCAL_DEBUG */
		}
		state->font_stack = fptr->next;
		XP_DELETE(fptr);
	}
	else
	{
		while ((fptr->next != NULL)&&(fptr->next->tag_type == P_ANCHOR))
		{
			fptr = fptr->next;
		}
		if (fptr->next->next != NULL)
		{
			lo_FontStack *f_tmp;

			f_tmp = fptr->next;
			fptr->next = fptr->next->next;
			attr = f_tmp->text_attr;
			XP_DELETE(f_tmp);
		}
	}

	return(attr);
}


void
lo_PopAllAnchors(lo_DocState *state)
{
	lo_FontStack *fptr;

	if (state->font_stack->next == NULL)
	{
#ifdef LOCAL_DEBUG
XP_TRACE(("Popped too many fonts!\n"));
#endif /* LOCAL_DEBUG */
		return;
	}

	/*
	 * Remove all anchors on top of the font stack
	 */
	fptr = state->font_stack;
	while ((fptr->tag_type == P_ANCHOR)&&(fptr->next != NULL))
	{
		lo_FontStack *f_tmp;

		f_tmp = fptr;
		fptr = fptr->next;
		XP_DELETE(f_tmp);
	}
	state->font_stack = fptr;

	/*
	 * Remove all anchors buried in the stack
	 */
	while (fptr->next != NULL)
	{
		/*
		 * Reset spurrious anchor color text entries
		 */
		if ((fptr->text_attr != NULL)&&
			(fptr->text_attr->attrmask & LO_ATTR_ANCHOR))
		{
			LO_TextAttr tmp_attr;

			lo_CopyTextAttr(fptr->text_attr, &tmp_attr);
			tmp_attr.attrmask =
				tmp_attr.attrmask & (~LO_ATTR_ANCHOR);
			tmp_attr.fg.red = STATE_DEFAULT_FG_RED(state);
			tmp_attr.fg.green = STATE_DEFAULT_FG_GREEN(state);
			tmp_attr.fg.blue = STATE_DEFAULT_FG_BLUE(state);
			tmp_attr.bg.red = STATE_DEFAULT_BG_RED(state);
			tmp_attr.bg.green = STATE_DEFAULT_BG_GREEN(state);
			tmp_attr.bg.blue = STATE_DEFAULT_BG_BLUE(state);
			fptr->text_attr = lo_FetchTextAttr(state, &tmp_attr);
		}

		if (fptr->next->tag_type == P_ANCHOR)
		{
			lo_FontStack *f_tmp;

			f_tmp = fptr->next;
			fptr->next = fptr->next->next;
			XP_DELETE(f_tmp);
		}
		else
		{
			fptr = fptr->next;
		}
	}
}


void
lo_PlaceBullet(MWContext *context, lo_DocState *state)
{
	LO_BulletStruct *bullet = NULL;
	LO_TextAttr tmp_attr;
	LO_TextStruct tmp_text;
	LO_TextInfo text_info;
	LO_TextAttr *tptr;
	PA_Block buff;
	char *str;
	int32 bullet_size;

	bullet = (LO_BulletStruct *)lo_NewElement(context, state, LO_BULLET, NULL, 0);
	if (bullet == NULL)
	{
#ifdef DEBUG
		assert (state->top_state->out_of_memory);
#endif
		return;
	}

	lo_SetDefaultFontAttr(state, &tmp_attr, context);
	tptr = lo_FetchTextAttr(state, &tmp_attr);

	memset (&tmp_text, 0, sizeof (tmp_text));
	buff = PA_ALLOC(1);
	if (buff == NULL)
	{
		state->top_state->out_of_memory = TRUE;
		return;
	}
	PA_LOCK(str, char *, buff);
	str[0] = ' ';
	PA_UNLOCK(buff);
	tmp_text.text = buff;
	tmp_text.text_len = 1;
	tmp_text.text_attr = tptr;
	FE_GetTextInfo(context, &tmp_text, &text_info);
	PA_FREE(buff);

	bullet_size = (text_info.ascent + text_info.descent) / 2;

	bullet->type = LO_BULLET;
	bullet->ele_id = NEXT_ELEMENT;
	bullet->x = state->x - (2 * bullet_size);
	if (bullet->x < state->win_left)
	{
		bullet->x = state->win_left;
	}
	bullet->x_offset = 0;
	bullet->y = state->y;
	bullet->y_offset =
		(text_info.ascent + text_info.descent - bullet_size) / 2;
	bullet->width = bullet_size;
	bullet->height = bullet_size;
	bullet->next = NULL;
	bullet->prev = NULL;

	bullet->FE_Data = NULL;

	bullet->level = state->list_stack->level;
	bullet->bullet_type = state->list_stack->bullet_type;
	bullet->text_attr = tptr;

	bullet->ele_attrmask = 0;

	bullet->sel_start = -1;
	bullet->sel_end = -1;

	lo_AppendToLineList(context, state, (LO_Element *)bullet, 0);

	state->baseline = text_info.ascent;
	state->line_height = text_info.ascent + text_info.descent;

	/*
	 * Clean up state
	 */
/*
 * Supporting old mistakes made in some other browsers.
 * I will put the "correct code" here, but comment it out, since
 * some other browsers allowed headers inside lists, so we should to, sigh.
	state->linefeed_state = 0;
 */
	state->at_begin_line = TRUE;
	state->cur_ele_type = LO_BULLET;
	if (bullet->x == state->win_left)
	{
		state->x += (bullet->x_offset + (2 * bullet->width));
	}
}


void
lo_PlaceBulletStr(MWContext *context, lo_DocState *state)
{
	intn len;
	char str2[22];
	char *str;
	char *str3;
	PA_Block buff;
	LO_TextStruct *bullet_text = NULL;
	LO_TextInfo text_info;

	bullet_text = (LO_TextStruct *)lo_NewElement(context, state, LO_TEXT, NULL, 0);
	if (bullet_text == NULL)
	{
#ifdef DEBUG
		assert (state->top_state->out_of_memory);
#endif
		return;
	}

	if( EDT_IS_EDITOR( context )) 
	{
		switch( state->list_stack->bullet_type ){
		case BULLET_ALPHA_L:
			str = "A";
			break;
		case BULLET_ALPHA_S:
			str = "a";
			break;
		case BULLET_NUM_S_ROMAN:
			str = "x";
			break;
		case BULLET_NUM_L_ROMAN:
			str = "X";
			break;
		default:
			str = "#";
			break;
		}
		len = XP_STRLEN(str);
		buff = PA_ALLOC(len + 1);
		if (buff != NULL)
		{
			PA_LOCK(str3, char *, buff);
			XP_STRCPY(str3, str);
			PA_UNLOCK(buff);
		}
	}
	else {
		if (state->list_stack->bullet_type == BULLET_ALPHA_S)
		{
			buff = lo_ValueToAlpha(state->list_stack->value, FALSE, &len);
		}
		else if (state->list_stack->bullet_type == BULLET_ALPHA_L)
		{
			buff = lo_ValueToAlpha(state->list_stack->value, TRUE, &len);
		}
		else if (state->list_stack->bullet_type == BULLET_NUM_S_ROMAN)
		{
			buff = lo_ValueToRoman(state->list_stack->value, FALSE, &len);
		}
		else if (state->list_stack->bullet_type == BULLET_NUM_L_ROMAN)
		{
			buff = lo_ValueToRoman(state->list_stack->value, TRUE, &len);
		}
		else
		{
			XP_SPRINTF(str2, "%d.", (intn)state->list_stack->value);
			len = XP_STRLEN(str2);
			buff = PA_ALLOC(len + 1);
			if (buff != NULL)
			{
				PA_LOCK(str, char *, buff);
				XP_STRCPY(str, str2);
				PA_UNLOCK(buff);
			}
			else
			{
				state->top_state->out_of_memory = TRUE;
			}
		}
	}

	if (buff == NULL)
	{
		return;
	}

	bullet_text->text = buff;
	bullet_text->text_len = len;
	bullet_text->text_attr = state->font_stack->text_attr;
	FE_GetTextInfo(context, bullet_text, &text_info);
	bullet_text->width = lo_correct_text_element_width(&text_info);
	bullet_text->height = text_info.ascent + text_info.descent;

	bullet_text->type = LO_TEXT;
	bullet_text->ele_id = NEXT_ELEMENT;
	bullet_text->x = state->x - (bullet_text->height / 2) -
		bullet_text->width;
	if (bullet_text->x < state->win_left)
	{
		bullet_text->x = state->win_left;
	}
	bullet_text->x_offset = 0;
	bullet_text->y = state->y;
	bullet_text->y_offset = 0;

	bullet_text->anchor_href = state->current_anchor;

	bullet_text->ele_attrmask = 0;
	if (state->breakable != FALSE)
	{
		bullet_text->ele_attrmask |= LO_ELE_BREAKABLE;
	}

	bullet_text->sel_start = -1;
	bullet_text->sel_end = -1;

	bullet_text->next = NULL;
	bullet_text->prev = NULL;

	bullet_text->FE_Data = NULL;

	lo_AppendToLineList(context, state, (LO_Element *)bullet_text, 0);

	state->baseline = text_info.ascent;
	state->line_height = (intn) bullet_text->height;

	/*
	 * Clean up state
	 */
/*
 * Supporting old mistakes made in some other browsers.
 * I will put the "correct code" here, but comment it out, since
 * some other browsers allowed headers inside lists, so we should to, sigh.
	state->linefeed_state = 0;
	state->at_begin_line = FALSE;
 */
	state->at_begin_line = TRUE;
	state->cur_ele_type = LO_TEXT;
}


#ifdef TEST_16BIT
#undef XP_WIN16
#endif /* TEST_16BIT */

#ifdef PROFILE
#pragma profile off
#endif