/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#include "xp.h"
#include "pa_parse.h"
#include "layout.h"
#include "laylayer.h"
#include "np.h"
#include "laystyle.h"
#include "edt.h"

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


#define	TABLE_BORDERS_ON		1
#define	TABLE_BORDERS_OFF		0
#define	TABLE_BORDERS_GONE		-1

#define	TABLE_DEF_INNER_CELL_PAD	1
#define	TABLE_DEF_INTER_CELL_PAD	2
#define	TABLE_DEF_CELL_BORDER		1

#define	TABLE_DEF_BORDER		0
#define TABLE_DEF_BORDER_STYLE		BORDER_SOLID
#define	TABLE_DEF_VERTICAL_SPACE	0
#define	TABLE_DEF_HORIZONTAL_SPACE	0

#define	TABLE_MAX_COLSPAN		1000
#define	TABLE_MAX_ROWSPAN		10000

typedef struct lo_cell_data_struct {
#ifdef XP_WIN16
	/*
	 * This struct must be a power of 2 if we are going to allocate an
	 * array of more than 128K
	 */
	unsigned width, height;
#else
	int32 width, height;
#endif
	lo_TableCell *cell;
} lo_cell_data;


LO_SubDocStruct *
	lo_EndCellSubDoc(MWContext *context, lo_DocState *state, lo_DocState *old_state,
		LO_Element *element, Bool relayout);
LO_SubDocStruct *
	lo_RelayoutCaptionSubdoc(MWContext *context, lo_DocState *state, lo_TableCaption *caption,
		LO_SubDocStruct *subdoc, int32 width, Bool is_a_header);
LO_CellStruct *
lo_RelayoutCell(MWContext *context, lo_DocState *state,
	LO_SubDocStruct *subdoc, lo_TableCell *cell_ptr,
	LO_CellStruct *cell, int32 width, Bool is_a_header,
	Bool relayout);
void
	lo_BeginCaptionSubDoc(MWContext *context, lo_DocState *state,
		lo_TableCaption *caption, PA_Tag *tag);
void
	lo_BeginCellSubDoc(MWContext *context, 
 					lo_DocState *state,
 					lo_TableRec *table,
 					char *bgcolor_attr,
 					char *background_attr,
                    lo_TileMode tile_mode,
 					char *valign_attr,
 					char *halign_attr,
 					char *width_attr,
 					char *height_attr,
 					Bool is_a_header, 
 					intn draw_borders,
					Bool relayout);
Bool
	lo_subdoc_has_elements(lo_DocState *sub_state);
Bool
	lo_cell_has_elements(LO_CellStruct *cell);
int32
	lo_align_subdoc(MWContext *context, lo_DocState *state, lo_DocState *old_state,
		LO_SubDocStruct *subdoc, lo_TableRec *table, lo_table_span *row_max);
int32
	lo_align_cell(MWContext *context, lo_DocState *state, lo_TableCell *cell_ptr,
		LO_CellStruct *cell, lo_TableRec *table, lo_table_span *row_max);

void
	lo_RelayoutTags(MWContext *context, lo_DocState *state, PA_Tag * tag_ptr,
		PA_Tag * tag_end_ptr, LO_Element * elem_list);
	
PA_Tag *
	lo_FindReuseableElement(MWContext *context, lo_DocState *state,
		LO_Element ** elem_list);

static void
lo_reuse_current_state(MWContext *context, lo_DocState *state,
	int32 width, int32 height, int32 margin_width, int32 margin_height,
	Bool destroyLineLists);
/*
 ********************************************************************************
 * Some Helper Functions
 */
 
static int32 lo_CalculateCellPercentWidth( lo_TableRec *table, int32 percent);
static int32 lo_CalculateCellPercentHeight( lo_TableRec *table, lo_TableCell *table_cell);

static int32
	lo_ComputeCellEmptySpace(MWContext *context,lo_TableRec *table,
		LO_SubDocStruct *subdoc);
static int32
	lo_FindDefaultCellWidth(MWContext * context, lo_TableRec *table, lo_TableCell *table_cell,
		int32 desired_width, int32 cell_empty_space);
static int32
	lo_ComputeInternalTableWidth(MWContext *context, lo_TableRec *table,
		lo_DocState *state);
static void
	lo_SetDefaultCellWidth(MWContext *context, lo_TableRec *table, LO_SubDocStruct *subdoc,
		lo_TableCell *table_cell, int32 cell_width);


static void lo_ResetWidthSpans( lo_TableRec *table );

static void lo_UpdateCaptionCellFromSubDoc( MWContext *context, lo_DocState *state, LO_SubDocStruct *subdoc, LO_CellStruct *cell_ele);
static void lo_FreeCaptionCell( MWContext *context, lo_DocState *state, LO_CellStruct *cell_ele);
static void lo_FreeTableCaption( MWContext *context, lo_DocState *state, lo_TableRec *table );

static int32
lo_ComputeCellEmptySpace(MWContext *context, lo_TableRec *table,
	LO_SubDocStruct *subdoc)
{
	int32 left_inner_pad;
	int32 right_inner_pad;
	int32 cell_empty_space;
	
	left_inner_pad = FEUNITS_X(table->inner_left_pad, context);
	right_inner_pad = FEUNITS_X(table->inner_right_pad, context);
	
	cell_empty_space =	(2 * subdoc->border_width) +
						(2 * subdoc->border_horiz_space) +
						(left_inner_pad + right_inner_pad);
	
	return cell_empty_space;
}


/*
 * Handle Fixed Table Layout. If the table has a COLS attribute
 * then the width of this cell may already be specified. The
 * trick is to know if this is the first row of the table. If
 * so, then this cell can choose it's width.
 */
static int32
lo_FindDefaultCellWidth(MWContext * context, lo_TableRec *table, lo_TableCell *table_cell,
	int32 desired_width, int32 cell_empty_space)
{
	lo_TableRow * row_ptr;
	int32 cell_num;
	int32 cell_width;
	int32 count;
	int32 cell_pad;
		
	cell_pad = FEUNITS_X(table->inter_cell_pad, context);
	
	row_ptr = table->row_ptr;
	cell_num = row_ptr->cells;
	
	/* if we don't have a width table or we're out of bounds, bail */
	if ( table->fixed_col_widths == NULL ||
		((cell_num+table_cell->colspan) > table->fixed_cols) )
	{
		table->fixed_cols = 0;
		table->default_cell_width = 0;
		return desired_width;
	}
	
	/* does this cell already have a width defined? */
	cell_width = table->fixed_col_widths[ cell_num ];
	if ( cell_width == 0 )
	{
		int32 col_width;
		
		/* use it's desired width or the default */
		cell_width = desired_width;
		if ( cell_width == 0 )
		{
			/*
			 * Get our size from the default cell width. We
			 * may need to clip it to the table width remaining.
			 */
			cell_width = table->default_cell_width * table_cell->colspan;
			
			/*
			 * If this is bigger than the width remaining the the table, then
			 * clip to the remaining space. If the table is already full, then
			 * we're going for broke and the cell gets all of it.
			 */
			if ( ( table->fixed_width_remaining > 0 ) &&
				( cell_width > table->fixed_width_remaining ) )
			{
				cell_width = table->fixed_width_remaining;
			}
	
			table->fixed_width_remaining -= cell_width;
			
			/*
			 * Remove any cell spacing so the cell will internally
			 * layout to the correct size.
			 */
			cell_width -= cell_empty_space * table_cell->colspan;
		}
		else
		{
			/*
			 * The cell has a specified size. Allocate all of it. If we
			 * don't have enough space, then we'll keep doing fixed layout,
			 * but the table may end up larger than expected.
			 */
			table->fixed_width_remaining -= cell_width + cell_empty_space;
		}
		
		/* make sure this stays reasonable */
		if ( table->fixed_width_remaining < 0 )
		{
			table->fixed_width_remaining = 0;
		}
					
		/* fill out the apropriate number of col widths */
		col_width = cell_width / table_cell->colspan;
		for ( count = table_cell->colspan; count > 0 ; --count )
		{
			table->fixed_col_widths[ cell_num ] = col_width;
			cell_num++;
		}
		
		/* add any odd amount to the last column */
		table->fixed_col_widths[ cell_num-1 ] += (cell_width-
			(col_width * table_cell->colspan));
	}
	else
	{		
		/* add up widths of each column our cell spans */
		for ( count = 1; count < table_cell->colspan; ++count )
		{
			cell_width += table->fixed_col_widths[ cell_num+count ];
		}
	}
			
	/*
	 * Add the empty space this cell spans
	 */
	cell_width += ( table_cell->colspan - 1 ) * ( cell_empty_space + cell_pad );
	
	return cell_width;
}


/*
 * For fixed layout, set the cell width for a cell after layout. This is used
 * for cases where we don't know how wide a cell is until after it's been layed
 * out (for example, when NOWRAP is set).
 *
 * We will have already chosen a default width for this cell. If our new width
 * is larger, then we need to resize the column/s.
 */
static void
lo_SetDefaultCellWidth(MWContext *context, lo_TableRec *table, LO_SubDocStruct *subdoc,
	lo_TableCell *table_cell, int32 cell_width)
{
	lo_TableRow * row_ptr;
	int32 cell_num;
	int32 count;
	int32 cur_width;
	int32 add_width;
	int32 cell_empty_space;
	int32 cell_pad;
			
	row_ptr = table->row_ptr;
	cell_num = row_ptr->cells;
	
	/* if we don't have a width table or we're out of bounds, bail */
	if ( table->fixed_col_widths == NULL ||
		((cell_num+table_cell->colspan) > table->fixed_cols) )
	{
		table->fixed_cols = 0;
		table->default_cell_width = 0;
		return;
	}
	
	/*
	 * Figure out the current width for these cells
	 */
	cur_width = 0;
	
	for ( count = table_cell->colspan-1; count >= 0 ; --count )
	{
		cur_width += table->fixed_col_widths[ cell_num+count ];
	}
	
	/*
	 * Add space for the empty space that this cell spans
	 */
	cell_empty_space = lo_ComputeCellEmptySpace(context, table, subdoc );
	cell_pad = FEUNITS_X(table->inter_cell_pad, context);

	cur_width += ( table_cell->colspan - 1 ) * ( cell_empty_space + cell_pad );
	
	/*
	 * If our new width is bigger, we need to grow the default width
	 */
	if ( cell_width > cur_width )
	{
		cell_width -= cur_width;
		add_width = cell_width / table_cell->colspan;
		
		table->fixed_width_remaining -= add_width;
		if ( table->fixed_width_remaining < 0 )
		{
			table->fixed_width_remaining = 0;
		}
		
		for ( count = table_cell->colspan; count > 0 ; --count )
		{
			table->fixed_col_widths[ cell_num ] += add_width;
			cell_width -= add_width;
			cell_num++;
		}
		
		/* add any odd amount to the last column */
		table->fixed_col_widths[ cell_num-1 ] += cell_width;
	}
}

static int32
lo_ComputeInternalTableWidth(MWContext *context,lo_TableRec *table,
	lo_DocState *state)
{
	int32 table_width;
	int32 cell_pad;
	int32 table_pad;
	
	cell_pad = FEUNITS_X(table->inter_cell_pad, context);
	
	table_width = table->width;
	if ( table_width == 0 )
	{
		/* Compute our starting width as the window width */
		table_width = (state->win_width - state->win_left -
			state->win_right);
	}
	
	/* remove space for the table borders */
	table_width -= ( table->table_ele->border_left_width
		+ table->table_ele->border_right_width );

	if (table->draw_borders == TABLE_BORDERS_OFF)
	{
		table_pad = FEUNITS_X(TABLE_DEF_CELL_BORDER, context);
		table_width -= 2 * table_pad;
	}
	
	if ( table->fixed_cols > 0 )
	{
		table_width -= ( table->fixed_cols + 1 ) * cell_pad;
	}
	else
	{
		table_width -= ( table->cols + 1 ) * cell_pad;
	}

	return table_width;
}

/*
 ********************************************************************************
 * Core Table Layout Code
 */
 
void
lo_BeginCaptionSubDoc(MWContext *context, lo_DocState *state,
	lo_TableCaption *caption, PA_Tag *tag)
{
	LO_SubDocStruct *subdoc;
	lo_DocState *new_state;
	int32 first_width, first_height;
	Bool allow_percent_width;
	Bool allow_percent_height;

	/*
	 * Fill in the subdoc structure with default data
	 */
	subdoc = (LO_SubDocStruct *)lo_NewElement(context, state, LO_SUBDOC, NULL, 0);
	if (subdoc == NULL)
	{
		return;
	}

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

	subdoc->anchor_href = state->current_anchor;

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

	subdoc->FE_Data = NULL;
	subdoc->backdrop.bg_color = NULL;
    subdoc->backdrop.url = NULL;
    subdoc->backdrop.tile_mode = LO_TILE_BOTH;
	subdoc->state = NULL;

	subdoc->vert_alignment = LO_ALIGN_CENTER;
	subdoc->horiz_alignment = caption->horiz_alignment;

	subdoc->alignment = LO_ALIGN_BOTTOM;

	subdoc->border_width = 0;
	subdoc->border_vert_space = 0;
	subdoc->border_horiz_space = 0;

	subdoc->ele_attrmask = 0;

	subdoc->sel_start = -1;
	subdoc->sel_end = -1;

	subdoc->border_width = FEUNITS_X(subdoc->border_width, context);

	if (subdoc->width == 0)
	{
		first_width = FEUNITS_X(5000, context);
		allow_percent_width = FALSE;
	}
	else
	{
		first_width = subdoc->width;
		allow_percent_width = TRUE;
	}

	if (subdoc->height == 0)
	{
		first_height = FEUNITS_Y(10000, context);
		allow_percent_height = FALSE;
	}
	else
	{
		first_height = subdoc->height;
		allow_percent_height = TRUE;
	}

	new_state = lo_NewLayout(context, first_width, first_height, 0, 0, NULL);
	if (new_state == NULL)
	{
		lo_FreeElement(context, (LO_Element *)subdoc, FALSE);
		return;
	}
	new_state->is_a_subdoc = SUBDOC_CAPTION;
	lo_InheritParentState(context, new_state, state);
	new_state->allow_percent_width = allow_percent_width;
	new_state->allow_percent_height = allow_percent_height;

	new_state->base_x = 0;
	new_state->base_y = 0;
	new_state->display_blocked = TRUE;
	if (subdoc->width == 0)
	{
		new_state->delay_align = TRUE;
	}

	new_state->win_left = 0;
	new_state->win_right = 0;
	new_state->x = new_state->win_left;
	new_state->y = 0;
	new_state->max_width = new_state->x + 1;
	new_state->left_margin = new_state->win_left;
	new_state->right_margin = new_state->win_width - new_state->win_right;
	new_state->list_stack->old_left_margin = new_state->left_margin;
	new_state->list_stack->old_right_margin = new_state->right_margin;

	/*
	 * Initialize the alignment stack for this state
	 * to match the caption alignment.
	 */
	if (subdoc->horiz_alignment != LO_ALIGN_LEFT)
	{
		lo_PushAlignment(new_state, tag->type, subdoc->horiz_alignment);
	}

	state->sub_state = new_state;

	state->current_ele = (LO_Element *)subdoc;
	
	/*
	 * We added a new state.
	 */
	lo_PushStateLevel ( context );
}


static void
lo_reuse_current_state(MWContext *context, lo_DocState *state,
	int32 width, int32 height, int32 margin_width, int32 margin_height,
	Bool destroyLineLists)
{
	lo_TopState *top_state;
	int32 doc_id;
    lo_DocLists *doc_lists;

	doc_id = XP_DOCID(context);
	top_state = lo_FetchTopState(doc_id);
	if ((top_state == NULL)||(state == NULL))
	{
		return;
	}

	state->subdoc_tags = NULL;
	state->subdoc_tags_end = NULL;

	state->text_fg.red = lo_master_colors[LO_COLOR_FG].red;
	state->text_fg.green = lo_master_colors[LO_COLOR_FG].green;
	state->text_fg.blue = lo_master_colors[LO_COLOR_FG].blue;

	state->text_bg.red = lo_master_colors[LO_COLOR_BG].red;
	state->text_bg.green = lo_master_colors[LO_COLOR_BG].green;
	state->text_bg.blue = lo_master_colors[LO_COLOR_BG].blue;

	state->anchor_color.red = lo_master_colors[LO_COLOR_LINK].red;
	state->anchor_color.green = lo_master_colors[LO_COLOR_LINK].green;
	state->anchor_color.blue = lo_master_colors[LO_COLOR_LINK].blue;

	state->visited_anchor_color.red = lo_master_colors[LO_COLOR_VLINK].red;
	state->visited_anchor_color.green = lo_master_colors[LO_COLOR_VLINK].green;
	state->visited_anchor_color.blue = lo_master_colors[LO_COLOR_VLINK].blue;

	state->active_anchor_color.red = lo_master_colors[LO_COLOR_ALINK].red;
	state->active_anchor_color.green = lo_master_colors[LO_COLOR_ALINK].green;
	state->active_anchor_color.blue = lo_master_colors[LO_COLOR_ALINK].blue;

	state->win_top = margin_height;
	state->win_bottom = margin_height;
	state->win_width = width;
	state->win_height = height;

	state->base_x = 0;
	state->base_y = 0;
	state->x = 0;
	state->y = 0;
	state->width = 0;
	state->line_num = 1;

	state->win_left = margin_width;
	state->win_right = margin_width;

	state->max_width = state->win_left + state->win_right;

	state->x = state->win_left;
	state->y = state->win_top;

	state->left_margin = state->win_left;
	state->right_margin = state->win_width - state->win_right;
	if (state->left_margin_stack != NULL)
	{
		lo_MarginStack *mptr;
		lo_MarginStack *margin;

		mptr = state->left_margin_stack;
		while (mptr != NULL)
		{
			margin = mptr;
			mptr = mptr->next;
			XP_DELETE(margin);
		}
		state->left_margin_stack = NULL;
	}
	if (state->right_margin_stack != NULL)
	{
		lo_MarginStack *mptr;
		lo_MarginStack *margin;

		mptr = state->right_margin_stack;
		while (mptr != NULL)
		{
			margin = mptr;
			mptr = mptr->next;
			XP_DELETE(margin);
		}
		state->right_margin_stack = NULL;
	}

	state->break_holder = state->x;
	state->min_width = 0;
	state->text_divert = P_UNKNOWN;
	state->linefeed_state = 2;
	state->delay_align = FALSE;
	state->breakable = TRUE;
	state->allow_amp_escapes = TRUE;
	state->preformatted = PRE_TEXT_NO;
	state->preformat_cols = 0;

	state->in_paragraph = FALSE;

	state->display_blocked = FALSE;
	state->display_blocking_element_id = 0;
	state->display_blocking_element_y = 0;

	if (destroyLineLists == TRUE)
	{
		if (state->line_list != NULL)
		{
			lo_FreeElementList(context, state->line_list);
			state->line_list = NULL;
		}
		state->end_last_line = NULL;
		if (state->float_list != NULL)
		{	
			lo_FreeElementList(context, state->float_list);
			state->float_list = NULL;
		}
	}

	state->base_font_size = DEFAULT_BASE_FONT_SIZE;
	if (state->font_stack != NULL)
	{
		lo_FontStack *fstack;
		lo_FontStack *fptr;

		fptr = state->font_stack;
		while (fptr != NULL)
		{
			fstack = fptr;
			fptr = fptr->next;
			XP_DELETE(fstack);
		}
		state->font_stack = NULL;
	}
	state->font_stack = lo_DefaultFont(state, context);

	state->font_stack->text_attr->size = state->base_font_size;

	if (state->align_stack != NULL)
	{
		lo_AlignStack *aptr;
		lo_AlignStack *align;

		aptr = state->align_stack;
		while (aptr != NULL)
		{
			align = aptr;
			aptr = aptr->next;
			XP_DELETE(align);
		}
		state->align_stack = NULL;
	}
	if (state->list_stack != NULL)
	{
		lo_ListStack *lptr;
		lo_ListStack *list;

		lptr = state->list_stack;
		while (lptr != NULL)
		{
			list = lptr;
			lptr = lptr->next;
			XP_DELETE(list);
		}
		state->list_stack = NULL;
	}
	state->list_stack = lo_DefaultList(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->baseline = 0;
	state->line_height = 0;
	state->break_pos = -1;
	state->break_width = 0;
	state->last_char_CR = FALSE;
	state->trailing_space = FALSE;
	state->at_begin_line = TRUE;

	state->old_break = NULL;
	state->old_break_block = NULL;
	state->old_break_pos = -1;
	state->old_break_width = 0;

	state->current_named_anchor = NULL;
	state->current_anchor = NULL;

	state->cur_ele_type = LO_NONE;
	state->current_ele = NULL;
	state->current_java = NULL;

	state->current_table = NULL;
	state->current_grid = NULL;
	state->current_multicol = NULL;

	if (state->top_state->doc_state)
		doc_lists = lo_GetCurrentDocLists(state->top_state->doc_state);
	else
		doc_lists = lo_GetCurrentDocLists(state);
	
	/*
	 * These values are saved into the (sub)doc here
	 * so that if it gets Relayout() later, we know where
	 * to reset the from counts to.
	 */
	if (doc_lists->form_list != NULL)
	{
		lo_FormData *form_list;

		form_list = doc_lists->form_list;
		state->form_ele_cnt = form_list->form_ele_cnt;
		state->form_id = form_list->id;
	}
	else
	{
		state->form_ele_cnt = 0;
		state->form_id = 0;
	}
	state->start_in_form = top_state->in_form;
	if (top_state->savedData.FormList != NULL)
	{
		lo_SavedFormListData *all_form_ele;

		all_form_ele = top_state->savedData.FormList;
		state->form_data_index = all_form_ele->data_index;
	}
	else
	{
		state->form_data_index = 0;
	}

	/*
	 * Remember number of embeds we had before beginning this
	 * (sub)doc. This information is used in laytable.c so
	 * it can preserve embed indices across a relayout.
	 */
	state->embed_count_base = top_state->embed_count;

	/*
	 * Ditto for anchors, images, applets and embeds
	 */
	state->url_count_base = doc_lists->url_list_len;
	state->image_list_count_base = doc_lists->image_list_count;
        state->applet_list_count_base = doc_lists->applet_list_count;
        state->embed_list_count_base = doc_lists->embed_list_count;
	state->current_layer_num_base = top_state->current_layer_num;
	state->current_layer_num_max = top_state->current_layer_num;

	state->must_relayout_subdoc = FALSE;
	state->allow_percent_width = TRUE;
	state->allow_percent_height = TRUE;
	state->is_a_subdoc = SUBDOC_NOT;
	state->current_subdoc = 0;
	state->sub_documents = NULL;
	state->sub_state = NULL;

	state->current_cell = NULL;

	state->extending_start = FALSE;
	state->selection_start = NULL;
	state->selection_start_pos = 0;
	state->selection_end = NULL;
	state->selection_end_pos = 0;
	state->selection_new = NULL;
	state->selection_new_pos = 0;

#ifdef EDITOR
	state->edit_force_offset = FALSE;
	state->edit_current_element = 0;
	state->edit_current_offset = 0;
	state->edit_relayout_display_blocked = FALSE;
#endif
#ifdef MOCHA
	state->in_relayout = FALSE;
#endif

	state->cur_text_block = NULL;
	
	/* we need min widths during the initial table layout pass */
	state->need_min_width = TRUE;
}


static void
lo_reuse_current_subdoc(MWContext *context, LO_SubDocStruct *subdoc)
{
	if (subdoc == NULL)
	{
		return;
	}

	subdoc->type = LO_SUBDOC;
	subdoc->ele_id = 0;
	subdoc->x = 0;
	subdoc->x_offset = 0;
	subdoc->y = 0;
	subdoc->y_offset = 0;
	subdoc->width = 0;
	subdoc->height = 0;
	subdoc->next = NULL;
	subdoc->prev = NULL;

	subdoc->FE_Data = NULL;

	/*
	 * We leave subdoc->state alone as it will get reused later.
	 */

    XP_FREEIF(subdoc->backdrop.bg_color);
    XP_FREEIF(subdoc->backdrop.url);

	/*
	 * These two come from lists of shared pointers, so
	 * we NULL them but don't free them.
	 */
	subdoc->anchor_href = NULL;
	subdoc->text_attr = NULL;

	subdoc->alignment = LO_ALIGN_LEFT;
	subdoc->vert_alignment = LO_ALIGN_TOP;
	subdoc->horiz_alignment = LO_ALIGN_LEFT;
	subdoc->border_width = 0;
	subdoc->border_vert_space = 0;
	subdoc->border_horiz_space = 0;
	subdoc->ele_attrmask = 0;
	subdoc->sel_start = -1;
	subdoc->sel_end = -1;
}


static void
lo_cleanup_old_state(lo_DocState *state)
{
	LO_Element **line_array;

	if (state == NULL)
	{
		return;
	}

	/*
	 * Clear out the start of the line_list.
	 */
#ifdef XP_WIN16
	{
		XP_Block *larray_array;

		XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array);
		state->line_array = larray_array[0];
		XP_UNLOCK_BLOCK(state->larray_array);
	}
#endif /* XP_WIN16 */
	XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
	line_array[0] = NULL;
	XP_UNLOCK_BLOCK(state->line_array);

	state->end_last_line = NULL;

	state->float_list = NULL;
}


static int32 lo_CalculateCellPercentWidth( lo_TableRec *table, int32 percent)
{
	int32 val = percent;

	if ( table->table_width_fixed != FALSE )
	{
		val = table->width * val / 100;
	}
	else
	{
		val = 0;
	}
	return val;
}

static int32 lo_CalculateCellPercentHeight( lo_TableRec *table, lo_TableCell *table_cell)
{
	int32 val;

	if ((table->height > 0)&&
		(table_cell->percent_height > 0))
	{
		val = table->height *
			table_cell->percent_height / 100;
	}
	else
	{
		val = 0;
	}
	return val;
}

void
lo_BeginCellSubDoc(MWContext *context, 
 					lo_DocState *state,
 					lo_TableRec *table,
 					char *bgcolor_attr,
 					char *background_attr,
                    lo_TileMode tile_mode,
 					char *valign_attr,
 					char *halign_attr,
 					char *width_attr,
 					char *height_attr,
 					Bool is_a_header, 
 					intn draw_borders,
					Bool relayout /* Is true during relayout without reload */)
{
	LO_SubDocStruct *subdoc;
	lo_DocState *new_state;
	lo_TableRow *table_row;
	lo_TableCell *table_cell;
	int32 val;
	int32 first_width, first_height;
	Bool allow_percent_width;
	Bool allow_percent_height;
	StyleStruct *style_struct=0;
#ifdef LOCAL_DEBUG
 fprintf(stderr, "lo_BeginCellSubDoc called\n");
#endif /* LOCAL_DEBUG */

	table_row = table->row_ptr;
	table_cell = table_row->cell_ptr;

	if (relayout == FALSE) 
	{
		if(state->top_state && state->top_state->style_stack)
			style_struct = STYLESTACK_GetStyleByIndex(
											state->top_state->style_stack,
											0);
	}

	/*
	 * Fill in the subdoc structure with default data
	 */
	lo_reuse_current_subdoc(context, table->current_subdoc);
	subdoc = table->current_subdoc;
	if (subdoc == NULL)
	{
		return;
	}

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

	subdoc->anchor_href = state->current_anchor;

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

	subdoc->FE_Data = NULL;

	subdoc->vert_alignment = table_row->vert_alignment;
	if (subdoc->vert_alignment == LO_ALIGN_DEFAULT)
	{
		subdoc->vert_alignment = LO_ALIGN_CENTER;
	}
	subdoc->horiz_alignment = table_row->horiz_alignment;
	if (subdoc->horiz_alignment == LO_ALIGN_DEFAULT)
	{
		if (is_a_header == FALSE)
		{
			subdoc->horiz_alignment = LO_ALIGN_LEFT;
		}
		else
		{
			subdoc->horiz_alignment = LO_ALIGN_CENTER;
		}
	}
	subdoc->alignment = LO_ALIGN_BOTTOM;

	if (draw_borders == TABLE_BORDERS_OFF)
	{
		subdoc->border_width = 0;
		subdoc->border_vert_space = TABLE_DEF_CELL_BORDER;
		subdoc->border_horiz_space = TABLE_DEF_CELL_BORDER;
	}
	else if (draw_borders == TABLE_BORDERS_ON)
	{
		subdoc->border_width = TABLE_DEF_CELL_BORDER;
		subdoc->border_vert_space = 0;
		subdoc->border_horiz_space = 0;
	}
	else
	{
		subdoc->border_width = 0;
		subdoc->border_vert_space = 0;
		subdoc->border_horiz_space = 0;
	}

	subdoc->ele_attrmask = 0;

	subdoc->sel_start = -1;
	subdoc->sel_end = -1;

	subdoc->backdrop.bg_color = NULL;
    subdoc->backdrop.url = NULL;
    subdoc->backdrop.tile_mode = LO_TILE_BOTH;

	/*
	 * May inherit a bg_color from your row.
	 */
	if (table_row->backdrop.bg_color != NULL)
	{
		subdoc->backdrop.bg_color = XP_NEW(LO_Color);
		if (subdoc->backdrop.bg_color != NULL)
			*subdoc->backdrop.bg_color = *table_row->backdrop.bg_color;
	}

	if (relayout == FALSE)
	{
		subdoc->backdrop.bg_color = NULL;
		subdoc->backdrop.url = NULL;
		subdoc->backdrop.tile_mode = LO_TILE_BOTH;

		/*
		 * May inherit a bg_color from your row.
		 */
		if (table_row->backdrop.bg_color != NULL)
		{
			subdoc->backdrop.bg_color = XP_NEW(LO_Color);
			if (subdoc->backdrop.bg_color != NULL)
				*subdoc->backdrop.bg_color = *table_row->backdrop.bg_color;
		}

		/*
		 * Else may inherit a bg_color from your table.
		 */
		else if (table->backdrop.bg_color != NULL)
		{
			subdoc->backdrop.bg_color = XP_NEW(LO_Color);
			if (subdoc->backdrop.bg_color != NULL)
				*subdoc->backdrop.bg_color = *table->backdrop.bg_color;
		}
		/*
		 * Else possibly inherit the background color attribute
		 * of a parent table cell.
		 */
		else if (state->is_a_subdoc == SUBDOC_CELL)
		{
			lo_DocState *up_state;

			/*
			 * Find the parent subdoc's state.
			 */
			up_state = state->top_state->doc_state;
			while ((up_state->sub_state != NULL)&&
				(up_state->sub_state != state))
			{
				up_state = up_state->sub_state;
			}
			if ((up_state->sub_state != NULL)&&
				(up_state->current_ele != NULL)&&
				(up_state->current_ele->type == LO_SUBDOC)&&
				(up_state->current_ele->lo_subdoc.backdrop.bg_color != NULL))
			{
				LO_Color *old_bg;

				old_bg = up_state->current_ele->lo_subdoc.backdrop.bg_color;
				subdoc->backdrop.bg_color = XP_NEW(LO_Color);
				if (subdoc->backdrop.bg_color != NULL)
					*subdoc->backdrop.bg_color = *old_bg;
			}
		}


		/* Use backdrop URL, if supplied. */
		if (background_attr)
		{
			subdoc->backdrop.url = XP_STRDUP(background_attr);
			subdoc->backdrop.tile_mode = tile_mode;
		}
    
		/*
		 * May inherit a backdrop_url from parent row.
		 */
		else if (table_row->backdrop.url)
		{
			subdoc->backdrop.url = XP_STRDUP(table_row->backdrop.url);
			subdoc->backdrop.tile_mode = table_row->backdrop.tile_mode;
		}

		/*
		 * Else may inherit a backdrop_url from parent table.
		 */
		else if (table->backdrop.url != NULL)
		{
			subdoc->backdrop.url = XP_STRDUP(table->backdrop.url);
			subdoc->backdrop.tile_mode = table->backdrop.tile_mode;
		}
		/*
		 * Else possibly inherit the backdrop_url
		 * of a parent table cell.
		 */
		else if (state->is_a_subdoc == SUBDOC_CELL)
		{
			lo_DocState *up_state;

			/*
			 * Find the parent subdoc's state.
			 */
			up_state = state->top_state->doc_state;
			while ((up_state->sub_state != NULL)&&
				(up_state->sub_state != state))
			{
				up_state = up_state->sub_state;
			}
			if ((up_state->sub_state != NULL)&&
				(up_state->current_ele != NULL)&&
				(up_state->current_ele->type == LO_SUBDOC)&&
				(up_state->current_ele->lo_subdoc.backdrop.url != NULL))
			{
				lo_Backdrop *src_backdrop = &up_state->current_ele->lo_subdoc.backdrop;
				subdoc->backdrop.url = XP_STRDUP(src_backdrop->url);
				subdoc->backdrop.tile_mode = src_backdrop->tile_mode;
			}
		}

		/*
		 * Check for a background color attribute
		 */
		if (bgcolor_attr)
		{
			uint8 red, green, blue;

			LO_ParseRGB(bgcolor_attr, &red, &green, &blue);
			XP_FREEIF(subdoc->backdrop.bg_color);
			subdoc->backdrop.bg_color = XP_NEW(LO_Color);
			if (subdoc->backdrop.bg_color != NULL)
			{
				subdoc->backdrop.bg_color->red = red;
				subdoc->backdrop.bg_color->green = green;
				subdoc->backdrop.bg_color->blue = blue;
			}
		}

		/* check for a style sheet directive for bgcolor */
		if(style_struct)
		{
			char *property = STYLESTRUCT_GetString(style_struct, BG_COLOR_STYLE);
		
			if(property)
			{
				uint8 red, green, blue;

				LO_ParseStyleSheetRGB(property, &red, &green, &blue);

				XP_FREEIF(subdoc->backdrop.bg_color);
				subdoc->backdrop.bg_color = XP_NEW(LO_Color);
				if (subdoc->backdrop.bg_color != NULL)
				{
					subdoc->backdrop.bg_color->red = red;
					subdoc->backdrop.bg_color->green = green;
					subdoc->backdrop.bg_color->blue = blue;
				}
				XP_FREE(property);
			}

			property = STYLESTRUCT_GetString(style_struct, BG_IMAGE_STYLE);
			if(property)
			{
				if(subdoc->backdrop.url)
					XP_FREE(subdoc->backdrop.url);
				subdoc->backdrop.url = NULL;

				if(strcasecomp(property, "none"))
			{
					subdoc->backdrop.url = XP_STRDUP(lo_ParseStyleSheetURL(property));
				}
				XP_FREE(property);
			}

			property = STYLESTRUCT_GetString(style_struct, BG_REPEAT_STYLE);
			if(property)
			{
				if(!strcasecomp(property, BG_REPEAT_NONE_STYLE))
					subdoc->backdrop.tile_mode = LO_NO_TILE;
				else if(!strcasecomp(property, BG_REPEAT_X_STYLE))
					subdoc->backdrop.tile_mode = LO_TILE_HORIZ;
				else if(!strcasecomp(property, BG_REPEAT_Y_STYLE))
					subdoc->backdrop.tile_mode = LO_TILE_VERT;
				else 
					subdoc->backdrop.tile_mode = LO_TILE_BOTH;
			}
		}
	}	
	else  /* If relayout is TRUE */
	{
		/*
		subdoc->backdrop.tile_mode = table_cell->cell->backdrop.tile_mode;		
		if (table_cell->cell->backdrop.bg_color != NULL)
		{
			subdoc->backdrop.bg_color = XP_NEW(LO_Color);
			subdoc->backdrop.bg_color->red = table_cell->cell->backdrop.bg_color->red;
			subdoc->backdrop.bg_color->green = table_cell->cell->backdrop.bg_color->green;
			subdoc->backdrop.bg_color->blue = table_cell->cell->backdrop.bg_color->blue;
		}
		if (table_cell->cell->backdrop.url != NULL)
		{
			subdoc->backdrop.url = XP_STRDUP(table_cell->cell->backdrop.url);
		}
		*/
		subdoc->backdrop = table_cell->cell->backdrop;
	}

	/*
	 * Check for a vertical align parameter
	 */
	if (valign_attr)
	{
		subdoc->vert_alignment = lo_EvalVAlignParam(valign_attr);
	}



	if(style_struct)
	{
		char *property = STYLESTRUCT_GetString(style_struct, VERTICAL_ALIGN_STYLE);
	
		if(property)
		{
			subdoc->vert_alignment = lo_EvalVAlignParam(property);
			XP_FREE(property);
		}
	}

	if (relayout)
	{
		subdoc->vert_alignment = table_cell->vert_alignment;
	}

	/*
	 * Check for a horizontal align parameter
	 */
	if (halign_attr)
	{
		subdoc->horiz_alignment = lo_EvalCellAlignParam(halign_attr);
	}

	if (relayout)
	{
		subdoc->horiz_alignment = table_cell->horiz_alignment;
	}

	subdoc->border_width = FEUNITS_X(subdoc->border_width, context);
	subdoc->border_vert_space =
		FEUNITS_Y(subdoc->border_vert_space, context);
	subdoc->border_horiz_space =
		FEUNITS_X(subdoc->border_horiz_space, context);

	/*
	 * Allow individual setting of cell
	 * width and height.
	 */
	if (width_attr)
	{
		Bool is_percent;

		val = lo_ValueOrPercent(width_attr, &is_percent);
		if (is_percent != FALSE)
		{
			/*
			 * Percentage width cells must be handled
			 * later after at least the whole row is
			 * processed.
			 */
			table_cell->percent_width = val;
			table_row->has_percent_width_cells = TRUE;
			table->has_percent_width_cells = TRUE;
			val = lo_CalculateCellPercentWidth( table, val );

			/* Copied to lo_CalculateCellPercentWidth() */
			/*
			if ( table->table_width_fixed != FALSE )
			{
				val = table->width * val / 100;
			}
			else
			{
				val = 0;
			}
			*/
		}
		else
		{
			table_cell->percent_width = 0;
			val = FEUNITS_X(val, context);
		}
		if (val < 0)
		{
			val = 0;
		}
		
		subdoc->width = val;
		table_cell->specified_width = subdoc->width;
	}

	/* During relayout without reload, recalculate width of subdoc */
	if (relayout)
	{
		if (table_cell->percent_width > 0)
		{
			subdoc->width = lo_CalculateCellPercentWidth( table, table_cell->percent_width);
			table_cell->specified_width = subdoc->width;
		}
		else {
			subdoc->width = table_cell->specified_width;
			if (subdoc->width < 0) {
				subdoc->width = 0;
			}
		}
		
	}
							
	/*
	 * If we're in the fixed layout case, figure out what our cell
	 * width should be.
	 */
	if ( table->fixed_cols > 0 )
	{
		int32 cell_empty_space;
		int32 def_cell_width;
		int32 desired_width;
		
		cell_empty_space = lo_ComputeCellEmptySpace ( context, table, subdoc);

		desired_width = subdoc->width;
		/*
		 * Obey percentage cell width in fixed columns if we
		 * already know table width.
		 */
		if ((table_cell->percent_width > 0)&&(table->width > 0))
		{
			int32 avail_width;
			int32 cell_pad;

			/*
			 * Percentage width is a percentage of the available
			 * cell space.
			 */
			cell_pad = FEUNITS_X(table->inter_cell_pad, context);
			avail_width = table->width -
				((table->fixed_cols + 1) * cell_pad);
			avail_width -= (table->table_ele->border_left_width +
					table->table_ele->border_right_width);
			desired_width = avail_width *
				table_cell->percent_width / 100;
			/*
			 * Pecentage width takes into account the
			 * cell padding/spacing already.
			 */
			desired_width -= cell_empty_space;
		}
		def_cell_width = lo_FindDefaultCellWidth (context, table,
			table_cell, desired_width, cell_empty_space);
		
		/*
		 * If NOWRAP is off, we can go ahead and layout to this width. Otherwise
		 * we want to layout to a dynamic width and then use the bigger of the
		 * default or computed width for the cell.
		 */
		if ( table_cell->nowrap == FALSE )
		{
			subdoc->width = def_cell_width;
		}
	}
		
	if (height_attr)
	{
		Bool is_percent;

		val = lo_ValueOrPercent(height_attr, &is_percent);
		if (is_percent != FALSE)
		{
			/*
			 * We can process percentage heights here if
			 * we have a known table height.
			 */
			table_cell->percent_height = val;
			table->has_percent_height_cells = TRUE;
			val = lo_CalculateCellPercentHeight(table, table_cell);

			/* Copied to lo_CalculateCellPercentHeight() */
			/*
			if ((table->height > 0)&&
				(table_cell->percent_height > 0))
			{
				val = table->height *
					table_cell->percent_height / 100;
			}
			else
			{
				val = 0;
			}
			*/
		}
		else
		{
			table_cell->percent_height = 0;
			val = FEUNITS_X(val, context);
		}
		subdoc->height = val;
		if (subdoc->height < 0)
		{
			subdoc->height = 0;
		}
		table_cell->specified_height = subdoc->height;
	}

	/* During relayout without reload, recalculate height of subdoc */
	if (relayout)
	{
		if (table_cell->percent_height > 0)
		{
			subdoc->height = lo_CalculateCellPercentHeight( table, table_cell);
			table_cell->specified_height = subdoc->height;
		}
		else {
			subdoc->height = table_cell->specified_height;
			if (subdoc->height < 0) {
				subdoc->height = 0;
			}
		}
		
	}

	if (subdoc->width == 0)
	{
		/*
		 * Choose a default size that will fit within the current table. If NOWRAP is
		 * on, we just want to go ahead and choose a big width so that we're sure
		 * to get the correct nonwrapping text layout.
		 */
		if ( table_cell->nowrap == FALSE )
		{
			first_width = lo_ComputeInternalTableWidth ( context, table, state );
			first_width -= lo_ComputeCellEmptySpace ( context, table, subdoc);
		}
		else
		{
			first_width = FEUNITS_X(5000, context);
		}
		
		allow_percent_width = FALSE;
	}
	else
	{
		first_width = subdoc->width;
		allow_percent_width = TRUE;
	}

	if (subdoc->height == 0)
	{
		first_height = FEUNITS_Y(10000, context);
		allow_percent_height = FALSE;
	}
	else
	{
		first_height = subdoc->height;
		allow_percent_height = TRUE;
	}

	lo_reuse_current_state(context, subdoc->state,
		first_width, first_height, 0, 0, TRUE);
	new_state = subdoc->state;
	subdoc->state = NULL;

	if (new_state == NULL)
	{
		lo_FreeElement(context, (LO_Element *)subdoc, FALSE);
		return;
	}
	new_state->is_a_subdoc = SUBDOC_CELL;
	lo_InheritParentState(context, new_state, state);
	new_state->allow_percent_width = allow_percent_width;
	new_state->allow_percent_height = allow_percent_height;

	new_state->base_x = 0;
	new_state->base_y = 0;
	new_state->display_blocked = TRUE;
	if (subdoc->width == 0)
	{
		new_state->delay_align = TRUE;
	}

	new_state->win_left = 0;
	new_state->win_right = 0;
	new_state->x = new_state->win_left;
	new_state->y = 0;
	new_state->max_width = new_state->x + 1;
	new_state->left_margin = new_state->win_left;
	new_state->right_margin = new_state->win_width - new_state->win_right;
	new_state->list_stack->old_left_margin = new_state->left_margin;
	new_state->list_stack->old_right_margin = new_state->right_margin;

	/*
	 * Initialize the alignment stack for this state
	 * to match the caption alignment.
	 */
	if (subdoc->horiz_alignment != LO_ALIGN_LEFT)
	{
		lo_PushAlignment(new_state, P_TABLE_DATA, subdoc->horiz_alignment);
	}

	state->sub_state = new_state;

	state->current_ele = (LO_Element *)subdoc;

	if (is_a_header != FALSE)
	{
		LO_TextAttr *old_attr;
		LO_TextAttr *attr;
		LO_TextAttr tmp_attr;

		old_attr = new_state->font_stack->text_attr;
		lo_CopyTextAttr(old_attr, &tmp_attr);
		tmp_attr.fontmask |= LO_FONT_BOLD;
		attr = lo_FetchTextAttr(new_state, &tmp_attr);

		lo_PushFont(new_state, P_TABLE_DATA, attr);
	}
}


Bool
lo_subdoc_has_elements(lo_DocState *sub_state)
{
	if (sub_state->end_last_line != NULL)
	{
		return(TRUE);
	}
	if (sub_state->float_list != NULL)
	{
		return(TRUE);
	}
	return(FALSE);
}


Bool
lo_cell_has_elements(LO_CellStruct *cell)
{
	if (cell->cell_list != NULL)
	{
		return(TRUE);
	}
	if (cell->cell_float_list != NULL)
	{
		return(TRUE);
	}
	return(FALSE);
}


int32
lo_align_subdoc(MWContext *context, lo_DocState *state, lo_DocState *old_state,
	LO_SubDocStruct *subdoc, lo_TableRec *table, lo_table_span *row_max)
{
	int32 ele_cnt;
	LO_Element *eptr;
	int32 vert_inc;
	int32 top_inner_pad;
	int32 bottom_inner_pad;
	int32 left_inner_pad;
	int32 right_inner_pad;
	LO_Element **line_array;

	top_inner_pad = FEUNITS_X(table->inner_top_pad, context);
	bottom_inner_pad = FEUNITS_X(table->inner_bottom_pad, context);
	left_inner_pad = FEUNITS_X(table->inner_left_pad, context);
	right_inner_pad = FEUNITS_X(table->inner_right_pad, context);

	ele_cnt = 0;

	/*
	 * Mess with vertical alignment.
	 */
	vert_inc = subdoc->height - (2 * subdoc->border_width) - 
		(top_inner_pad + bottom_inner_pad) - old_state->y;
	if (vert_inc > 0)
	{
		if (subdoc->vert_alignment == LO_ALIGN_CENTER)
		{
			vert_inc = vert_inc / 2;
		}
		else if (subdoc->vert_alignment == LO_ALIGN_BOTTOM)
		{
			/* vert_inc unchanged */
		}
		else if ((subdoc->vert_alignment == LO_ALIGN_BASELINE)&&
			(row_max != NULL))
		{
			int32 baseline;
			int32 tmp_val;

			baseline = lo_GetSubDocBaseline(subdoc);
			tmp_val = row_max->min_dim - baseline;
			if (tmp_val > vert_inc)
			{
				tmp_val = vert_inc;
			}
			if (tmp_val > 0)
			{
				vert_inc = tmp_val;
			}
			else
			{
				vert_inc = 0;
			}
		}
		else
		{
			vert_inc = 0;
		}
	}
	else
	{
		vert_inc = 0;
	}

	/*
	 * Make eptr point to the start of the element chain
	 * for this subdoc.
	 */
#ifdef XP_WIN16
{
	XP_Block *larray_array;

	XP_LOCK_BLOCK(larray_array, XP_Block *, old_state->larray_array);
	old_state->line_array = larray_array[0];
	XP_UNLOCK_BLOCK(old_state->larray_array);
}
#endif /* XP_WIN16 */
	XP_LOCK_BLOCK(line_array, LO_Element **, old_state->line_array);
	eptr = line_array[0];
	XP_UNLOCK_BLOCK(old_state->line_array);

	while (eptr != NULL)
	{
		eptr->lo_any.x += left_inner_pad;

		switch (eptr->type)
		{
		    case LO_LINEFEED:
			if (eptr->lo_linefeed.ele_attrmask &
				LO_ELE_DELAY_ALIGNED)
			{
			    if (eptr->lo_linefeed.ele_attrmask &
				LO_ELE_DELAY_CENTER)
			    {
				int32 side_inc;
				int32 inside_width;
				LO_Element *c_eptr;

				c_eptr = eptr;
				inside_width = subdoc->width -
				    (2 * subdoc->border_width) -
				    (2 * subdoc->border_horiz_space) -
				    (left_inner_pad + right_inner_pad);

				side_inc = inside_width -
					(c_eptr->lo_linefeed.x +
					c_eptr->lo_linefeed.x_offset -
					left_inner_pad);
				side_inc = side_inc / 2;
				if (side_inc > 0)
				{
					c_eptr->lo_linefeed.width -= side_inc;
					if (c_eptr->lo_linefeed.width < 0)
					{
						c_eptr->lo_linefeed.width = 0;
					}
					while ((c_eptr->lo_any.prev != NULL)&&
						(c_eptr->lo_any.prev->type !=
							LO_LINEFEED))
					{
						c_eptr->lo_any.x += side_inc;
						if (c_eptr->type == LO_CELL)
						{
						    lo_ShiftCell(
							(LO_CellStruct *)c_eptr,
							side_inc, 0);
						}
						c_eptr = c_eptr->lo_any.prev;
					}
					c_eptr->lo_any.x += side_inc;
					if (c_eptr->type == LO_CELL)
					{
					    lo_ShiftCell(
						(LO_CellStruct *)c_eptr,
						side_inc, 0);
					}
				}
			    }
			    else if (eptr->lo_linefeed.ele_attrmask &
				LO_ELE_DELAY_RIGHT)
			    {
				int32 side_inc;
				int32 inside_width;
				LO_Element *c_eptr;

				c_eptr = eptr;
				inside_width = subdoc->width -
				    (2 * subdoc->border_width) -
				    (2 * subdoc->border_horiz_space) -
				    (left_inner_pad + right_inner_pad);

				side_inc = inside_width -
					(c_eptr->lo_linefeed.x +
					c_eptr->lo_linefeed.x_offset -
					left_inner_pad);
				if (side_inc > 0)
				{
					c_eptr->lo_linefeed.width -= side_inc;
					if (c_eptr->lo_linefeed.width < 0)
					{
						c_eptr->lo_linefeed.width = 0;
					}
					while ((c_eptr->lo_any.prev != NULL)&&
						(c_eptr->lo_any.prev->type !=
							LO_LINEFEED))
					{
						c_eptr->lo_any.x += side_inc;
						if (c_eptr->type == LO_CELL)
						{
						    lo_ShiftCell(
							(LO_CellStruct *)c_eptr,
							side_inc, 0);
						}
						c_eptr = c_eptr->lo_any.prev;
					}
					c_eptr->lo_any.x += side_inc;
					if (c_eptr->type == LO_CELL)
					{
					    lo_ShiftCell(
						(LO_CellStruct *)c_eptr,
						side_inc, 0);
					}
				}
			    }
			    eptr->lo_linefeed.ele_attrmask &=
					(~(LO_ELE_DELAY_ALIGNED));
			}
			break;
		    default:
			break;
		}
		eptr->lo_any.y += (vert_inc + top_inner_pad);
		if (eptr->type == LO_CELL)
		{
			lo_ShiftCell((LO_CellStruct *)eptr, left_inner_pad,
				(vert_inc + top_inner_pad));
		}
		eptr = eptr->lo_any.next;
		ele_cnt++;
	}

	eptr = old_state->float_list;
	while (eptr != NULL)
	{
		eptr->lo_any.x += left_inner_pad;
		eptr->lo_any.y += (vert_inc + top_inner_pad);
		if (eptr->type == LO_CELL)
		{
			lo_ShiftCell((LO_CellStruct *)eptr, left_inner_pad,
				(vert_inc + top_inner_pad));
		}
		eptr = eptr->lo_any.next;
		ele_cnt++;
	}

	return(ele_cnt);
}


int32
lo_align_cell(MWContext *context, lo_DocState *state, lo_TableCell *cell_ptr,
	LO_CellStruct *cell, lo_TableRec *table, lo_table_span *row_max)
{
	int32 ele_cnt;
	LO_Element *eptr;
	int32 vert_inc;
	int32 top_inner_pad;
	int32 bottom_inner_pad;
	int32 left_inner_pad;
	int32 right_inner_pad;

	top_inner_pad = FEUNITS_X(table->inner_top_pad, context);
	bottom_inner_pad = FEUNITS_X(table->inner_bottom_pad, context);
	left_inner_pad = FEUNITS_X(table->inner_left_pad, context);
	right_inner_pad = FEUNITS_X(table->inner_right_pad, context);

	ele_cnt = 0;

	/*
	 * Mess with vertical alignment.
	 */
	vert_inc = cell->height - (2 * cell->border_width) - 
		(top_inner_pad + bottom_inner_pad) - cell_ptr->max_y;
	if (vert_inc > 0)
	{
		if (cell_ptr->vert_alignment == LO_ALIGN_CENTER)
		{
			vert_inc = vert_inc / 2;
		}
		else if (cell_ptr->vert_alignment == LO_ALIGN_BOTTOM)
		{
			/* vert_inc unchanged */
		}
		else if ((cell_ptr->vert_alignment == LO_ALIGN_BASELINE)&&
			(row_max != NULL))
		{
			int32 baseline;
			int32 tmp_val;

			baseline = lo_GetCellBaseline(cell);
			tmp_val = row_max->min_dim - baseline;
			if (tmp_val > vert_inc)
			{
				tmp_val = vert_inc;
			}
			if (tmp_val > 0)
			{
				vert_inc = tmp_val;
			}
			else
			{
				vert_inc = 0;
			}
		}
		else
		{
			vert_inc = 0;
		}
	}
	else
	{
		vert_inc = 0;
	}

	/*
	 * Make eptr point to the start of the element chain
	 * for this cell.
	 */
	eptr = cell->cell_list;

	while (eptr != NULL)
	{
		eptr->lo_any.x += left_inner_pad;

		switch (eptr->type)
		{
		    case LO_LINEFEED:
			if (eptr->lo_linefeed.ele_attrmask &
				LO_ELE_DELAY_ALIGNED)
			{
			    if (eptr->lo_linefeed.ele_attrmask &
				LO_ELE_DELAY_CENTER)
			    {
				int32 side_inc;
				int32 inside_width;
				LO_Element *c_eptr;

				c_eptr = eptr;
				inside_width = cell->width -
				    (2 * cell->border_width) -
				    (2 * cell->border_horiz_space) -
				    (left_inner_pad + right_inner_pad);

				side_inc = inside_width -
					(c_eptr->lo_linefeed.x +
					c_eptr->lo_linefeed.x_offset -
					left_inner_pad);
				side_inc = side_inc / 2;
				if (side_inc > 0)
				{
					c_eptr->lo_linefeed.width -= side_inc;
					if (c_eptr->lo_linefeed.width < 0)
					{
						c_eptr->lo_linefeed.width = 0;
					}
					while ((c_eptr->lo_any.prev != NULL)&&
						(c_eptr->lo_any.prev->type !=
							LO_LINEFEED))
					{
						c_eptr->lo_any.x += side_inc;
						if (c_eptr->type == LO_CELL)
						{
						    lo_ShiftCell(
							(LO_CellStruct *)c_eptr,
							side_inc, 0);
						}
						c_eptr = c_eptr->lo_any.prev;
					}
					c_eptr->lo_any.x += side_inc;
					if (c_eptr->type == LO_CELL)
					{
					    lo_ShiftCell(
						(LO_CellStruct *)c_eptr,
						side_inc, 0);
					}
				}
			    }
			    else if (eptr->lo_linefeed.ele_attrmask &
				LO_ELE_DELAY_RIGHT)
			    {
				int32 side_inc;
				int32 inside_width;
				LO_Element *c_eptr;

				c_eptr = eptr;
				inside_width = cell->width -
				    (2 * cell->border_width) -
				    (2 * cell->border_horiz_space) -
				    (left_inner_pad + right_inner_pad);

				side_inc = inside_width -
					(c_eptr->lo_linefeed.x +
					c_eptr->lo_linefeed.x_offset -
					left_inner_pad);
				if (side_inc > 0)
				{
					c_eptr->lo_linefeed.width -= side_inc;
					if (c_eptr->lo_linefeed.width < 0)
					{
						c_eptr->lo_linefeed.width = 0;
					}
					while ((c_eptr->lo_any.prev != NULL)&&
						(c_eptr->lo_any.prev->type !=
							LO_LINEFEED))
					{
						c_eptr->lo_any.x += side_inc;
						if (c_eptr->type == LO_CELL)
						{
						    lo_ShiftCell(
							(LO_CellStruct *)c_eptr,
							side_inc, 0);
						}
						c_eptr = c_eptr->lo_any.prev;
					}
					c_eptr->lo_any.x += side_inc;
					if (c_eptr->type == LO_CELL)
					{
					    lo_ShiftCell(
						(LO_CellStruct *)c_eptr,
						side_inc, 0);
					}
				}
			    }
			    eptr->lo_linefeed.ele_attrmask &=
					(~(LO_ELE_DELAY_ALIGNED));
			}
			break;
		    default:
			break;
		}
		eptr->lo_any.y += (vert_inc + top_inner_pad);
		if (eptr->type == LO_CELL)
		{
			lo_ShiftCell((LO_CellStruct *)eptr, left_inner_pad,
				(vert_inc + top_inner_pad));
		}
		eptr = eptr->lo_any.next;
		ele_cnt++;
	}

	eptr = cell->cell_float_list;
	while (eptr != NULL)
	{
		eptr->lo_any.x += left_inner_pad;
		eptr->lo_any.y += (vert_inc + top_inner_pad);
		if (eptr->type == LO_CELL)
		{
			lo_ShiftCell((LO_CellStruct *)eptr, left_inner_pad,
				(vert_inc + top_inner_pad));
		}
		eptr = eptr->lo_any.next;
		ele_cnt++;
	}

	return(ele_cnt);
}


LO_SubDocStruct *
lo_EndCellSubDoc(MWContext *context, lo_DocState *state, lo_DocState *old_state,
	LO_Element *element, Bool relayout)
{
	LO_Element *eptr;
	LO_SubDocStruct *subdoc;
	PA_Block buff;
	char *str;
	Bool line_break;
	int32 baseline_inc;
	int32 line_inc;
	int32 subdoc_height, subdoc_width;
	int32 doc_height;
	LO_TextStruct tmp_text;
	LO_TextInfo text_info;
	LO_Element **line_array;
	Bool dynamic_width;
	Bool dynamic_height;
#ifdef LOCAL_DEBUG
 fprintf(stderr, "lo_EndCellSubDoc called\n");
#endif /* LOCAL_DEBUG */

    /* Close any layers (blocks) that were opened in this cell, in case
	   the document author forgot to. */
    while (old_state->layer_nest_level > 0)
	{
		lo_EndLayer(context, old_state, PR_TRUE);
	}

	subdoc = (LO_SubDocStruct *)element;
	subdoc->state = (void *)old_state;

	/*
	 * All this work is to get the text_info filled in for the current
	 * font in the font stack. Yuck, there must be a better way.
	 */
	memset (&tmp_text, 0, sizeof (tmp_text));
	buff = PA_ALLOC(1);
	if (buff == NULL)
	{
		state->top_state->out_of_memory = TRUE;
		return(NULL);
	}
	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, &text_info);
	PA_FREE(buff);

	if (relayout == FALSE)
	{
		/*
		 * We don't want to leave any end paragraph linbreaks in the subdoc
		 * as they give us a really ugly blank line at the bottom of a cell. So,
		 * if the last element in the list is paragraph break linefeed, then
		 * we kill it.
		 */
		eptr = old_state->end_last_line;
		if ( (eptr != NULL) && (eptr->type == LO_LINEFEED) &&
			(eptr->lo_linefeed.break_type == LO_LINEFEED_BREAK_PARAGRAPH) )
		{
			LO_Element *prev;
			
			prev = eptr->lo_any.prev;

			/*
			 * Kill this layout element and reset the subdoc height
			 */
			if ( prev != NULL )
			{
				prev->lo_any.next = NULL;
				old_state->end_last_line = prev;
			}
			else
			{
				old_state->line_list = NULL;
				old_state->end_last_line = NULL;
			}
			
			/* The if condition checks to make sure that the linefeed that we just killed
			 * was sticking out of the bottom of the cell.  This fixes bug 81282.
			 */
			if (eptr->lo_linefeed.y + eptr->lo_linefeed.line_height >= old_state->y) {
				/* shrink the document */
				old_state->y -= eptr->lo_linefeed.line_height;
			}

			lo_RecycleElements ( context, old_state, eptr );
		}
	}
	
	dynamic_width = FALSE;
	dynamic_height = FALSE;
	if (subdoc->width == 0)
	{
		dynamic_width = TRUE;
		/*
		 * Subdocs that were dynamically widthed, and which
		 * contained right aligned items, must be relaid out
		 * once their width is determined.
		 */
		eptr = old_state->float_list;
		while (eptr != NULL)
		{
			if ((eptr->type == LO_IMAGE)&&
				(eptr->lo_image.image_attr->alignment == LO_ALIGN_RIGHT))
			{
				old_state->must_relayout_subdoc = TRUE;
				break;
			}
			else if ((eptr->type == LO_SUBDOC)&&
				(eptr->lo_subdoc.alignment == LO_ALIGN_RIGHT))
			{
				old_state->must_relayout_subdoc = TRUE;
				break;
			}
			eptr = eptr->lo_any.next;
		}

		subdoc->width = old_state->max_width;
	}

	/*
	 * Add in the size of the subdoc's borders
	 */
	subdoc->width += (2 * subdoc->border_width);

	/*
	 * Figure the height the subdoc laid out in.
	 * Use this if we were passed no height, or if it is greater
	 * than the passed height.
	 */
	doc_height = old_state->y + (2 * subdoc->border_width);
	if (subdoc->height == 0)
	{
		dynamic_height = TRUE;
		subdoc->height = doc_height;
	}
	else if (subdoc->height < doc_height)
	{
		subdoc->height = doc_height;
	}

	/*
	 * Make eptr point to the start of the element chain
	 * for this subdoc.
	 */
#ifdef XP_WIN16
{
	XP_Block *larray_array;

	XP_LOCK_BLOCK(larray_array, XP_Block *, old_state->larray_array);
	old_state->line_array = larray_array[0];
	XP_UNLOCK_BLOCK(old_state->larray_array);
}
#endif /* XP_WIN16 */
	XP_LOCK_BLOCK(line_array, LO_Element **, old_state->line_array);
	eptr = line_array[0];
	XP_UNLOCK_BLOCK(old_state->line_array);

	while (eptr != NULL)
	{
		switch (eptr->type)
		{
		    case LO_LINEFEED:
			if ((eptr->lo_linefeed.x + eptr->lo_linefeed.x_offset +
				eptr->lo_linefeed.width) >= (subdoc->width - 1 -
				(2 * subdoc->border_width)))
			{
				eptr->lo_linefeed.width = (subdoc->width - 1 -
					(2 * subdoc->border_width)) -
					(eptr->lo_linefeed.x +
					eptr->lo_linefeed.x_offset);
				if (eptr->lo_linefeed.width < 0)
				{
					eptr->lo_linefeed.width = 0;
				}
			}
#ifdef OLD_DELAY_CENTERED
			if (eptr->lo_linefeed.ele_attrmask &
				LO_ELE_DELAY_CENTERED)
			{
				int32 side_inc;
				LO_Element *c_eptr;

				c_eptr = eptr;

				side_inc = subdoc->width - 1 -
					(2 * subdoc->border_width) -
					(eptr->lo_linefeed.x +
					eptr->lo_linefeed.x_offset);
				side_inc = side_inc / 2;
				if (side_inc > 0)
				{
					c_eptr->lo_linefeed.width -= side_inc;
					if (c_eptr->lo_linefeed.width < 0)
					{
						c_eptr->lo_linefeed.width = 0;
					}
					while ((c_eptr->lo_any.prev != NULL)&&
						(c_eptr->lo_any.prev->type !=
							LO_LINEFEED))
					{
						c_eptr->lo_any.x += side_inc;
						if (c_eptr->type == LO_CELL)
						{
						    lo_ShiftCell(
							(LO_CellStruct *)c_eptr,
							side_inc, 0);
						}
						c_eptr = c_eptr->lo_any.prev;
					}
					c_eptr->lo_any.x += side_inc;
					if (c_eptr->type == LO_CELL)
					{
					    lo_ShiftCell(
						(LO_CellStruct *)c_eptr,
						side_inc, 0);
					}
				}
				eptr->lo_linefeed.ele_attrmask &=
					(~(LO_ELE_DELAY_CENTERED));
			}
#endif /* OLD_DELAY_CENTERED */
			break;
		    /*
		     * This is awful, but any dynamically dimenstioned
		     * subdoc that contains one of these elements
		     * must be relayed out to take care of window
		     * width and height dependencies.
		     */
			/* case LO_HRULE:
		    case LO_SUBDOC:
		    case LO_TABLE:
				old_state->must_relayout_subdoc = TRUE;
			*/

			break;
		    case LO_IMAGE:
/* WILL NEED THIS LATER FOR PERCENT WIDTH IMAGES
			if ((old_state->allow_percent_width == FALSE)||
				(old_state->allow_percent_height == FALSE))
			{
				old_state->must_relayout_subdoc = TRUE;
			}
*/
			break;
		    default:
			break;
		}
		eptr = eptr->lo_any.next;
	}

	/*
	 * Remember: (2 * subdoc->border_width) has already been added to the subdoc
	 * width above...
	 */
	subdoc_width = subdoc->width + (2 * subdoc->border_horiz_space);
	subdoc_height = subdoc->height + (2 * subdoc->border_width) +
		(2 * subdoc->border_vert_space);

	if (old_state->display_blocked != FALSE)
	{
		old_state->display_blocked = FALSE;
	}

	/*
	 * Will this subdoc make the line too wide.
	 */
	if ((state->x + subdoc_width) > state->right_margin)
	{
		line_break = TRUE;
	}
	else
	{
		line_break = FALSE;
	}

	/*
	 * if we are at the beginning of the line.  There is
	 * no point in breaking, we are just too wide.
	 * Also don't break in unwrappedpreformatted text.
	 * Also can't break inside a NOBR section.
	 */
	if ((state->at_begin_line != FALSE)||
		(state->preformatted == PRE_TEXT_YES)||
		(state->breakable == FALSE))
	{
		line_break = FALSE;
	}

	/*
         * break on the subdoc if we have
         * a break.
         */
/* DOn't break in cell
        if (line_break != FALSE)
        {
                lo_SoftLineBreak(context, state, TRUE);
                subdoc->x = state->x;
                subdoc->y = state->y;
        }
*/

	/*
	 * Figure out how to align this subdoc.
	 * baseline_inc is how much to increase the baseline
	 * of previous element of this line.  line_inc is how
	 * much to increase the line height below the baseline.
	 */
	baseline_inc = 0;
	line_inc = 0;
	/*
	 * If we are at the beginning of a line, with no baseline,
	 * we first set baseline and line_height based on the current
	 * font, then place the subdoc.
	 */
	if (state->baseline == 0)
	{
		state->baseline = 0;
	}

	lo_CalcAlignOffsets(state, &text_info, (intn)subdoc->alignment,
		subdoc_width, subdoc_height,
		&subdoc->x_offset, &subdoc->y_offset, &line_inc, &baseline_inc);

	subdoc->x_offset += (int16)subdoc->border_horiz_space;
	subdoc->y_offset += (int32)subdoc->border_vert_space;

	old_state->base_x = subdoc->x + subdoc->x_offset +
		subdoc->border_width;
	old_state->base_y = subdoc->y + subdoc->y_offset +
		subdoc->border_width;

	/*
	 * Clean up state
	state->x = state->x + subdoc->x_offset +
		subdoc_width - subdoc->border_horiz_space;
	state->linefeed_state = 0;
	state->at_begin_line = FALSE;
	state->trailing_space = FALSE;
	state->cur_ele_type = LO_SUBDOC;
	 */

	return(subdoc);
}


void
lo_relayout_recycle(MWContext *context, lo_DocState *state, LO_Element *elist)
{
	LO_LockLayout();
    
	while (elist != NULL)
	{
		LO_Element *eptr;

		eptr = elist;
		elist = elist->lo_any.next;
		eptr->lo_any.next = NULL;

		if (eptr->type == LO_IMAGE)
		{
			eptr->lo_any.next = state->top_state->trash;
			state->top_state->trash = eptr;
		}
		else if (eptr->type == LO_CELL)
		{
			if (eptr->lo_cell.cell_list != NULL)
			{
			    if (eptr->lo_cell.cell_list_end != NULL)
			    {
				eptr->lo_cell.cell_list_end->lo_any.next = NULL;
			    }
			    lo_relayout_recycle(context, state,
					eptr->lo_cell.cell_list);
				eptr->lo_cell.cell_list = NULL;
				eptr->lo_cell.cell_list_end = NULL;
			}
			if (eptr->lo_cell.cell_float_list != NULL)
			{
			    lo_relayout_recycle(context, state,
					eptr->lo_cell.cell_float_list);
				eptr->lo_cell.cell_float_list = NULL;
			}
			lo_RecycleElements(context, state, eptr);
		}
		else
		{
			/*
			 * If this is an embed, tell it that it should
			 * recycle rather than delete itself.
			 */
			if (eptr->type == LO_EMBED)
			{
				NPL_SameElement((LO_EmbedStruct*)eptr);
			}
			lo_RecycleElements(context, state, eptr);
		}
	}
	LO_UnlockLayout();
}


static void
lo_cleanup_state(MWContext *context, lo_DocState *state)
{
	LO_Element **line_array;

	if (state == NULL)
	{
		return;
	}

	if (state->left_margin_stack != NULL)
	{
		lo_MarginStack *mptr;
		lo_MarginStack *margin;

		mptr = state->left_margin_stack;
		while (mptr != NULL)
		{
			margin = mptr;
			mptr = mptr->next;
			XP_DELETE(margin);
		}
		state->left_margin_stack = NULL;
	}
	if (state->right_margin_stack != NULL)
	{
		lo_MarginStack *mptr;
		lo_MarginStack *margin;

		mptr = state->right_margin_stack;
		while (mptr != NULL)
		{
			margin = mptr;
			mptr = mptr->next;
			XP_DELETE(margin);
		}
		state->right_margin_stack = NULL;
	}

	if (state->line_list != NULL)
	{
		lo_relayout_recycle(context, state, state->line_list);
		state->line_list = NULL;
	}

	if (state->font_stack != NULL)
	{
		lo_FontStack *fstack;
		lo_FontStack *fptr;

		fptr = state->font_stack;
		while (fptr != NULL)
		{
			fstack = fptr;
			fptr = fptr->next;
			XP_DELETE(fstack);
		}
		state->font_stack = NULL;
	}

	if (state->align_stack != NULL)
	{
		lo_AlignStack *aptr;
		lo_AlignStack *align;

		aptr = state->align_stack;
		while (aptr != NULL)
		{
			align = aptr;
			aptr = aptr->next;
			XP_DELETE(align);
		}
		state->align_stack = NULL;
	}

	if (state->list_stack != NULL)
	{
		lo_ListStack *lptr;
		lo_ListStack *list;

		lptr = state->list_stack;
		while (lptr != NULL)
		{
			list = lptr;
			lptr = lptr->next;
			XP_DELETE(list);
		}
		state->list_stack = NULL;
	}

	if (state->line_buf != NULL)
	{
		PA_FREE(state->line_buf);
		state->line_buf = NULL;
	}

	if (state->float_list != NULL)
	{
		lo_relayout_recycle(context, state, state->float_list);
		state->float_list = NULL;
	}

	if (state->line_array != NULL)
	{
		LO_Element *eptr;

#ifdef XP_WIN16
{
		XP_Block *larray_array;
		intn cnt;

		XP_LOCK_BLOCK(larray_array, XP_Block *, state->larray_array);
		cnt = state->larray_array_size - 1;
		while (cnt > 0)
		{
			XP_FREE_BLOCK(larray_array[cnt]);
			cnt--;
		}
		state->line_array = larray_array[0];
		XP_UNLOCK_BLOCK(state->larray_array);
		XP_FREE_BLOCK(state->larray_array);
}
#endif /* XP_WIN16 */

		eptr = NULL;
		XP_LOCK_BLOCK(line_array, LO_Element **, state->line_array);
		eptr = line_array[0];
		if (eptr != NULL)
		{
			lo_relayout_recycle(context, state, eptr);
		}
		XP_UNLOCK_BLOCK(state->line_array);
		XP_FREE_BLOCK(state->line_array);
	}

	XP_DELETE(state);
}


static lo_FormData *
lo_merge_form_data(lo_FormData *new_form_list, lo_FormData *hold_form_list)
{
	intn i;
	LO_FormElementStruct **new_ele_list;
	LO_FormElementStruct **hold_ele_list;

	PA_LOCK(new_ele_list, LO_FormElementStruct **,
			new_form_list->form_elements);
	PA_LOCK(hold_ele_list, LO_FormElementStruct **,
			hold_form_list->form_elements);
	for (i=0; i<new_form_list->form_ele_cnt; i++)
	{
		if (hold_ele_list[i] != new_ele_list[i])
		{
#ifdef MOCHA
			if (new_ele_list[i]->mocha_object == NULL)
			{
				new_ele_list[i]->mocha_object
				    = hold_ele_list[i]->mocha_object;
			}
#endif
			hold_ele_list[i] = new_ele_list[i];
		}
	}
	PA_UNLOCK(hold_form_list->form_elements);
	PA_UNLOCK(new_form_list->form_elements);

#ifdef MOCHA
	/* XXX sometimes the second pass does not reflect a form object! */
	if (new_form_list->mocha_object != NULL)
	{
		hold_form_list->mocha_object = new_form_list->mocha_object;
	}
#endif
	return(hold_form_list);
}


/*
 * Serious table magic.  Given a table cell subdoc,
 * redo the layout to the new width from the stored tag list,
 * and create a whole new subdoc, which is returned.
 */
LO_SubDocStruct *
lo_RelayoutCaptionSubdoc(MWContext *context, lo_DocState *state, lo_TableCaption *caption,
	LO_SubDocStruct *subdoc, int32 width, Bool is_a_header)
{
	int32 first_height;
	lo_TopState *top_state;
	lo_DocState *old_state;
	lo_DocState *new_state;
	PA_Tag *tag_list;
	PA_Tag *tag_ptr;
	PA_Tag *tag_end_ptr;
	int32 doc_id;
	Bool allow_percent_width;
	Bool allow_percent_height;
	int32 hold_form_ele_cnt;
	int32 hold_form_data_index;
	intn hold_form_id;
	intn hold_current_form_num;
	Bool hold_in_form;
	lo_FormData *hold_form_list;
	int32 hold_embed_global_index;
	int32 original_tag_count;
	intn hold_url_list_len;
	NET_ReloadMethod save_force;
	Bool save_diff_state;
	int32 save_state_pushes;
	int32 save_state_pops;
	lo_DocLists *doc_lists;
	int32 hold_image_list_count;
        int32 hold_applet_list_count;
        int32 hold_embed_list_count;
	int32 hold_current_layer_num;

	/*
	 * Relayout is NOT allowed to block on images, so never
	 * have the forced reload flag set.
	 */
	save_force = state->top_state->force_reload;
	state->top_state->force_reload = NET_CACHE_ONLY_RELOAD;

#ifdef LOCAL_DEBUG
 fprintf(stderr, "lo_RelayoutSubdoc called\n");
#endif /* LOCAL_DEBUG */

	top_state = state->top_state;
	old_state = (lo_DocState *) subdoc->state;
	
	/*
	 * We are going to reuse this subdoc element instead
	 * of allocating a new one.
	 */
	/* subdoc->state = NULL; */
	subdoc->width = width;
	subdoc->height = 0;

	doc_lists = lo_GetCurrentDocLists(state);
	
	/*
	 * If this subdoc contained form elements.  We want to reuse
	 * them without messing up the order of later form elements in
	 * other subdocs.
	 *
	 * We assume that relaying out the doc will create the exact
	 * same number of form elements in the same order as the first time.
	 * so we reset global form counters to before this subdoc
	 * was ever laid out, saving the current counts to restore later.
	 */
	if (doc_lists->form_list != NULL)
	{
		lo_FormData *form_list;
		lo_FormData *tmp_fptr;

		hold_form_id = old_state->form_id;
		hold_current_form_num = doc_lists->current_form_num;
		form_list = doc_lists->form_list;
		hold_form_list = NULL;
		while ((form_list != NULL)&&
			(form_list->id > hold_form_id))
		{
			tmp_fptr = form_list;
			form_list = form_list->next;
			tmp_fptr->next = hold_form_list;
			hold_form_list = tmp_fptr;
		}
		doc_lists->form_list = form_list;
		doc_lists->current_form_num = hold_form_id;
		if (form_list != NULL)
		{
			hold_form_ele_cnt = form_list->form_ele_cnt;
			form_list->form_ele_cnt = old_state->form_ele_cnt;
		}
		else
		{
			hold_form_ele_cnt = 0;
		}
	}
	else
	{
		hold_form_id = 0;
		hold_form_ele_cnt = 0;
		hold_form_list = NULL;
		hold_current_form_num = 0;
	}

	/*
	 * We're going to be deleting and then recreating the contents 
	 * of this subdoc.  We want the newly-created embeds to have
	 * the same embed indices as their recycled counterparts, so
	 * we reset the master embed index in the top_state to be the
	 * value it held at the time the old subdoc was created. When
	 * we're finished creating the new subdoc, the master index
	 * should be back to its original value (but just in case we
	 * save that value in a local variable here).
	 */
	hold_embed_global_index = top_state->embed_count;
	top_state->embed_count = old_state->embed_count_base;

	/* ditto here for the last comment.  We are trying to keep
	 * a tag count that reflects the order of tags coming in
 	 * on the stream.  We need that count to be correct across
	 * multiple relayout instances so we restore the top state
	 * count with the saved state
	 */
	original_tag_count = top_state->tag_count;
	top_state->tag_count = state->beginning_tag_count;

	/*
	 * Same goes for anchors and images
	 */
	hold_url_list_len = doc_lists->url_list_len;
	doc_lists->url_list_len = old_state->url_count_base;

	hold_image_list_count = doc_lists->image_list_count;
	doc_lists->image_list_count = old_state->image_list_count_base;

	hold_applet_list_count = doc_lists->applet_list_count;
	doc_lists->applet_list_count = old_state->applet_list_count_base;
	
	hold_embed_list_count = doc_lists->embed_list_count;
	doc_lists->embed_list_count = old_state->embed_list_count_base;

	hold_current_layer_num = top_state->current_layer_num;
	top_state->current_layer_num = old_state->current_layer_num_base;
	
	/*
	 * Save whether we are in a form or not, and restore
	 * to what we were when the subdoc was started.
	 */
	hold_in_form = top_state->in_form;
	top_state->in_form = old_state->start_in_form;

	if (top_state->savedData.FormList != NULL)
	{
		lo_SavedFormListData *all_form_ele;

		all_form_ele = top_state->savedData.FormList;
		hold_form_data_index = all_form_ele->data_index;
		all_form_ele->data_index = old_state->form_data_index;
	}
	else
	{
		hold_form_data_index = 0;
	}

	/*
	 * Still variable height, but we know the width now.
	 */
	first_height = FEUNITS_Y(10000, context);
	allow_percent_height = FALSE;
	allow_percent_width = TRUE;

	/*
	 * Get a new sub-state to create the new subdoc in.
	 */
	/*
	new_state = lo_NewLayout(context, width, first_height, 0, 0, NULL);
	*/
	lo_reuse_current_state(context, subdoc->state, width,
		first_height, 0, 0, FALSE);

	new_state = subdoc->state;
	subdoc->state = NULL;

	
	if (new_state == NULL)
	{
		state->top_state->force_reload = save_force;
		return(subdoc);
	}

	new_state->end_last_line = NULL;
	/*
	if (old_state->is_a_subdoc == SUBDOC_CAPTION)
	{
		new_state->is_a_subdoc = SUBDOC_CAPTION;
	}
	else
	{
		new_state->is_a_subdoc = SUBDOC_CELL;
	}
	*/
	new_state->is_a_subdoc = SUBDOC_CAPTION;

	lo_InheritParentColors(context, new_state, state);
	new_state->allow_percent_width = allow_percent_width;
	new_state->allow_percent_height = allow_percent_height;
	/*
	 * In relaying out subdocs, we NEVER link back up subdoc tags
	 */
	new_state->subdoc_tags = NULL;
	new_state->subdoc_tags_end = NULL;

	/*
	 * Initialize other new state sundries.
	 */
	new_state->base_x = 0;
	new_state->base_y = 0;
	new_state->display_blocked = TRUE;
	if (subdoc->width == 0)
	{
		new_state->delay_align = TRUE;
	}

	new_state->win_left = 0;
	new_state->win_right = 0;
	new_state->x = new_state->win_left;
	new_state->y = 0;
	new_state->max_width = new_state->x + 1;
	new_state->left_margin = new_state->win_left;
	new_state->right_margin = new_state->win_width - new_state->win_right;
	new_state->list_stack->old_left_margin = new_state->left_margin;
	new_state->list_stack->old_right_margin = new_state->right_margin;

	/* we don't need min widths during relayout */
	new_state->need_min_width = FALSE;

	/* reset line_height stack */
	while (new_state->line_height_stack)
		lo_PopLineHeight(new_state);

	/* 
	 * Initialize the alignment stack for this state
	 * to match the caption alignment.
	 */
	if (subdoc->horiz_alignment != LO_ALIGN_LEFT)
	{
		lo_PushAlignment(new_state, P_TABLE_DATA, subdoc->horiz_alignment);
	}

	state->sub_state = new_state;

	state->current_ele = (LO_Element *)subdoc;
	
	if (is_a_header != FALSE)
	{
		LO_TextAttr *old_attr;
		LO_TextAttr *attr;
		LO_TextAttr tmp_attr;

		old_attr = new_state->font_stack->text_attr;
		lo_CopyTextAttr(old_attr, &tmp_attr);
		tmp_attr.fontmask |= LO_FONT_BOLD;
		attr = lo_FetchTextAttr(new_state, &tmp_attr);

		lo_PushFont(new_state, P_TABLE_HEADER, attr);
	}

	/*
	 * Move our current state down into the new sub-state.
	 * Fetch the tag list from the old subdoc's state in preparation
	 * for relayout.
	 */
	state = new_state;

	/*
	tag_ptr = old_state->subdoc_tags;
	tag_end_ptr = old_state->subdoc_tags_end;
	old_state->subdoc_tags = NULL;
	old_state->subdoc_tags_end = NULL;
	if (tag_end_ptr != NULL)
	{
		tag_end_ptr = tag_end_ptr->next;
	}
	*/

	/*
	 * Clean up memory used by old subdoc state.
	 */
	/*
	lo_cleanup_state(context, old_state);
	*/

#if 0    /* Doesn't need to happen any more because we do non-destructive reflow
	/*
	 * Save our parent's state levels
	 */
	save_diff_state = top_state->diff_state;
	save_state_pushes = top_state->state_pushes;
	save_state_pops = top_state->state_pops;

	while (tag_ptr != tag_end_ptr)
	{
		PA_Tag *tag;
		lo_DocState *sub_state;
		lo_DocState *up_state;
		lo_DocState *tmp_state;
		Bool may_save;

		tag = tag_ptr;
		tag_ptr = tag_ptr->next;
		tag->next = NULL;
		tag_list = tag_ptr;

		up_state = NULL;
		sub_state = state;
		while (sub_state->sub_state != NULL)
		{
			up_state = sub_state;
			sub_state = sub_state->sub_state;
		}

		if ((sub_state->is_a_subdoc == SUBDOC_CELL)||
			(sub_state->is_a_subdoc == SUBDOC_CAPTION))
		{
			may_save = TRUE;
		}
		else
		{
			may_save = FALSE;
		}

		/*
		 * Reset these so we can tell if anything happened
		 */
		top_state->diff_state = FALSE;
		top_state->state_pushes = 0;
		top_state->state_pops = 0;

#ifdef MOCHA
		sub_state->in_relayout = TRUE;
#endif
		lo_LayoutTag(context, sub_state, tag);
		tmp_state = lo_CurrentSubState(state);
#ifdef MOCHA
		if (tmp_state == sub_state)
		{
			sub_state->in_relayout = FALSE;
		}
#endif

		if (may_save != FALSE)
		{
			int32 state_diff;
			
			/* how has our state level changed? */
			state_diff = top_state->state_pushes - top_state->state_pops;
			
			/*
			 * That tag popped us up one state level.  If this new
			 * state is still a subdoc, save the tag there.
			 */
			if (state_diff == -1)
			{
				if ((tmp_state->is_a_subdoc == SUBDOC_CELL)||
				    (tmp_state->is_a_subdoc == SUBDOC_CAPTION))
				{
                    /* if we just popped a table we need to insert
                     * a dummy end tag to pop the dummy start tag
                     * we shove on the stack after createing a table
                     */
   					PA_Tag *new_tag = LO_CreateStyleSheetDummyTag(tag);
					if(new_tag)
					{
						lo_SaveSubdocTags(context, tmp_state, new_tag);
					}

				    lo_SaveSubdocTags(context, tmp_state, tag);
				}
				else
				{
				    PA_FreeTag(tag);
				}
			}
			/*
			 * Else that tag put us in a new subdoc on the same
			 * level.  It needs to be saved one level up,
			 * if the parent is also a subdoc.
			 */
			else if (( up_state != NULL ) &&
				( top_state->diff_state != FALSE ) &&
				( state_diff == 0 ))
			{
				if ((up_state->is_a_subdoc == SUBDOC_CELL)||
				     (up_state->is_a_subdoc == SUBDOC_CAPTION))
				{
				    lo_SaveSubdocTags(context, up_state, tag);
				}
				else
				{
				    PA_FreeTag(tag);
				}
			}
			/*
			 * Else we are still in the same subdoc
			 */
			else if (( top_state->diff_state == FALSE ) &&
				( state_diff == 0 ))
			{
				lo_SaveSubdocTags(context, sub_state, tag);
			}
			/*
			 * Else that tag started a new, nested subdoc.
			 * Add the starting tag to the parent.
			 */
			else if (( state_diff == 1 ))
			{
				lo_SaveSubdocTags(context, sub_state, tag);
				/*
				 * Since we have extended the parent chain,
				 * we need to reset the child to the new
				 * parent end-chain.
				 */
				if ((tmp_state->is_a_subdoc == SUBDOC_CELL)||
				    (tmp_state->is_a_subdoc == SUBDOC_CAPTION))
				{
					PA_Tag *new_tag;
					
					tmp_state->subdoc_tags =
						sub_state->subdoc_tags_end;

					/* add an aditional dummy tag so that style sheets
					 * can use it to query styles from for this entry
					 * that created a table
					 */
					new_tag = LO_CreateStyleSheetDummyTag(tag);
					if(new_tag)
					{
						lo_SaveSubdocTags(context, tmp_state, new_tag);
					}
				}
			}
			/*
			 * This can never happen.
			 */
			else
			{
				PA_FreeTag(tag);
			}
		}
		tag_ptr = tag_list;
	}

	/*
	 * Restore our parent's state levels
	 */
	top_state->diff_state = save_diff_state;
	top_state->state_pushes = save_state_pushes;
	top_state->state_pops = save_state_pops;
#endif

	/*
	 * Link the end of this subdoc's tag chain back to
	 * its parent.  The beginning should have never been
	 * unlinked from the parent.
	 */
	/*
	if (state->subdoc_tags_end != NULL)
	{
		state->subdoc_tags_end->next = tag_end_ptr;
	}
	*/

	lo_rl_ReflowDocState( context, state );

	/*
	 * makes sure we are at the bottom
	 * of everything in the document.
	 */
	/*
	lo_CloseOutLayout(context, state);
	*/

	lo_EndLayoutDuringReflow( context, state );

	old_state = state;
	/*
	 * Get the unique document ID, and retreive this
	 * documents layout state.
	 */
	doc_id = XP_DOCID(context);
	top_state = lo_FetchTopState(doc_id);
	state = top_state->doc_state;

	while ((state->sub_state != NULL)&&
		(state->sub_state != old_state))
	{
		state = state->sub_state;
	}

	/*
	 * Ok, now reset the global form element counters
	 * to their original values.
	 */
	if ((doc_lists->form_list != NULL)||(hold_form_list != NULL))
	{
		lo_FormData *form_list;
		lo_FormData *new_form_list;
		lo_FormData *tmp_fptr;

		/*
		 * Remove any new forms created in this subdocs to a
		 * separate list.
		 */
		form_list = doc_lists->form_list;
		new_form_list = NULL;
		while ((form_list != NULL)&&
			(form_list->id > hold_form_id))
		{
			tmp_fptr = form_list;
			form_list = form_list->next;
			tmp_fptr->next = new_form_list;
			new_form_list = tmp_fptr;
		}

		/*
		 * Restore the element count in the form we started in
		 * because the subdoc may have left it half finished.
		 */
		if (form_list != NULL)
		{
			form_list->form_ele_cnt = hold_form_ele_cnt;
		}

		/*
		 * Merge the newly created forms with the old ones
		 * since the new ones may have partially complete
		 * data.
		 */
		while ((new_form_list != NULL)&&(hold_form_list != NULL))
		{
			tmp_fptr = lo_merge_form_data(new_form_list,
					hold_form_list);
			new_form_list = new_form_list->next;
			hold_form_list = hold_form_list->next;
			tmp_fptr->next = form_list;
			form_list = tmp_fptr;
		}

		/*
		 * Add back on forms that are from later subdocs.
		 */
		while (hold_form_list != NULL)
		{
			tmp_fptr = hold_form_list;
			hold_form_list = hold_form_list->next;
			tmp_fptr->next = form_list;
			form_list = tmp_fptr;
		}

		doc_lists->form_list = form_list;
		doc_lists->current_form_num = hold_current_form_num;
	}

	/*
	 * Restore in form status to what is
	 * was before this relayout.
	 */
	top_state->in_form = hold_in_form;

	if (top_state->savedData.FormList != NULL)
	{
		lo_SavedFormListData *all_form_ele;

		all_form_ele = top_state->savedData.FormList;
		all_form_ele->data_index = hold_form_data_index;
	}

	/*
	 * The master embed index is typically now be back
	 * to its original value, but might not be if we
	 * only had to relayout some (not all) of the embeds
	 * in this subdoc.  So, make sure it's set back to
	 * its original value here.
	 */
	top_state->embed_count = hold_embed_global_index;

	/* verify that the tag count is still correct */
	top_state->tag_count = original_tag_count;

	/*
	 * Likewise for the anchor list.
	 */
	doc_lists->url_list_len = hold_url_list_len;
	doc_lists->image_list_count = hold_image_list_count;
        doc_lists->applet_list_count = hold_applet_list_count;
        doc_lists->embed_list_count = hold_embed_list_count;
	top_state->current_layer_num = hold_current_layer_num;

	subdoc = NULL;
	if ((state != old_state)&&
		(state->current_ele != NULL))
	{
		subdoc = lo_EndCellSubDoc(context, state,
				old_state, state->current_ele, TRUE);
		state->sub_state = NULL;
		state->current_ele = NULL;
/*
		if ((state->is_a_subdoc == SUBDOC_CELL)||
			(state->is_a_subdoc == SUBDOC_CAPTION))
		{
			if (old_state->subdoc_tags_end != NULL)
			{
				state->subdoc_tags_end =
					old_state->subdoc_tags_end;
			}
		}
*/
	}

	old_state->must_relayout_subdoc = FALSE;

	state->top_state->force_reload = save_force;
	return(subdoc);
}


LO_CellStruct *
lo_RelayoutCell(MWContext *context, lo_DocState *state,
	LO_SubDocStruct *subdoc, lo_TableCell *cell_ptr,
	LO_CellStruct *cell, int32 width, Bool is_a_header,
	Bool relayout)
{
	LO_CellStruct *cell_ele;
	int32 first_height;
	lo_TopState *top_state;
	lo_DocState *old_state;
	lo_DocState *new_state;
	PA_Tag *tag_list;
	PA_Tag *tag_ptr;
	PA_Tag *tag_end_ptr;
	int32 doc_id;
	Bool allow_percent_width;
	Bool allow_percent_height;
	int32 hold_form_ele_cnt;
	int32 hold_form_data_index;
	intn hold_form_id;
	intn hold_current_form_num;
	Bool hold_in_form;
	lo_FormData *hold_form_list;
	int32 hold_embed_global_index;
	int32 original_tag_count;
	intn hold_url_list_len;
	NET_ReloadMethod save_force;
	Bool save_diff_state;
	int32 save_state_pushes;
	int32 save_state_pops;
	lo_DocLists *doc_lists;
	int32 hold_image_list_count;
        int32 hold_applet_list_count;
        int32 hold_embed_list_count;
	int32 hold_current_layer_num;
	LO_Element * elem_list;

	/*
	 * Relayout is NOT allowed to block on images, so never
	 * have the forced reload flag set.
	 */
	save_force = state->top_state->force_reload;
	state->top_state->force_reload = NET_CACHE_ONLY_RELOAD;

	top_state = state->top_state;
	
	/*
	 * We are going to reuse this subdoc element instead
	 * of allocating a new one.
	lo_reuse_current_subdoc(context, subdoc);
	subdoc->state = NULL;
	 */
	subdoc->width = width;
	subdoc->height = 0;

	subdoc->horiz_alignment = cell_ptr->horiz_alignment;

	doc_lists = lo_GetCurrentDocLists(state);
	
	/*
	 * If this subdoc contained form elements.  We want to reuse
	 * them without messing up the order of later form elements in
	 * other subdocs.
	 *
	 * We assume that relaying out the doc will create the exact
	 * same number of form elements in the same order as the first time.
	 * so we reset global form counters to before this subdoc
	 * was ever laid out, saving the current counts to restore later.
	 */
	if (doc_lists->form_list != NULL)
	{
		lo_FormData *form_list;
		lo_FormData *tmp_fptr;

		hold_form_id = cell_ptr->form_id;
		hold_current_form_num = doc_lists->current_form_num;
		form_list = doc_lists->form_list;
		hold_form_list = NULL;
		while ((form_list != NULL)&&
			(form_list->id > hold_form_id))
		{
			tmp_fptr = form_list;
			form_list = form_list->next;
			tmp_fptr->next = hold_form_list;
			hold_form_list = tmp_fptr;
		}
		doc_lists->form_list = form_list;
		doc_lists->current_form_num = hold_form_id;
		if (form_list != NULL)
		{
			hold_form_ele_cnt = form_list->form_ele_cnt;
			form_list->form_ele_cnt = cell_ptr->form_ele_cnt;
		}
		else
		{
			hold_form_ele_cnt = 0;
		}
	}
	else
	{
		hold_form_id = 0;
		hold_form_ele_cnt = 0;
		hold_form_list = NULL;
		hold_current_form_num = 0;
	}

	/*
	 * We're going to be deleting and then recreating the contents 
	 * of this subdoc.  We want the newly-created embeds to have
	 * the same embed indices as their recycled counterparts, so
	 * we reset the master embed index in the top_state to be the
	 * value it held at the time the old subdoc was created. When
	 * we're finished creating the new subdoc, the master index
	 * should be back to its original value (but just in case we
	 * save that value in a local variable here).
	 */
	hold_embed_global_index = top_state->embed_count;
	top_state->embed_count = cell_ptr->embed_count_base;

	/* same for tag count */
	original_tag_count = top_state->tag_count;
	top_state->tag_count = cell_ptr->beginning_tag_count;

	/*
	 * Same goes for anchors and images
	 */
	hold_url_list_len = doc_lists->url_list_len;
	doc_lists->url_list_len = cell_ptr->url_count_base;

	hold_image_list_count = doc_lists->image_list_count;
	doc_lists->image_list_count = cell_ptr->image_list_count_base;

	hold_applet_list_count = doc_lists->applet_list_count;
	doc_lists->applet_list_count = cell_ptr->applet_list_count_base;
	
	hold_embed_list_count = doc_lists->embed_list_count;
	doc_lists->embed_list_count = cell_ptr->embed_list_count_base;

	hold_current_layer_num = top_state->current_layer_num;
	top_state->current_layer_num = cell_ptr->current_layer_num_base;

	/*
	 * Save whether we are in a form or not, and restore
	 * to what we were when the subdoc was started.
	 */
	hold_in_form = top_state->in_form;
	top_state->in_form = cell_ptr->start_in_form;

	if (top_state->savedData.FormList != NULL)
	{
		lo_SavedFormListData *all_form_ele;

		all_form_ele = top_state->savedData.FormList;
		hold_form_data_index = all_form_ele->data_index;
		all_form_ele->data_index = cell_ptr->form_data_index;
	}
	else
	{
		hold_form_data_index = 0;
	}

	/*
	 * Still variable height, but we know the width now.
	 */
	first_height = FEUNITS_Y(10000, context);
	allow_percent_height = FALSE;
	allow_percent_width = TRUE;

	/*
	 * Get a new sub-state to create the new subdoc in.
	 */
	lo_reuse_current_state(context, subdoc->state,
		width, first_height, 0, 0, TRUE);
	new_state = subdoc->state;
	subdoc->state = NULL;


	if (new_state == NULL)
	{
		state->top_state->force_reload = save_force;
		return(cell);
	}
	if (cell_ptr->is_a_subdoc == SUBDOC_CAPTION)
	{
		new_state->is_a_subdoc = SUBDOC_CAPTION;
	}
	else
	{
		new_state->is_a_subdoc = SUBDOC_CELL;
	}

	lo_InheritParentState(context, new_state, state);
	new_state->allow_percent_width = allow_percent_width;
	new_state->allow_percent_height = allow_percent_height;
	/*
	 * In relaying out subdocs, we NEVER link back up subdoc tags
	 */
	new_state->subdoc_tags = NULL;
	new_state->subdoc_tags_end = NULL;

	/*
	 * Initialize other new state sundries.
	 */
	new_state->base_x = 0;
	new_state->base_y = 0;
	new_state->display_blocked = TRUE;
	if (subdoc->width == 0)
	{
		new_state->delay_align = TRUE;
	}

	new_state->win_left = 0;
	new_state->win_right = 0;
	new_state->x = new_state->win_left;
	new_state->y = 0;
	new_state->max_width = new_state->x + 1;
	new_state->left_margin = new_state->win_left;
	new_state->right_margin = new_state->win_width - new_state->win_right;
	new_state->list_stack->old_left_margin = new_state->left_margin;
	new_state->list_stack->old_right_margin = new_state->right_margin;
	
	/* we don't need min widths during relayout */
	new_state->need_min_width = FALSE;
    
    /* reset line_height stack */
    while(new_state->line_height_stack)
        lo_PopLineHeight(new_state);
    
    /*
	 * Initialize the alignment stack for this state
	 * to match the caption alignment.
	 */
	if (subdoc->horiz_alignment != LO_ALIGN_LEFT)
	{
		lo_PushAlignment(new_state, P_TABLE_DATA, subdoc->horiz_alignment);
	}

	state->sub_state = new_state;

	state->current_ele = (LO_Element *)subdoc;

	if (is_a_header != FALSE)
	{
		LO_TextAttr *old_attr;
		LO_TextAttr *attr;
		LO_TextAttr tmp_attr;

		old_attr = new_state->font_stack->text_attr;
		lo_CopyTextAttr(old_attr, &tmp_attr);
		tmp_attr.fontmask |= LO_FONT_BOLD;
		attr = lo_FetchTextAttr(new_state, &tmp_attr);

		lo_PushFont(new_state, P_TABLE_HEADER, attr);
	}

	/*
	 * Move our current state down into the new sub-state.
	 * Fetch the tag list from the old subdoc's state in preparation
	 * for relayout.
	 */
	
	state = new_state;
	/*
	tag_ptr = cell_ptr->subdoc_tags;
	tag_end_ptr = cell_ptr->subdoc_tags_end;
	cell_ptr->subdoc_tags = NULL;
	cell_ptr->subdoc_tags_end = NULL;
	
	if (tag_end_ptr != NULL)
	{
		tag_end_ptr = tag_end_ptr->next;
	}
	*/

	/*
	 * Copy some cell attribute state from the old cell to the subdoc
	 * so that we retain the correct state. Make sure that the bg_color
	 * doesn't get freed.
	 */
	subdoc->horiz_alignment = cell_ptr->horiz_alignment;
	subdoc->vert_alignment = cell_ptr->vert_alignment;
	subdoc->backdrop = cell->backdrop;
	/*
	cell->backdrop.bg_color = NULL;
	cell->backdrop.url = NULL;
	*/
	
	/*
	 * Clean up memory used by old cell.
	 */
	/*
	elem_list = cell->cell_list;
	cell->next = NULL;
	cell->cell_list = NULL;
	cell->cell_list_end = NULL;
	lo_relayout_recycle(context, state, (LO_Element *)cell);
	*/

	/*
	 * Relayout all the goodies
	 */
	/*
	lo_RelayoutTags(context, state, tag_ptr, tag_end_ptr, elem_list);
	*/

	/*
	 * Link the end of this subdoc's tag chain back to
	 * its parent.  The beginning should have never been
	 * unlinked from the parent.
	 */
	/*
	if (state->subdoc_tags_end != NULL)
	{
		state->subdoc_tags_end->next = tag_end_ptr;
	}
	*/

	/*
	 * makes sure we are at the bottom
	 * of everything in the document.
	 */

	lo_rl_ReflowCell( context, state, cell );
	
	/* Replaced with lo_EndLayoutDuringReflow()
	lo_CloseOutLayout(context, state);
	*/
	
	lo_EndLayoutDuringReflow( context, state );
	

	old_state = state;
	/*
	 * Get the unique document ID, and retreive this
	 * documents layout state.
	 */
	doc_id = XP_DOCID(context);
	top_state = lo_FetchTopState(doc_id);
	state = top_state->doc_state;

	while ((state->sub_state != NULL)&&
		(state->sub_state != old_state))
	{
		state = state->sub_state;
	}

	/*
	 * Ok, now reset the global form element counters
	 * to their original values.
	 */
	if ((doc_lists->form_list != NULL)||(hold_form_list != NULL))
	{
		lo_FormData *form_list;
		lo_FormData *new_form_list;
		lo_FormData *tmp_fptr;

		/*
		 * Remove any new forms created in this subdocs to a
		 * separate list.
		 */
		form_list = doc_lists->form_list;
		new_form_list = NULL;
		while ((form_list != NULL)&&
			(form_list->id > hold_form_id))
		{
			tmp_fptr = form_list;
			form_list = form_list->next;
			tmp_fptr->next = new_form_list;
			new_form_list = tmp_fptr;
		}

		/*
		 * Restore the element count in the form we started in
		 * because the subdoc may have left it half finished.
		 */
		if (form_list != NULL)
		{
			form_list->form_ele_cnt = hold_form_ele_cnt;
		}

		/*
		 * Merge the newly created forms with the old ones
		 * since the new ones may have partially complete
		 * data.
		 */
		while ((new_form_list != NULL)&&(hold_form_list != NULL))
		{
			tmp_fptr = lo_merge_form_data(new_form_list,
					hold_form_list);
			new_form_list = new_form_list->next;
			hold_form_list = hold_form_list->next;
			tmp_fptr->next = form_list;
			form_list = tmp_fptr;
		}

		/*
		 * Add back on forms that are from later subdocs.
		 */
		while (hold_form_list != NULL)
		{
			tmp_fptr = hold_form_list;
			hold_form_list = hold_form_list->next;
			tmp_fptr->next = form_list;
			form_list = tmp_fptr;
		}

		doc_lists->form_list = form_list;
		doc_lists->current_form_num = hold_current_form_num;
	}

	/*
	 * Restore in form status to what is
	 * was before this relayout.
	 */
	top_state->in_form = hold_in_form;

	if (top_state->savedData.FormList != NULL)
	{
		lo_SavedFormListData *all_form_ele;

		all_form_ele = top_state->savedData.FormList;
		all_form_ele->data_index = hold_form_data_index;
	}

	/*
	 * The master embed index is typically now be back
	 * to its original value, but might not be if we
	 * only had to relayout some (not all) of the embeds
	 * in this subdoc.  So, make sure it's set back to
	 * its original value here.
	 */
	top_state->embed_count = hold_embed_global_index;
	
	/* ditto for tag_count */
	top_state->tag_count = original_tag_count;

	/*
	 * Likewise for the anchor list and image list.
	 */
	doc_lists->url_list_len = hold_url_list_len;
	doc_lists->image_list_count = hold_image_list_count;
        doc_lists->applet_list_count = hold_applet_list_count;
        doc_lists->embed_list_count = hold_embed_list_count;
	top_state->current_layer_num = hold_current_layer_num;
	
	subdoc = NULL;
	cell_ele = NULL;
	if ((state != old_state)&&
		(state->current_ele != NULL))
	{
		lo_DocState *subdoc_state;
		int32 dx, dy;
		int32 base_x, base_y;

		subdoc = lo_EndCellSubDoc(context, state,
				old_state, state->current_ele, TRUE);
		state->sub_state = NULL;
		state->current_ele = NULL;
/*
		if ((state->is_a_subdoc == SUBDOC_CELL)||
			(state->is_a_subdoc == SUBDOC_CAPTION))
		{
			if (old_state->subdoc_tags_end != NULL)
			{
				state->subdoc_tags_end =
					old_state->subdoc_tags_end;
			}
		}
*/
		subdoc_state = (lo_DocState *)(subdoc->state);
		cell_ptr->start_in_form = subdoc_state->start_in_form;
		cell_ptr->form_id = subdoc_state->form_id;
		cell_ptr->form_ele_cnt = subdoc_state->form_ele_cnt;
		cell_ptr->form_data_index = subdoc_state->form_data_index;
		cell_ptr->embed_count_base = subdoc_state->embed_count_base;
		cell_ptr->url_count_base = subdoc_state->url_count_base;
		cell_ptr->image_list_count_base = subdoc_state->image_list_count_base;
		cell_ptr->applet_list_count_base = subdoc_state->applet_list_count_base;
		cell_ptr->embed_list_count_base = subdoc_state->embed_list_count_base;                
		cell_ptr->current_layer_num_base = subdoc_state->current_layer_num_base;
		cell_ptr->must_relayout = subdoc_state->must_relayout_subdoc;
		cell_ptr->is_a_subdoc = subdoc_state->is_a_subdoc;
		/*
		cell_ptr->subdoc_tags = subdoc_state->subdoc_tags;
		cell_ptr->subdoc_tags_end = subdoc_state->subdoc_tags_end;
		*/
		subdoc_state->subdoc_tags = NULL;
		subdoc_state->subdoc_tags_end = NULL;
		cell_ptr->max_y = subdoc_state->y;
		cell_ptr->horiz_alignment = subdoc->horiz_alignment;
		cell_ptr->vert_alignment = subdoc->vert_alignment;
			
		/*
		cell_ele = lo_SmallSquishSubDocToCell(context, state, subdoc,
				&dx, &dy);
		*/

		cell_ele = cell_ptr->cell;
		lo_CreateCellFromSubDoc(context, state, subdoc, cell_ele, &dx, &dy);

		lo_cleanup_old_state(subdoc->state);
		base_x = cell_ele->x + cell_ele->x_offset + cell_ele->border_width;
		base_y = cell_ele->y + cell_ele->y_offset + cell_ele->border_width;
		dx -= base_x;
		dy -= base_y;
		cell_ptr->cell_base_x = dx;
		cell_ptr->cell_base_y = dy;
	}

	XP_ASSERT ( cell_ele != NULL );

	old_state->must_relayout_subdoc = FALSE;

	state->top_state->force_reload = save_force;

	/* pop the style stack once to get rid of the extra dummy tag
	 * that we added to the stack to force styles on the begin
	 * subdoc in relayout
	 */
	if (relayout == FALSE)
	{
		LO_PopStyleTagByIndex(context, &state, P_TABLE_DATA, 0);
	}

	return(cell_ele);
}

/*
 * if normal borders is TRUE draw cell borders if the rest of
 * the table has borders.  If FALSE, never draw cell borders
 */
void
lo_BeginTableCellAttributes(MWContext *context, 
 							lo_DocState *state, 
 							lo_TableRec *table,
 							char * colspan_attr,
							char * rowspan_attr,
 							char * nowrap_attr,
 							char * bgcolor_attr,
 							char * background_attr,
                            lo_TileMode tile_mode,
 							char * valign_attr,
 							char * halign_attr,
  							char * width_attr,
							char * height_attr,
							Bool is_a_header,
							Bool normal_borders)
{
	lo_TableRow *table_row;
	lo_TableCell *table_cell;
	int32 val;
#ifdef LOCAL_DEBUG
 fprintf(stderr, "lo_BeginTableCell called\n");
#endif /* LOCAL_DEBUG */

	table_row = table->row_ptr;

	table_cell = XP_NEW(lo_TableCell);
	if (table_cell == NULL)
	{
		return;
	}

	if (state->is_a_subdoc != SUBDOC_NOT) 
	{
		table_cell->in_nested_table = TRUE;
	}
	else 
	{
		table_cell->in_nested_table = FALSE;
	}	

	/* Copied to lo_InitForBeginCell()
	table_cell->must_relayout = FALSE;
	*/
	table_cell->subdoc_tags = NULL;
	table_cell->subdoc_tags_end = NULL;

	/* Copied to lo_InitForBeginCell()
	table_cell->cell_done = FALSE;
	*/
	table_cell->nowrap = FALSE;
	table_cell->is_a_header = is_a_header;
	table_cell->percent_width = 0;
	table_cell->percent_height = 0;
	table_cell->specified_width = 0;
	table_cell->specified_height = 0;
	table_cell->min_width = 0;
	table_cell->max_width = 0;
	table_cell->height = 0;
	table_cell->baseline = 0;
	table_cell->rowspan = 1;
	table_cell->colspan = 1;
	table_cell->cell = NULL;
	table_cell->next = NULL;
	table_cell->beginning_tag_count = state->top_state->tag_count;

	lo_InitForBeginCell( table_row, table_cell );
	
    if (table->current_subdoc == NULL)
	{
		XP_DELETE(table_cell);
		return;
	}

	if (colspan_attr)
	{
		val = XP_ATOI(colspan_attr);
		if (val < 1)
		{
			val = 1;
		}
		/*
		 * Prevent huge colspan
		 */
		if (val > TABLE_MAX_COLSPAN)
		{
			val = TABLE_MAX_COLSPAN;
		}
		table_cell->colspan = val;
	}

	if (rowspan_attr)
	{
		val = XP_ATOI(rowspan_attr);
		if (val < 1)
		{
			val = 1;
		}
		/*
		 * Prevent huge rowspan
		 */
		if (val > TABLE_MAX_ROWSPAN)
		{
			val = TABLE_MAX_ROWSPAN;
		}
		table_cell->rowspan = val;
	}

	if (nowrap_attr)
	{
		table_cell->nowrap = TRUE;
	}

	/* Copied to lo_InitForBeginCell()
	if (table_row->cell_list == NULL)
	{
		table_row->cell_list = table_cell;
		table_row->cell_ptr = table_cell;
	}
	else
	{
		table_row->cell_ptr->next = table_cell;
		table_row->cell_ptr = table_cell;
	}
	*/

	lo_BeginCellSubDoc(context, 
						state, 
						table,
						bgcolor_attr,
                        background_attr,
                        tile_mode,
						valign_attr,
						halign_attr,
						width_attr,
						height_attr,
 						is_a_header, 
						normal_borders ? table->draw_borders : 0,
						FALSE);

	/*
	 * We added a new state.
	 */
	lo_PushStateLevel ( context );
}


void
lo_BeginTableCell(MWContext *context, lo_DocState *state, lo_TableRec *table,
	PA_Tag *tag, Bool is_a_header)
{
	char *colspan_attr = (char*)lo_FetchParamValue(context, tag, PARAM_COLSPAN);
	char *rowspan_attr = (char*)lo_FetchParamValue(context, tag, PARAM_ROWSPAN);
	char *nowrap_attr  = (char*)lo_FetchParamValue(context, tag, PARAM_NOWRAP);
	char *bgcolor_attr = (char*)lo_FetchParamValue(context, tag, PARAM_BGCOLOR);
	char *background_attr= (char*)lo_FetchParamValue(context, tag, PARAM_BACKGROUND);
	char *valign_attr  = (char*)lo_FetchParamValue(context, tag, PARAM_VALIGN);
	char *halign_attr  = (char*)lo_FetchParamValue(context, tag, PARAM_ALIGN);
	char *width_attr   = (char*)lo_FetchParamValue(context, tag, PARAM_WIDTH);
	char *height_attr  = (char*)lo_FetchParamValue(context, tag, PARAM_HEIGHT);

	/* remove the PA_LOCK stuff */

	lo_BeginTableCellAttributes(context, 
								state, 
								table,
								colspan_attr,
								rowspan_attr,
								nowrap_attr,
								bgcolor_attr,
                                background_attr,
                                LO_TILE_BOTH, /* Default backdrop tile mode */
								valign_attr,
								halign_attr,
								width_attr,
								height_attr,
								is_a_header,
								TRUE);

	if(colspan_attr)
		PA_FREE(colspan_attr);
	if(rowspan_attr)
		PA_FREE(rowspan_attr);
	if(nowrap_attr)
		PA_FREE(nowrap_attr);
	if(bgcolor_attr)
		PA_FREE(bgcolor_attr);
	if(background_attr)
		PA_FREE(background_attr);
	if(valign_attr)
		PA_FREE(valign_attr);
	if(halign_attr)
		PA_FREE(halign_attr);
	if(width_attr)
		PA_FREE(width_attr);
	if(height_attr)
		PA_FREE(height_attr);
}


void
lo_EndTableCell(MWContext *context, lo_DocState *state, Bool relayout )
{
	LO_SubDocStruct *subdoc;
	LO_CellStruct *cell_ele;
	lo_TableRec *table;
	lo_TableRow *table_row;
	lo_TableCell *table_cell;
	lo_table_span *span_rec;
	int32 top_inner_pad;
	int32 bottom_inner_pad;
	int32 left_inner_pad;
	int32 right_inner_pad;
	int32 i;
	lo_TopState *top_state;
	lo_DocState *old_state;
	int32 doc_id;
#ifdef LOCAL_DEBUG
 fprintf(stderr, "lo_EndTableCell called\n");
#endif /* LOCAL_DEBUG */

	/*
	 * makes sure we are at the bottom
	 * of everything in the document.
	 */
	if (relayout == FALSE) 
	{
		lo_CloseOutLayout(context, state);
	}
	else
	{
		lo_EndLayoutDuringReflow( context, state );
	}

	old_state = state;
	/*
	 * Get the unique document ID, and retrieve this
	 * documents layout state.
	 */
	doc_id = XP_DOCID(context);
	top_state = lo_FetchTopState(doc_id);
	state = top_state->doc_state;

	while ((state->sub_state != NULL)&&
		(state->sub_state != old_state))
	{
		state = state->sub_state;
	}

	subdoc = NULL;
	cell_ele = NULL;
	if ((state != old_state)&&
		(state->current_ele != NULL))
	{
		subdoc = lo_EndCellSubDoc(context, state,
				old_state, state->current_ele, relayout);
		state->sub_state = NULL;
		state->current_ele = NULL;
		if ((state->is_a_subdoc == SUBDOC_CELL)||
			(state->is_a_subdoc == SUBDOC_CAPTION))
		{
			if (old_state->subdoc_tags_end != NULL)
			{
				state->subdoc_tags_end =
					old_state->subdoc_tags_end;
			}
		}
		else
		{
		}
	}

	/*
	 * We popped a state level.
	 */
	lo_PopStateLevel ( context );

	table = state->current_table;
	table_row = table->row_ptr;
	table_cell = table_row->cell_ptr;
	
	top_inner_pad = FEUNITS_X(table->inner_top_pad, context);
	bottom_inner_pad = FEUNITS_X(table->inner_bottom_pad, context);
	left_inner_pad = FEUNITS_X(table->inner_left_pad, context);
	right_inner_pad = FEUNITS_X(table->inner_right_pad, context);

	if (subdoc != NULL)
	{
		int32 subdoc_width;
		int32 subdoc_height;
		int32 min_cellwidth;
		int32 base_x, base_y;
		int32 dx, dy;
		int32 cell_internal_width;
		lo_DocState *subdoc_state;
		Bool reset_min_width;
		
		reset_min_width = FALSE;
		
		cell_internal_width = old_state->max_width;
		
		/* how big do we think this cell should be? */
		min_cellwidth = old_state->min_width;

		/*
		* If the cell is smaller than this, we need to grow it so that
		* any internal elements don't overwrite the cell borders.
		*/
		if (subdoc->width < min_cellwidth)
		{
			/*
			* We need to lay things out again as we now have more space.
			* Do we want to be smart and see if we contain things that
			* will change?
			 * Be sure to add in the width of the borders as later code expects it
			*/
			subdoc->width = min_cellwidth + (2 * subdoc->border_width);
			old_state->must_relayout_subdoc = TRUE;
			
			cell_internal_width = min_cellwidth;
		}

		subdoc_width = subdoc->width +
			(2 * subdoc->border_horiz_space) +
			(left_inner_pad + right_inner_pad);
		subdoc_height = subdoc->height +
			(2 * subdoc->border_vert_space) +
			(top_inner_pad + bottom_inner_pad);
		table_cell->max_width = subdoc_width;
		table_cell->min_width = old_state->min_width +
			(2 * subdoc->border_width) +
			(2 * subdoc->border_horiz_space) +
			(left_inner_pad + right_inner_pad);

		/*
		 * This is some nasty logic. We need to reset the min_width
		 * of the cell in cases where we're certain about how large
		 * the cell is.
		 */
		if ( (table_cell->min_width < table_cell->max_width) )
		{
			/*
			 * If we're a fixed width cell and fixed layout (COLS) is on.
			 * BRAIN DAMAGE: It's arguable that we should always do this - a width for
			 * the cell was specified, we should respect it. However, certain popular
			 * sites don't look too good if we do...
			 */
			if ( (table_cell->specified_width > 0) &&
				 (table_cell->percent_width == 0) &&
				 (table->fixed_cols > 0) )
			{
				reset_min_width = TRUE;
			}
			else
			/*
			 * If we're in a fixed width table who's width does not
			 * depend on it's parent and are using fixed layout (COLS).
			 */
			if ( (table->fixed_cols > 0) &&
				 (table->table_width_fixed != FALSE) )
			{
				reset_min_width = TRUE;
			}
		}
		
		/*
		 * If nowrap is on or min_width ended up being bigger than
		 * max_width
		 */
		if ((table_cell->nowrap != FALSE)||
			(table_cell->min_width > table_cell->max_width))
		{
			reset_min_width = TRUE;
		}
		
		if ( reset_min_width != FALSE )
		{
			table_cell->min_width = table_cell->max_width;
		}

		/*
		 * If we're in the fixed layout case, then we may need to grow this
		 * column to fit this new cell width. This will allow any subsequent
		 * columns to be layed out to the correct size and not have to be
		 * relayed out.
		 */
		if ( table->fixed_cols > 0 )
		{
			lo_SetDefaultCellWidth( context, table, subdoc, table_cell, cell_internal_width );
		}
				
		table_cell->height = subdoc_height;
		table_cell->baseline = lo_GetSubDocBaseline(subdoc);

		subdoc_state = (lo_DocState *)(subdoc->state);
		table_cell->start_in_form = subdoc_state->start_in_form;
		table_cell->form_id = subdoc_state->form_id;
		table_cell->form_ele_cnt = subdoc_state->form_ele_cnt;
		table_cell->form_data_index = subdoc_state->form_data_index;
		table_cell->embed_count_base = subdoc_state->embed_count_base;
		table_cell->url_count_base = subdoc_state->url_count_base;
		table_cell->image_list_count_base = subdoc_state->image_list_count_base;
		table_cell->applet_list_count_base = subdoc_state->applet_list_count_base;
		table_cell->embed_list_count_base = subdoc_state->embed_list_count_base;

		table_cell->current_layer_num_base = subdoc_state->current_layer_num_base;
		table_cell->must_relayout = subdoc_state->must_relayout_subdoc;
		table_cell->is_a_subdoc = subdoc_state->is_a_subdoc;
		if (relayout == FALSE)
		{
			table_cell->subdoc_tags = subdoc_state->subdoc_tags;
			table_cell->subdoc_tags_end = subdoc_state->subdoc_tags_end;
		}
		subdoc_state->subdoc_tags = NULL;
		subdoc_state->subdoc_tags_end = NULL;
		table_cell->max_y = subdoc_state->y;
		table_cell->horiz_alignment = subdoc->horiz_alignment;
		table_cell->vert_alignment = subdoc->vert_alignment;

		if (relayout == FALSE)
		{
			cell_ele = lo_SmallSquishSubDocToCell(context, state, subdoc,
				&dx, &dy);
			/* Keep ptr. back to table state structs for relayout */
			cell_ele->table_cell = table_cell;
			cell_ele->table_row = table_row;
			cell_ele->table = table;
		}
		else 
		{
			cell_ele = table_cell->cell;
			lo_CreateCellFromSubDoc(context, state, subdoc, cell_ele, &dx, &dy);
		}

		lo_cleanup_old_state(subdoc->state);
		base_x = cell_ele->x + cell_ele->x_offset + cell_ele->border_width;
		base_y = cell_ele->y + cell_ele->y_offset + cell_ele->border_width;
		dx -= base_x;
		dy -= base_y;
		table_cell->cell_base_x = dx;
		table_cell->cell_base_y = dy;
	}

	table_cell->cell = cell_ele;
	table_cell->cell_done = TRUE;

	i = 0;
	while (i < table_cell->colspan)
	{

		if ((table->width_spans == NULL)||
		    ((table->width_span_ptr != NULL)&&
		     (table->width_span_ptr->next == NULL)))
		{
			span_rec = XP_NEW(lo_table_span);
			if (span_rec == NULL)
			{
				return;
			}
			span_rec->dim = 1;
			span_rec->min_dim = 1;
			span_rec->span = 0;
			span_rec->next = NULL;

			if (table->width_spans == NULL)
			{
				table->width_spans = span_rec;
				table->width_span_ptr = span_rec;
			}
			else
			{
				table->width_span_ptr->next = span_rec;
				table->width_span_ptr = span_rec;
			}
		}
		else
		{
			if (table->width_span_ptr == NULL)
			{
				table->width_span_ptr = table->width_spans;
			}
			else
			{
				table->width_span_ptr =
					table->width_span_ptr->next;
			}
			span_rec = table->width_span_ptr;
		}

		if (span_rec->span > 0)
		{
			span_rec->span--;
			table_row->cells++;

			/*
			 * This only happens if a colspan on this
			 * row overlaps a rowspan from a previous row.
			 */
			if (i > 0)
			{
				int32 new_span;

				new_span = table_cell->rowspan - 1;
				if (new_span > span_rec->span)
				{
					span_rec->span = new_span;
				}
				i++;
				/*
				 * Don't count this cell twice.
				 */
				table_row->cells--;
			}
			continue;
		}
		else
		{
			span_rec->span = table_cell->rowspan - 1;
			i++;
			if (table_cell->colspan == 1)
			{
				if (table_cell->max_width > span_rec->dim)
				{
				    span_rec->dim = table_cell->max_width;
				}
				if (table_cell->min_width > span_rec->min_dim)
				{
				    span_rec->min_dim = table_cell->min_width;
				}
			}
		}
	}

	span_rec = table->height_span_ptr;
	if (table_cell->rowspan == 1)
	{
		int32 tmp_val;

		/*
		 * If the user specified a height for the cell,
		 * that becomes our min height.
		 */
		if ((table_cell->specified_height > 0)&&
			(table_cell->specified_height > span_rec->min_dim))
		{
			span_rec->min_dim = table_cell->specified_height;
		}

		tmp_val = table_cell->baseline - span_rec->min_dim;
		if (tmp_val > 0)
		{
			span_rec->min_dim += tmp_val;
			if (table_row->vert_alignment == LO_ALIGN_BASELINE)
			{
				span_rec->dim += tmp_val;
			}
		}

		if (table_row->vert_alignment == LO_ALIGN_BASELINE)
		{
			tmp_val = (table_cell->height - table_cell->baseline) -
				(span_rec->dim - span_rec->min_dim);
			if (tmp_val > 0)
			{
				span_rec->dim += tmp_val;
			}
		}
		else
		{
			if (table_cell->height > span_rec->dim)
			{
				span_rec->dim = table_cell->height;
			}
		}
	}

	table_row->cells += table_cell->colspan;
}


void
lo_BeginTableCaption(MWContext *context, lo_DocState *state, lo_TableRec *table,
	PA_Tag *tag)
{
	lo_TableCaption *caption;
	PA_Block buff;
	char *str;

	caption = XP_NEW(lo_TableCaption);
	if (caption == NULL)
	{
		return;
	}

	caption->vert_alignment = LO_ALIGN_TOP;
	caption->horiz_alignment = LO_ALIGN_CENTER;
	caption->min_width = 0;
	caption->max_width = 0;
	caption->height = 0;
	caption->subdoc = NULL;
	caption->cell_ele = NULL;

	/*
	 * Check for an align parameter
 	 */
	buff = lo_FetchParamValue(context, tag, PARAM_ALIGN);
	if (buff != NULL)
	{
		PA_LOCK(str, char *, buff);
		if (pa_TagEqual("bottom", str))
		{
			caption->vert_alignment = LO_ALIGN_BOTTOM;
		}
		PA_UNLOCK(buff);
		PA_FREE(buff);
	}

	table->caption = caption;

	lo_BeginCaptionSubDoc(context, state, caption, tag);
}


void
lo_EndTableCaption(MWContext *context, lo_DocState *state)
{
	LO_SubDocStruct *subdoc;
	lo_TableRec *table;
	lo_TableCaption *caption;
	lo_TopState *top_state;
	lo_DocState *old_state;
	int32 doc_id;
	int32 top_inner_pad;
	int32 bottom_inner_pad;
	int32 left_inner_pad;
	int32 right_inner_pad;

	/*
	 * makes sure we are at the bottom
	 * of everything in the document.
	 */
	lo_CloseOutLayout(context, state);

	old_state = state;
	/*
	 * Get the unique document ID, and retreive this
	 * documents layout state.
	 */
	doc_id = XP_DOCID(context);
	top_state = lo_FetchTopState(doc_id);
	state = top_state->doc_state;

	while ((state->sub_state != NULL)&&
		(state->sub_state != old_state))
	{
		state = state->sub_state;
	}

	subdoc = NULL;
	if ((state != old_state)&&
		(state->current_ele != NULL))
	{
		subdoc = lo_EndCellSubDoc(context, state,
				old_state, state->current_ele, FALSE);
		state->sub_state = NULL;
		state->current_ele = NULL;
		if ((state->is_a_subdoc == SUBDOC_CELL)||
			(state->is_a_subdoc == SUBDOC_CAPTION))
		{
			if (old_state->subdoc_tags_end != NULL)
			{
				state->subdoc_tags_end =
					old_state->subdoc_tags_end;
			}
		}
	}

	table = state->current_table;
	caption = table->caption;

	top_inner_pad = FEUNITS_X(table->inner_top_pad, context);
	bottom_inner_pad = FEUNITS_X(table->inner_bottom_pad, context);
	left_inner_pad = FEUNITS_X(table->inner_left_pad, context);
	right_inner_pad = FEUNITS_X(table->inner_right_pad, context);

	if (subdoc != NULL)
	{
		int32 subdoc_width;
		int32 subdoc_height;

		subdoc_width = subdoc->width +
			(2 * subdoc->border_horiz_space) +
			(left_inner_pad + right_inner_pad);
		subdoc_height = subdoc->height +
			(2 * subdoc->border_vert_space) + 
			(top_inner_pad + bottom_inner_pad);
		caption->max_width = subdoc_width;
		caption->min_width = old_state->min_width +
			(2 * subdoc->border_width) +
			(2 * subdoc->border_horiz_space) +
			(left_inner_pad + right_inner_pad);
		if (caption->min_width > caption->max_width)
		{
			caption->min_width = caption->max_width;
		}
		caption->height = subdoc_height;
	}

	caption->subdoc = subdoc;

	/* If the caption is empty, free the memory used by it */
	if (lo_subdoc_has_elements(caption->subdoc->state) == FALSE)
	{
		lo_FreeTableCaption( context, state, table );
	}
	
	/*
	 * We popped a state level.
	 */
	lo_PopStateLevel ( context );
}


void
lo_BeginTableRowAttributes(MWContext *context, 
				 			lo_DocState *state, 
				 			lo_TableRec *table,
				 			char *bgcolor_attr,
                            char *background_attr,
				 			char *valign_attr,
				 			char *halign_attr)
{
	lo_TableRow *table_row;
	lo_table_span *span_rec;
	char *bgcolor_from_style=NULL;
#ifdef LOCAL_DEBUG
 fprintf(stderr, "lo_BeginTableRow called\n");
#endif /* LOCAL_DEBUG */

	table_row = XP_NEW(lo_TableRow);
	if (table_row == NULL)
	{
		return;
	}

	/* copied to lo_UpdateTableStateForBeginRow()
	table_row->row_done = FALSE; 
	*/
	table_row->has_percent_width_cells = FALSE;
	table_row->backdrop.bg_color = NULL;
    table_row->backdrop.url = NULL;
    table_row->backdrop.tile_mode = LO_TILE_BOTH;
	/* copied to lo_UpdateTableStateForBeginRow()
	table_row->cells = 0;
	*/
	table_row->vert_alignment = LO_ALIGN_DEFAULT;
	table_row->horiz_alignment = LO_ALIGN_DEFAULT;

	/* copied to lo_UpdateTableStateForBeginRow()
	table_row->cell_list = NULL;
	table_row->cell_ptr = NULL;
	*/
	table_row->next = NULL;
	
	
	lo_UpdateTableStateForBeginRow( table, table_row );

	/* check for style sheet color and override if necessary */
	if(state->top_state && state->top_state->style_stack)
	{
		StyleStruct *style_struct;

		style_struct = STYLESTACK_GetStyleByIndex(
											state->top_state->style_stack,
											0);

		if(style_struct)
		{
			bgcolor_from_style = STYLESTRUCT_GetString(style_struct, BG_COLOR_STYLE);

			if(bgcolor_from_style)
			{
				bgcolor_attr = bgcolor_from_style;
			}
		}
	}

	/*
	 * Check for a background color attribute
	 */
	if (bgcolor_attr)
	{
		uint8 red, green, blue;
		XP_Bool rv;

		if(bgcolor_from_style)
			rv = LO_ParseStyleSheetRGB(bgcolor_attr, &red, &green, &blue);
		else
			rv = LO_ParseRGB(bgcolor_attr, &red, &green, &blue);

		if(rv)
		{
			table_row->backdrop.bg_color = XP_NEW(LO_Color);
			if (table_row->backdrop.bg_color != NULL)
			{
				table_row->backdrop.bg_color->red = red;
				table_row->backdrop.bg_color->green = green;
				table_row->backdrop.bg_color->blue = blue;
			}
		}
	}

    if (background_attr)
        table_row->backdrop.url = XP_STRDUP(background_attr);

	XP_FREEIF(bgcolor_from_style);


	/*
	 * Check for a vertical align parameter
	 */
	if (valign_attr)
	{
		table_row->vert_alignment = lo_EvalVAlignParam(valign_attr);
	}

	/*
	 * Check for a horizontal align parameter
	 */
	if (halign_attr)
	{
		table_row->horiz_alignment = lo_EvalCellAlignParam(halign_attr);
	}

	
	if (table->row_list == NULL)
	{
		table->row_list = table_row;
		table->row_ptr = table_row;
	}
	else
	{
		table->row_ptr->next = table_row;
		table->row_ptr = table_row;
	}

	/* copied to lo_UpdateTableStateForBeginRow()
	table->width_span_ptr = NULL;
	*/

	span_rec = XP_NEW(lo_table_span);
	if (span_rec == NULL)
	{
		return;
	}
	
	if (table->height_spans == NULL)
	{
		table->height_spans = span_rec;
		table->height_span_ptr = span_rec;
	}
	else
	{
		table->height_span_ptr->next = span_rec;
		table->height_span_ptr = span_rec;
	}

	span_rec->dim = 1;
	/*
	 * Since min_dim on the heights is never used.
	 * I am appropriating it to do baseline aligning.  It will
	 * start as 0, and eventually be the amount of baseline needed
	 * to align all these cells in this row
	 * by their baselines.
	 */
	span_rec->min_dim = 0;
	span_rec->span = 0;
	span_rec->next = NULL;
}


void
lo_BeginTableRow(MWContext *context, lo_DocState *state, lo_TableRec *table,
	PA_Tag *tag)
{
	char *bgcolor_attr = (char*)lo_FetchParamValue(context, tag, PARAM_BGCOLOR);
	char *background_attr = (char*)lo_FetchParamValue(context, tag, PARAM_BACKGROUND);
	char *valign_attr  = (char*)lo_FetchParamValue(context, tag, PARAM_VALIGN);
	char *halign_attr  = (char*)lo_FetchParamValue(context, tag, PARAM_ALIGN);

	/* remove the PA_LOCK stuff */

	lo_BeginTableRowAttributes(context,
								state, 
								table,
								bgcolor_attr,
                                background_attr,
								valign_attr,
								halign_attr);

	if(bgcolor_attr)
		PA_FREE(bgcolor_attr);
	if(valign_attr)
		PA_FREE(valign_attr);
	if(halign_attr)
		PA_FREE(halign_attr);
}


void
lo_EndTableRow(MWContext *context, lo_DocState *state, lo_TableRec *table)
{
	lo_TableRow *table_row;
#ifdef LOCAL_DEBUG
 fprintf(stderr, "lo_EndTableRow called\n");
#endif /* LOCAL_DEBUG */

	table_row = table->row_ptr;
	table_row->row_done = TRUE;
	table->rows++;

	while (table->width_span_ptr != NULL)
	{
		table->width_span_ptr = table->width_span_ptr->next;
		if (table->width_span_ptr != NULL)
		{
			table->width_span_ptr->span--;
			table_row->cells++;
		}
	}

	if (table_row->cells > table->cols)
	{
		table->cols = table_row->cells;
	}
	
	/*
	 * We've hit the end of a row, if it's the first row and
	 * we haven't allocated all columns yet and table width
	 * is remaining, then set their size now.
	 */
	if ( table->fixed_width_remaining > 0 )
	{
		if ( (table_row->cells < table->fixed_cols) && (table->fixed_col_widths != NULL) )
		{
			int32 count;
			int32 colwidth;
			int32 cell_extra_space;
			int32 left_inner_pad;
			int32 right_inner_pad;
			
			/* how much border/pad space do we need per cell */
			if (table->draw_borders == TABLE_BORDERS_OFF)
			{
				cell_extra_space = 2 * TABLE_DEF_CELL_BORDER;
			}
			else if (table->draw_borders == TABLE_BORDERS_ON)
			{
				cell_extra_space = 2 * TABLE_DEF_CELL_BORDER;
			}
			else
			{
				cell_extra_space = 0;
			}
			
			left_inner_pad = FEUNITS_X(table->inner_left_pad, context);
			right_inner_pad = FEUNITS_X(table->inner_right_pad, context);
			cell_extra_space += left_inner_pad + right_inner_pad;
			
			/* divide space up to the remaining cols */
			colwidth = ( table->fixed_width_remaining / 
				( table->fixed_cols - table_row->cells )) - cell_extra_space;
			
			if ( colwidth < 0 )
			{
				colwidth = 0;
			}
			
			for ( count = table_row->cells; count < table->fixed_cols; ++count )
			{
				table->fixed_col_widths[ count ] = colwidth;
				table->fixed_width_remaining -= colwidth;
			}
			
			/* add any leftover space to the last row */
			table->fixed_col_widths[ table->fixed_cols - 1 ] += 
				table->fixed_width_remaining;
		}
		table->fixed_width_remaining = 0;
	}
}

void
lo_BeginTableAttributes(MWContext *context, 
						lo_DocState *state, 
						char *align_attr,
						char *border_attr,
						char *border_top_attr,
						char *border_bottom_attr,
						char *border_left_attr,
						char *border_right_attr,
						char *border_color_attr,
						char *border_style_attr,
						char *vspace_attr,
						char *hspace_attr,
						char *bgcolor_attr,
						char *background_attr,
						char *width_attr,
						char *height_attr,
						char *cellpad_attr,
						char *toppad_attr,
						char *bottompad_attr,
						char *leftpad_attr,
						char *rightpad_attr,
						char *cellspace_attr,
						char *cols_attr)
{
	lo_TableRec *table;
	LO_TableStruct *table_ele;
	int32 val;
	char *bgcolor_from_style = NULL;
	Bool allow_percent_width;
	Bool allow_percent_height;
#ifdef LOCAL_DEBUG
 fprintf(stderr, "lo_BeginTable called\n");
#endif /* LOCAL_DEBUG */

	if (state == NULL)
	{
		return;
	}

	table_ele = (LO_TableStruct *)lo_NewElement(context, state, LO_TABLE, NULL, 0);
	if (table_ele == NULL)
	{
		state->top_state->out_of_memory = TRUE;
		return;
	}

	table_ele->type = LO_TABLE;
	/* Copied into lo_PositionTableElement() */
	/*
	table_ele->ele_id = NEXT_ELEMENT;
	table_ele->x = state->x;
	table_ele->x_offset = 0;
	table_ele->y = state->y;
	table_ele->y_offset = 0;
	table_ele->width = 0;
	table_ele->height = 0;
	table_ele->line_height = 0;
	*/
	
	table_ele->FE_Data = NULL;
	table_ele->anchor_href = state->current_anchor;

	/*
	 * Default to the current alignment
	 */
	if ( state->align_stack != NULL )
	{
		table_ele->alignment = state->align_stack->alignment;
	}
	else
	{
		table_ele->alignment = LO_ALIGN_LEFT;
	}

	table_ele->border_width = TABLE_DEF_BORDER;
	table_ele->border_top_width = TABLE_DEF_BORDER;
	table_ele->border_bottom_width = TABLE_DEF_BORDER;
	table_ele->border_left_width = TABLE_DEF_BORDER;
	table_ele->border_right_width = TABLE_DEF_BORDER;
	table_ele->border_style = TABLE_DEF_BORDER_STYLE;

	table_ele->border_vert_space = TABLE_DEF_VERTICAL_SPACE;
	table_ele->border_horiz_space = TABLE_DEF_HORIZONTAL_SPACE;

	table_ele->border_color.red = 0;
	table_ele->border_color.green = 0;
	table_ele->border_color.blue = 0;

	table_ele->ele_attrmask = 0;

	table_ele->sel_start = -1;
	table_ele->sel_end = -1;

	/* Copied into lo_PositionTableElement() */
	/*
	table_ele->next = NULL;
	table_ele->prev = NULL;
	*/

	/*
	 * Check for an align parameter
	 */
	if (align_attr)
	{
		Bool floating;

		floating = FALSE;
		table_ele->alignment = lo_EvalAlignParam(align_attr, &floating);
		/*
		 * Only allow left and right (floating) and center.
		 */
		if (floating != FALSE)
		{
			table_ele->ele_attrmask |= LO_ELE_FLOATING;
		}
		else
		{
			/*
			 * Tables can be left, center, right, justify or char alignment.
			 * We only do left, center and right. True left and right will
			 * hit the floating case above. So, we set all alignments to
			 * be either center or left.
			 */
			switch ( table_ele->alignment )
			{
				case LO_ALIGN_LEFT:
				case LO_ALIGN_RIGHT:
				case LO_ALIGN_CENTER:
					break;
					
				case LO_ALIGN_NCSA_CENTER:
					table_ele->alignment = LO_ALIGN_CENTER;
					break;
				
				default:
					table_ele->alignment = LO_ALIGN_LEFT;
					break;
			}
		}
	}

	lo_PositionTableElement(state, table_ele);

	/*
	 * Push our alignment state if we're not floating
	 */

	/* Copied into lo_PositionTableElement() */
	/*
	if ( !(table_ele->ele_attrmask & LO_ELE_FLOATING) )
	{
		lo_PushAlignment(state, P_TABLE_DATA, table_ele->alignment);
	}
	*/

	/*
	 * Get the border parameter.
	 */
	if (border_attr)
	{
		val = XP_ATOI(border_attr);
		if ((val == 0)&&(*border_attr == '0'))
		{
			val = -1;
		}
		else if (val < 1)
		{
			val = 1;
		}
		table_ele->border_width = val;
		table_ele->border_top_width = val;
		table_ele->border_bottom_width = val;
		table_ele->border_left_width = val;
		table_ele->border_right_width = val;
	}
	table_ele->border_width = FEUNITS_X(table_ele->border_width, context);

	/*
	 * Get the top border parameter.
	 */
	if (border_top_attr)
	{
		val = XP_ATOI(border_top_attr);
		if ((val == 0)&&(*border_top_attr == '0'))
		{
			val = -1;
		}
		else if (val < 1)
		{
			val = 1;
		}
		table_ele->border_top_width = val;
	}
	table_ele->border_top_width = FEUNITS_Y(table_ele->border_top_width, context);
	
	/*
	 * Get the bottom border parameter.
	 */
	if (border_bottom_attr)
	{
		val = XP_ATOI(border_bottom_attr);
		if ((val == 0)&&(*border_bottom_attr == '0'))
		{
			val = -1;
		}
		else if (val < 1)
		{
			val = 1;
		}
		table_ele->border_bottom_width = val;
	}
	table_ele->border_bottom_width = FEUNITS_Y(table_ele->border_bottom_width, context);
	
	/*
	 * Get the left border parameter.
	 */
	if (border_left_attr)
	{
		val = XP_ATOI(border_left_attr);
		if ((val == 0)&&(*border_left_attr == '0'))
		{
			val = -1;
		}
		else if (val < 1)
		{
			val = 1;
		}
		table_ele->border_left_width = val;
	}
	table_ele->border_left_width = FEUNITS_X(table_ele->border_left_width, context);
	
	/*
	 * Get the right border parameter.
	 */
	if (border_right_attr)
	{
		val = XP_ATOI(border_right_attr);
		if ((val == 0)&&(*border_right_attr == '0'))
		{
			val = -1;
		}
		else if (val < 1)
		{
			val = 1;
		}
		table_ele->border_right_width = val;
	}
	table_ele->border_right_width = FEUNITS_X(table_ele->border_right_width, context);

	/*
	 * Get the border style parameter.
	 */
	if (border_style_attr)
	{
		int32 border_style;
		
		border_style = BORDER_OUTSET;
		if ( pa_TagEqual("none", border_style_attr) )
		{
			border_style = BORDER_NONE;
		}
		else if ( pa_TagEqual("dotted", border_style_attr) )
		{
			border_style = BORDER_DOTTED;
		}
		else if ( pa_TagEqual("dashed", border_style_attr) )
		{
			border_style = BORDER_DASHED;
		}
		else if ( pa_TagEqual("solid", border_style_attr) )
		{
			border_style = BORDER_SOLID;
		}
		else if ( pa_TagEqual("double", border_style_attr) )
		{
			border_style = BORDER_DOUBLE;
		}
		else if ( pa_TagEqual("groove", border_style_attr) )
		{
			border_style = BORDER_GROOVE;
		}
		else if ( pa_TagEqual("ridge", border_style_attr) )
		{
			border_style = BORDER_RIDGE;
		}
		else if ( pa_TagEqual("inset", border_style_attr) )
		{
			border_style = BORDER_INSET;
		}
		else if ( pa_TagEqual("outset", border_style_attr) )
		{
			border_style = BORDER_OUTSET;
		}
		
		table_ele->border_style = border_style;
	}

	/*
	 * Get the border color parameter.
	 */
	if (border_color_attr)
	{
		uint8 red, green, blue;

		LO_ParseStyleSheetRGB(border_color_attr, &red, &green, &blue);
		table_ele->border_color.red = red;
		table_ele->border_color.green = green;
		table_ele->border_color.blue = blue;
	}
	
	/*
	 * Get the extra vertical space parameter.
	 */
	if (vspace_attr)
	{
		val = XP_ATOI(vspace_attr);
		if (val < 0)
		{
			val = 0;
		}
		table_ele->border_vert_space = val;
	}
	table_ele->border_vert_space = FEUNITS_Y(table_ele->border_vert_space,
						context);

	/*
	 * Get the extra horizontal space parameter.
	 */
	if (hspace_attr)
	{
		val = XP_ATOI(hspace_attr);
		if (val < 0)
		{
			val = 0;
		}
		table_ele->border_horiz_space = val;
	}
	table_ele->border_horiz_space = FEUNITS_X(table_ele->border_horiz_space,
						context);

	table = XP_NEW(lo_TableRec);
	
	/* Keep ptr to table state structure.  Will be needed during relayout */
	table_ele->table = table;

	if (table == NULL)
	{
		return;
	}

	if(table_ele->border_top_width < 0)
			table_ele->border_top_width = 0;
 	if(table_ele->border_bottom_width < 0)
			table_ele->border_bottom_width = 0;
	if(table_ele->border_right_width < 0)
			table_ele->border_right_width = 0;
	if(table_ele->border_left_width < 0)
			table_ele->border_left_width = 0;

	if(table_ele->border_width < 0)
	{
		/* backwards compatibility */
		table->draw_borders = TABLE_BORDERS_GONE;			
		table_ele->border_width = 0;
		table_ele->border_top_width = 0;
		table_ele->border_bottom_width = 0;
		table_ele->border_right_width = 0;
		table_ele->border_left_width = 0;
	}
	else if (table_ele->border_top_width == 0
		&& table_ele->border_bottom_width == 0
		&& table_ele->border_left_width == 0
		&& table_ele->border_right_width == 0)
	{
		table->draw_borders = TABLE_BORDERS_OFF;
	}
	else
	{
		table->draw_borders = TABLE_BORDERS_ON;
	}
	table->has_percent_width_cells = FALSE;
	table->has_percent_height_cells = FALSE;
	table->backdrop.bg_color = NULL;
    table->backdrop.url = NULL;
    table->backdrop.tile_mode = LO_TILE_BOTH;

	/* Copied to lo_InitTableRecord()
	/*
	table->rows = 0;
	table->cols = 0;
	*/

	table->width_spans = NULL;
	table->width_span_ptr = NULL;
	table->height_spans = NULL;
	/* Copied to lo_InitTableRecord()
	table->height_span_ptr = NULL;
	*/
	table->caption = NULL;
	table->table_ele = table_ele;
	table->current_subdoc = NULL;
	table->row_list = NULL;
	table->row_ptr = NULL;
	table->width = 0;
	table->height = 0;
	
	lo_InitTableRecord( table );

	/*
	 * Percent width, height added for relayout
	 */
	table->percent_width = 0;
	table->percent_height = 0;

	table->inner_top_pad = TABLE_DEF_INNER_CELL_PAD;
	table->inner_bottom_pad = TABLE_DEF_INNER_CELL_PAD;
	table->inner_left_pad = TABLE_DEF_INNER_CELL_PAD;
	table->inner_right_pad = TABLE_DEF_INNER_CELL_PAD;
	table->inter_cell_pad = TABLE_DEF_INTER_CELL_PAD;

	table->current_subdoc = (LO_SubDocStruct *)lo_NewElement(context, state, LO_SUBDOC, NULL, 0);
	table->current_subdoc->type = LO_SUBDOC;
	
	table->current_subdoc->backdrop.bg_color = NULL;
    table->current_subdoc->backdrop.url = NULL;
    table->current_subdoc->backdrop.tile_mode = LO_TILE_BOTH;
	table->current_subdoc->state = lo_NewLayout(context,
		state->win_width, state->win_height, 0, 0, NULL);

	table->default_cell_width = 0;
	table->fixed_width_remaining = 0;
	table->fixed_col_widths = NULL;
	table->table_width_fixed = FALSE;
	table->fixed_cols = 0;
	
	/*
	 * You can't do percentage widths if the parent's
	 * width is still undecided.
	 */
	allow_percent_width = TRUE;
	allow_percent_height = TRUE;
	if ((state->is_a_subdoc == SUBDOC_CELL)||
		(state->is_a_subdoc == SUBDOC_CAPTION))
	{
		lo_TopState *top_state;
		lo_DocState *new_state;
		int32 doc_id;

		/*
		 * Get the unique document ID, and retreive this
		 * documents layout state.
		 */
		doc_id = XP_DOCID(context);
		top_state = lo_FetchTopState(doc_id);
		new_state = top_state->doc_state;

		while ((new_state->sub_state != NULL)&&
			(new_state->sub_state != state))
		{
			new_state = new_state->sub_state;
		}

		if ((new_state->sub_state == state)&&
			(new_state->current_ele != NULL))
		{
			LO_SubDocStruct *subdoc;

			subdoc = (LO_SubDocStruct *)new_state->current_ele;
			if (subdoc->width == 0)
			{
				allow_percent_width = FALSE;
#ifdef LOCAL_DEBUG
 fprintf(stderr, "Percent width not allowed in this subdoc!\n");
#endif /* LOCAL_DEBUG */
			}
			else
			{
#ifdef LOCAL_DEBUG
 fprintf(stderr, "Percent width IS allowed in this subdoc!\n");
#endif /* LOCAL_DEBUG */
			}
			if (subdoc->height == 0)
			{
				allow_percent_height = FALSE;
			}
		}
	}

	
	/* check for style sheet color and override if necessary */
	if(state->top_state && state->top_state->style_stack)
	{
		StyleStruct *style_struct;

		style_struct = STYLESTACK_GetStyleByIndex(
											state->top_state->style_stack,
											0);

		if(style_struct)
		{
			bgcolor_from_style = STYLESTRUCT_GetString(style_struct, BG_COLOR_STYLE);

			if(bgcolor_from_style)
			{
				bgcolor_attr = bgcolor_from_style;
			}
		}
	}

	/*
	 * Check for a background color attribute
	 */
	if (bgcolor_attr)
	{
		uint8 red, green, blue;
		XP_Bool rv;

		if(bgcolor_from_style)
			rv = LO_ParseStyleSheetRGB(bgcolor_attr, &red, &green, &blue);
		else
			rv = LO_ParseRGB(bgcolor_attr, &red, &green, &blue);
		if(rv)
		{
			table->backdrop.bg_color = XP_NEW(LO_Color);
			if (table->backdrop.bg_color != NULL)
			{
				table->backdrop.bg_color->red = red;
				table->backdrop.bg_color->green = green;
				table->backdrop.bg_color->blue = blue;
			}
		}
	}

    if (background_attr)
        table->backdrop.url = XP_STRDUP(background_attr);
	XP_FREEIF(bgcolor_from_style);


	/*
	 * Get the width and height parameters, in absolute or percentage.
	 * If percentage, make it absolute.
	 */
	if (width_attr)
	{
		Bool is_percent;

		val = lo_ValueOrPercent(width_attr, &is_percent);
		if (is_percent != FALSE)
		{
			table->percent_width = val;
			/*
			if (allow_percent_width == FALSE)
			{
				val = 0;
			}
			else
			{
				val = (state->win_width - state->win_left -
					state->win_right) * val / 100;
			}
			*/
			
		}
		else
		{
			table->percent_width = 0;
			table->table_width_fixed = TRUE;
			val = FEUNITS_X(val, context);
		}
		if (val < 0)
		{
			val = 0;
		}
		table->width = val;
	}
	
	if (height_attr)
	{
		Bool is_percent;

		val = lo_ValueOrPercent(height_attr, &is_percent);
		if (is_percent != FALSE)
		{
			table->percent_height = val;
			/*
			if (allow_percent_height == FALSE)
			{
				val = 0;
			}
			else
			{
				val = (state->win_height - state->win_top -
					state->win_bottom) * val / 100;
			}
			*/
		}
		else
		{
			table->percent_height = 0;
			val = FEUNITS_X(val, context);
		}
		if (val < 0)
		{
			val = 0;
		}
		table->height = val;
	}

	lo_SetTableDimensions(state, table, allow_percent_width, allow_percent_height);

	if (cellpad_attr)
	{
		val = XP_ATOI(cellpad_attr);
		if (val < 0)
		{
			val = 0;
		}
		/* set all our pads to this value */
		table->inner_top_pad = val;
		table->inner_bottom_pad = val;
		table->inner_left_pad = val;
		table->inner_right_pad = val;
	}

	if (toppad_attr)
	{
		val = XP_ATOI(toppad_attr);
		if (val < 0)
		{
			val = 0;
		}
		table->inner_top_pad = val;
	}

	if (bottompad_attr)
	{
		val = XP_ATOI(bottompad_attr);
		if (val < 0)
		{
			val = 0;
		}
		table->inner_bottom_pad = val;
	}

	if (leftpad_attr)
	{
		val = XP_ATOI(leftpad_attr);
		if (val < 0)
		{
			val = 0;
		}
		table->inner_left_pad = val;
	}

	if (rightpad_attr)
	{
		val = XP_ATOI(rightpad_attr);
		if (val < 0)
		{
			val = 0;
		}
		table->inner_right_pad = val;
	}
	
	if (cellspace_attr)
	{
		val = XP_ATOI(cellspace_attr);
		if (val < 0)
		{
			val = 0;
		}
		table->inter_cell_pad = val;
	}

	/*
	 * Get the COLS parameter.
	 */
	if (cols_attr)
	{
		val = XP_ATOI(cols_attr);
		if (val < 0)
		{
			val = 0;
		}
		table_ele->border_horiz_space = val;
		
		/*
		 * If we have a specified number of columns, then
		 * we first need to make sure we have a real table
		 * width and then compute our default column width.
		 */
		table->fixed_cols = val;		
		if ( val > 0 )
		{
		
			/* Copied to lo_CalcFixedColWidths() */
			/*
			int32 count;
			int32 table_width;
			
			table->fixed_cols = val;
			
			table_width = lo_ComputeInternalTableWidth ( context, table, state );
			*/

			/* Split the space up evenly */
			/*
			table->default_cell_width = table_width / val;
			*/

			/* and we have all the table width left to play with */
			/*
			table->fixed_width_remaining = table_width;
			*/
			
			/* allocate and initialize our width array */			
			table->fixed_col_widths = XP_ALLOC(val * sizeof(int32));
			if (table->fixed_col_widths == NULL)
			{
				state->top_state->out_of_memory = TRUE;
				table->fixed_cols = 0;
				table->default_cell_width = 0;
				return;
			}


			/*
			for ( count = 0; count < val; ++count )
			{
				table->fixed_col_widths[ count ] = 0;
			}
			*/

			lo_CalcFixedColWidths( context, state, table );
		}
	}

	/* Copied to lo_UpdateStateAfterBeginTable() */
	/* 
	state->current_table = table;
	*/
	
	lo_UpdateStateAfterBeginTable( state, table );
}


/*
 * Preparse the tag attributes and call the real begin table
 */
void
lo_BeginTable(MWContext *context, lo_DocState *state, PA_Tag *tag)
{

	/* style sheets variables */
	StyleStruct *style_struct=NULL;
	SS_Number *top_padding, *bottom_padding, *left_padding, *right_padding;

    char *align_attr =    	(char*)lo_FetchParamValue(context, tag, PARAM_ALIGN);
    char *border_attr =   	(char*)lo_FetchParamValue(context, tag, PARAM_BORDER);
    char *border_top_attr =		NULL;
    char *border_bottom_attr =	NULL;
    char *border_left_attr =	NULL;
    char *border_right_attr =	NULL;
    char *border_color_attr =	(char*)lo_FetchParamValue(context, tag, PARAM_BORDERCOLOR);
    char *border_style_attr =	NULL;
    char *vspace_attr =   	(char*)lo_FetchParamValue(context, tag, PARAM_VSPACE);
    char *hspace_attr =   	(char*)lo_FetchParamValue(context, tag, PARAM_HSPACE);
	char *bgcolor_attr =   	(char*)lo_FetchParamValue(context, tag, PARAM_BGCOLOR);
	char *background_attr =  	(char*)lo_FetchParamValue(context, tag, PARAM_BACKGROUND);
	char *width_attr =     	(char*)lo_FetchParamValue(context, tag, PARAM_WIDTH);
	char *height_attr =   	(char*)lo_FetchParamValue(context, tag, PARAM_HEIGHT);
	char *cellpad_attr =   	(char*)lo_FetchParamValue(context, tag, PARAM_CELLPAD);
	char *toppad_attr =   	(char*)lo_FetchParamValue(context, tag, PARAM_TOPPAD);
	char *bottompad_attr =  (char*)lo_FetchParamValue(context, tag, PARAM_BOTTOMPAD);
	char *leftpad_attr =   	(char*)lo_FetchParamValue(context, tag, PARAM_LEFTPAD);
	char *rightpad_attr =   (char*)lo_FetchParamValue(context, tag, PARAM_RIGHTPAD);
	char *cellspace_attr = 	(char*)lo_FetchParamValue(context, tag, PARAM_CELLSPACE);
	char *cols_attr = 		(char*)lo_FetchParamValue(context, tag, PARAM_COLS);

	if(!border_style_attr)
	{
		border_style_attr = XP_STRDUP("outset"); 
	}

	if(!border_color_attr)
	{
		XP_ASSERT(state);
		if(state)
			border_color_attr = PR_smprintf("#%2x%2x%2x", 
								   state->text_bg.red,
								   state->text_bg.green,
								   state->text_bg.blue);
	}

	if(state->top_state->style_stack)
		style_struct = STYLESTACK_GetStyleByIndex(state->top_state->style_stack, 0);

	if(style_struct)
	{
		left_padding = STYLESTRUCT_GetNumber(style_struct, LEFTPADDING_STYLE);
		if(left_padding)
		{
			LO_AdjustSSUnits(left_padding, LEFTPADDING_STYLE, context, state);
			XP_FREEIF(leftpad_attr);
			leftpad_attr = PR_smprintf("%ld", (int32)left_padding->value);
		}

		right_padding = STYLESTRUCT_GetNumber(style_struct, RIGHTPADDING_STYLE);
		if(right_padding)
		{
			LO_AdjustSSUnits(right_padding, RIGHTPADDING_STYLE, context, state);
			XP_FREEIF(rightpad_attr);
			rightpad_attr = PR_smprintf("%ld", (int32)right_padding->value);
		}

		top_padding = STYLESTRUCT_GetNumber(style_struct, TOPPADDING_STYLE);
		if(top_padding)
		{
			LO_AdjustSSUnits(top_padding, TOPPADDING_STYLE, context, state);
			XP_FREEIF(toppad_attr);
			toppad_attr = PR_smprintf("%ld", (int32)top_padding->value);
		}

		bottom_padding = STYLESTRUCT_GetNumber(style_struct, BOTTOMPADDING_STYLE);
		if(bottom_padding)
		{
			LO_AdjustSSUnits(bottom_padding, BOTTOMPADDING_STYLE, context, state);
			XP_FREEIF(bottompad_attr);
			bottompad_attr = PR_smprintf("%ld", (int32)bottom_padding->value);
		}
	}

	/* remove the PA_LOCK stuff, it does nothing anyways */

	lo_BeginTableAttributes(context, 
							state, 
							align_attr,
							border_attr,
							border_top_attr,
							border_bottom_attr,
							border_left_attr,
							border_right_attr,
							border_color_attr,
							border_style_attr,
							vspace_attr,
							hspace_attr,
							bgcolor_attr,
                            background_attr,
							width_attr,
							height_attr,
							cellpad_attr,
							toppad_attr,
							bottompad_attr,
							leftpad_attr,
							rightpad_attr,
							cellspace_attr,
							cols_attr);

	if(align_attr)
		PA_FREE(align_attr);
	if(border_attr)
		PA_FREE(border_attr);
	if(border_top_attr)
		PA_FREE(border_top_attr);
	if(border_bottom_attr)
		PA_FREE(border_bottom_attr);
	if(border_left_attr)
		PA_FREE(border_left_attr);
	if(border_right_attr)
		PA_FREE(border_right_attr);
	if(border_color_attr)
		PA_FREE(border_color_attr);
	if(border_style_attr)
		PA_FREE(border_style_attr);
	if(vspace_attr)
		PA_FREE(vspace_attr);
	if(hspace_attr)
		PA_FREE(hspace_attr);
	if(bgcolor_attr)
		PA_FREE(bgcolor_attr);
	if(background_attr)
		PA_FREE(background_attr);
	if(width_attr)
		PA_FREE(width_attr);
	if(height_attr)
		PA_FREE(height_attr);
	if(cellpad_attr)
		PA_FREE(cellpad_attr);
	if(toppad_attr)
		PA_FREE(toppad_attr);
	if(bottompad_attr)
		PA_FREE(bottompad_attr);
	if(leftpad_attr)
		PA_FREE(leftpad_attr);
	if(rightpad_attr)
		PA_FREE(rightpad_attr);
	if(cellspace_attr)
		PA_FREE(cellspace_attr);
	if(cols_attr)
		PA_FREE(cols_attr);
}


static void
lo_fill_cell_array(lo_TableRec *table, lo_cell_data XP_HUGE *cell_array,
	lo_TableCell *blank_cell, int32 cell_pad, Bool *relayout_pass)
{
	int32 x, y;
	int32 indx;
	lo_table_span *row_max;
	lo_table_span *col_max;
	lo_TableRow *row_ptr;
	lo_TableCell *cell_ptr;

	row_max = table->height_spans;
	row_ptr = table->row_list;
	for (y = 0; y < table->rows; y++)
	{
		x = 0;
		col_max = table->width_spans;
		cell_ptr = row_ptr->cell_list;
		row_ptr->cells_in_row = 0;
		while (cell_ptr != NULL)
		{
			/*
			 * Also on this pass check if any of the cells
			 * NEED to be relaid out later.
			 */
			if (cell_ptr->must_relayout != FALSE)
			{
				*relayout_pass = TRUE;
			}

			/*
			 * "fix" up badly specified row spans.
			 */
			if ((y + cell_ptr->rowspan) > table->rows)
			{
				cell_ptr->rowspan = table->rows - y;
				if (cell_ptr->rowspan == 1)
				{
					if (cell_ptr->height > row_max->dim)
					{
						row_max->dim = cell_ptr->height;
					}
				}
			}

			indx = (y * table->cols) + x;

			if (cell_array[indx].cell == blank_cell)
			{
				x++;
				col_max = col_max->next;
				continue;
			}

			/*
			 * If the cell is not an empty cell, count it for this row
			 */
			/* if (cell_ptr->cell->cell_list != NULL || cell_ptr->cell->cell_float_list != NULL) */
				row_ptr->cells_in_row++;

			cell_array[indx].cell = cell_ptr;
			cell_array[indx].width = cell_ptr->max_width;
			cell_array[indx].height = cell_ptr->height;

			if (cell_ptr->colspan > 1)
			{
				int32 i;
				int32 width, min_width;
				lo_table_span *max_ptr;

				max_ptr = col_max;

				width = max_ptr->dim;
				min_width = max_ptr->min_dim;
				for (i=1; i < cell_ptr->colspan; i++)
				{
					cell_array[indx + i].cell = blank_cell;
					max_ptr = max_ptr->next;
					width = width + cell_pad + max_ptr->dim;
					min_width = min_width + cell_pad +
						max_ptr->min_dim;
				}

				if (width < cell_ptr->max_width)
				{
					int32 add_width;
					int32 add;
					lo_table_span *add_ptr;

					add_ptr = col_max;
					add_width = cell_ptr->max_width -
						width;
					add = 0;
					while (add_ptr != max_ptr)
					{
						int32 newWidth;

						newWidth = add_width *
							add_ptr->dim / width;
						add_ptr->dim += newWidth;
						add += newWidth;
						add_ptr = add_ptr->next;
					}
					add_ptr->dim += (add_width - add);
				}

				if (min_width < cell_ptr->min_width)
				{
					int32 add_width;
					int32 add;
					lo_table_span *add_ptr;

					add_ptr = col_max;
					add_width = cell_ptr->min_width -
						min_width;
					add = 0;
					while (add_ptr != max_ptr)
					{
						int32 newWidth;

						newWidth = add_width *
							add_ptr->min_dim /
							min_width;
						/*
						 * We are not allowed to add
						 * enough to put min_dim > dim.
						 */
						if ((add_ptr->min_dim +
						     newWidth) > add_ptr->dim)
						{
						    newWidth = add_ptr->dim -
							add_ptr->min_dim;
						    /*
						     * don't let newWidth become
						     * negative.
						     */
						    if (newWidth < 0)
						    {
								newWidth = 0;
						    }
						}
						add_ptr->min_dim += newWidth;
						add += newWidth;
						add_ptr = add_ptr->next;
					}
					add_ptr->min_dim += (add_width - add);
				}
				col_max = max_ptr;
			}

			if (cell_ptr->rowspan > 1)
			{
				int32 i;
				int32 height;
				int32 tmp_val;
				lo_table_span *max_ptr;

				max_ptr = row_max;

				height = max_ptr->dim;
				for (i=1; i < cell_ptr->rowspan; i++)
				{
				    cell_array[indx + (i * table->cols)].cell =
						blank_cell;
				    if (cell_ptr->colspan > 1)
				    {
						int32 j;

						for (j=1; j < cell_ptr->colspan; j++)
						{
						    cell_array[indx +
							(i * table->cols) + j].cell =
							blank_cell;
						}
				    }
				    max_ptr = max_ptr->next;
				    height = height + cell_pad + max_ptr->dim;
				}

				tmp_val = cell_ptr->baseline - row_max->min_dim;
				if (tmp_val > 0)
				{
					row_max->min_dim += tmp_val;
					if (row_ptr->vert_alignment ==
						LO_ALIGN_BASELINE)
					{
						row_max->dim += tmp_val;
						height += tmp_val;
					}
					/*
					 * Baseline spacing shouldn't grow the
					 * max height, except for baseline
					 * aligned rows as above.
					 */
					if (row_max->min_dim > row_max->dim)
					{
						row_max->min_dim = row_max->dim;
					}
				}

				if (height < cell_ptr->height)
				{
					int32 add_height;
					int32 add;
					lo_table_span *add_ptr;

					add_ptr = row_max;
					add_height = cell_ptr->height -
						height;
					add = 0;
					while (add_ptr != max_ptr)
					{
						int32 newHeight;

						newHeight = add_height *
							add_ptr->dim / height;
						add_ptr->dim += newHeight;
						add += newHeight;
						add_ptr = add_ptr->next;
					}
					add_ptr->dim += (add_height - add);
				}
			}

			x += cell_ptr->colspan;

			col_max = col_max->next;
			cell_ptr = cell_ptr->next;
			cell_array[indx].cell->next = NULL;
		}
		row_ptr = row_ptr->next;
		row_max = row_max->next;
	}
}

static void
lo_percent_width_cells(lo_TableRec *table, lo_cell_data XP_HUGE *cell_array,
	lo_TableCell *blank_cell, int32 cell_pad, int32 table_pad,
	int32 *table_width, int32 *min_table_width,
	int32 *base_table_width, int32 *min_base_table_width)
{
	int32 x, y;
	int32 indx;
	int32 new_table_width;
	int32 new_min_table_width;
	int32 new_base_table_width;
	int32 new_min_base_table_width;
	Bool need_pass_two;
	lo_table_span *row_max;
	lo_table_span *col_max;
	lo_TableRow *row_ptr;
	lo_TableCell *cell_ptr;

	new_table_width = 0;
	row_ptr = table->row_list;
	row_max = table->height_spans;
	for (y=0; y < table->rows; y++)
	{
	    if (row_ptr->has_percent_width_cells != FALSE)
	    {
			int32 reserve;
			int32 unknown, unknown_base;

			unknown = 0;
			unknown_base = 0;
			reserve = 100 - table->cols;
			col_max = table->width_spans;
			for (x=0; x < table->cols; x++)
			{
				indx = (y * table->cols) + x;
				cell_ptr = cell_array[indx].cell;
				if ((cell_ptr == blank_cell)||
					(cell_ptr == NULL))
				{
					col_max = col_max->next;
					continue;
				}
				if (cell_ptr->percent_width > 0)
				{
					int32 width;

					reserve += cell_ptr->colspan;
					if (cell_ptr->percent_width > reserve)
					{
					    cell_ptr->percent_width = reserve;
					    reserve = 0;
					}
					else
					{
					    reserve -= cell_ptr->percent_width;
					}
					width = cell_ptr->max_width * 100 /
						cell_ptr->percent_width;
					if (width > new_table_width)
					{
						new_table_width = width;
					}
				}
				else
				{
					unknown++;
					unknown_base += cell_ptr->max_width;
				}
				col_max = col_max->next;
			}
			if (unknown)
			{
			    col_max = table->width_spans;
			    for (x=0; x < table->cols; x++)
			    {
				indx = (y * table->cols) + x;
				cell_ptr = cell_array[indx].cell;
				if ((cell_ptr == blank_cell)||
					(cell_ptr == NULL))
				{
					col_max = col_max->next;
					continue;
				}
				if (cell_ptr->percent_width == 0)
				{
					int32 width;
					int32 percent;

					if (unknown == 1)
					{
					    percent = reserve;
					}
					else
					{
					    percent = reserve *
							cell_ptr->max_width /
							unknown_base;
					}
					reserve -= percent;
					if (reserve < 0)
					{
					    reserve = 0;
					}
					percent += cell_ptr->colspan;
					cell_ptr->percent_width = percent;

					width = cell_ptr->max_width * 100 /
						cell_ptr->percent_width;
					if (width > new_table_width)
					{
						new_table_width = width;
					}

					unknown--;
				}
				col_max = col_max->next;
			    }
			}
	    }
	    else
	    {
			int32 width;

			width = 0;
			col_max = table->width_spans;
			for (x=0; x < table->cols; x++)
			{
				indx = (y * table->cols) + x;
				cell_ptr = cell_array[indx].cell;
				if ((cell_ptr == blank_cell)||
					(cell_ptr == NULL))
				{
					col_max = col_max->next;
					continue;
				}
				width += cell_ptr->max_width;
				col_max = col_max->next;
			}
			if (width > new_table_width)
			{
				new_table_width = width;
			}
	    }
	    row_ptr = row_ptr->next;
	    row_max = row_max->next;
	}

	if (*table_width > new_table_width)
	{
		new_table_width = *table_width;
	}

	/*
	 * If we already know how wide this table must be
	 * Use that width when calculate percentage cell widths.
	 */
	if ((table->width > 0)&&(table->width >= *min_table_width))
	{
		new_table_width = table->width;
	}

	need_pass_two = FALSE;

	col_max = table->width_spans;
	for (x=0; x < table->cols; x++)
	{
		int32 current_max;

		current_max = col_max->dim;
		col_max->dim = 1;
		row_max = table->height_spans;
		for (y=0; y < table->rows; y++)
		{
			indx = (y * table->cols) + x;
			cell_ptr = cell_array[indx].cell;
			if ((cell_ptr == blank_cell)||
				(cell_ptr == NULL))
			{
				row_max = row_max->next;
				continue;
			}
			if ((cell_ptr->percent_width > 0)&&
				(cell_ptr->colspan == 1))
			{
				int32 p_width;

				p_width = new_table_width *
					cell_ptr->percent_width / 100;
				if (p_width < cell_ptr->min_width)
				{
					p_width = cell_ptr->min_width;
				}
				if (p_width > col_max->dim)
				{
					col_max->dim = p_width;
				}
			}
			else if (cell_ptr->colspan > 1)
			{
				need_pass_two = TRUE;
			}
			else
			{
				if (cell_ptr->max_width > col_max->dim)
				{
					col_max->dim =
						cell_ptr->max_width;
				}
			}
			row_max = row_max->next;
		}
		if (col_max->dim < col_max->min_dim)
		{
			col_max->dim = col_max->min_dim;
		}
		col_max = col_max->next;
	}
	/*
	 * Take care of spanning columns if any
	 */
	if (need_pass_two != FALSE)
	{
	    row_max = table->height_spans;
	    for (y=0; y < table->rows; y++)
	    {
			col_max = table->width_spans;
			for (x=0; x < table->cols; x++)
			{
				indx = (y * table->cols) + x;
				cell_ptr = cell_array[indx].cell;
				if ((cell_ptr == blank_cell)||
					(cell_ptr == NULL))
				{
					col_max = col_max->next;
					continue;
				}
				if (cell_ptr->colspan > 1)
				{
					int32 i;
					int32 width;
					lo_table_span *max_ptr;
					int32 p_width;
					int32 new_width;

					new_width = cell_ptr->max_width;
				    if (cell_ptr->percent_width > 0)
				    {
						p_width = new_table_width *
							cell_ptr->percent_width / 100;
						if (p_width >= cell_ptr->min_width)
						{
							new_width = p_width;
						}
				    }

					max_ptr = col_max;

					width = max_ptr->dim;
					for (i=1; i < cell_ptr->colspan; i++)
					{
						max_ptr = max_ptr->next;
						width = width + cell_pad +
							max_ptr->dim;
					}

					if (width < new_width)
					{
						int32 add_width;
						int32 add;
						lo_table_span *add_ptr;

						add_ptr = col_max;
						add_width = new_width - width;
						add = 0;
						while (add_ptr != max_ptr)
						{
							int32 newWidth;

							newWidth = add_width *
								add_ptr->dim /
								width;
							add_ptr->dim +=newWidth;
							add += newWidth;
							add_ptr = add_ptr->next;
						}
						add_ptr->dim += (add_width -
							add);
					}
				}
				col_max = col_max->next;
			}
			row_max = row_max->next;
	    }
	}

	new_table_width = 0;
	new_min_table_width = 0;
	new_base_table_width = 0;
	new_min_base_table_width = 0;
	col_max = table->width_spans;
	while (col_max != NULL)
	{
		new_base_table_width += col_max->dim;
		new_min_base_table_width += col_max->min_dim;
		new_table_width = new_table_width + cell_pad +
			col_max->dim;
		new_min_table_width = new_min_table_width + cell_pad +
			col_max->min_dim;
		col_max = col_max->next;
	}
	new_table_width += cell_pad;
	new_table_width += (table->table_ele->border_left_width + 
		table->table_ele->border_right_width);
	new_min_table_width += cell_pad;
	new_min_table_width += (table->table_ele->border_left_width + 
		table->table_ele->border_right_width);
	if (table->draw_borders == TABLE_BORDERS_OFF)
	{
		new_table_width += (2 * table_pad);
		new_min_table_width += (2 * table_pad);
	}

	*table_width = new_table_width;
	*min_table_width = new_min_table_width;
	*base_table_width = new_base_table_width;
	*min_base_table_width = new_min_base_table_width;
}

static void
lo_cell_relayout_pass(MWContext *context, lo_DocState *state,
	lo_TableRec *table, lo_cell_data XP_HUGE *cell_array, lo_TableCell *blank_cell,
	int32 cell_pad, Bool *rowspan_pass, Bool relayout)
{
	int32 x, y;
	int32 indx;
	lo_table_span *row_max;
	lo_table_span *col_max;
	lo_TableRow *row_ptr;
	lo_TableCell *cell_ptr;
	int32 top_inner_pad;
	int32 bottom_inner_pad;
	int32 left_inner_pad;
	int32 right_inner_pad;

	top_inner_pad = FEUNITS_X(table->inner_top_pad, context);
	bottom_inner_pad = FEUNITS_X(table->inner_bottom_pad, context);
	left_inner_pad = FEUNITS_X(table->inner_left_pad, context);
	right_inner_pad = FEUNITS_X(table->inner_right_pad, context);

	row_ptr = table->row_list;
	row_max = table->height_spans;
	for (y=0; y < table->rows; y++)
	{
		Bool max_height_valid;
		Bool changed_row_height;
		int32 max_row_height;
			
		max_height_valid = FALSE;
		changed_row_height = FALSE;
		
		max_row_height = 0;
		
		col_max = table->width_spans;

		for (x=0; x < table->cols; x++)
		{
			LO_CellStruct *cell_struct;
			int32 width;
			int32 inside_width;
			Bool has_elements;

			indx = (y * table->cols) + x;
			cell_ptr = cell_array[indx].cell;
			if ((cell_ptr == blank_cell)||
				(cell_ptr == NULL))
			{
				col_max = col_max->next;
				continue;
			}
			cell_struct = cell_ptr->cell;
			inside_width = col_max->dim;
			width = inside_width;
			if (cell_ptr->colspan > 1)
			{
				int32 i;
				lo_table_span *max_ptr;

				max_ptr = col_max;

				/*
				 * We need to add some cellpads for the spanned cells
				 * to our inside width
				 */
				inside_width += ( cell_ptr->colspan - 1 ) * cell_pad;
				
				for (i=1; i < cell_ptr->colspan; i++)
				{
					max_ptr = max_ptr->next;
					inside_width += max_ptr->dim;
					width += cell_pad + max_ptr->dim;
				}
			}
									
			/*
			 * We want to relayout the cell if it's been tagged as
			 * needing it or if it's been layed out to a different size.
			 */
			if ( (cell_ptr->must_relayout != FALSE) || 
				((width != cell_ptr->max_width)) )
			{
				cell_ptr->must_relayout = FALSE;
				
				inside_width = inside_width -
				    (2 * cell_struct->border_width) -
				    (2 * cell_struct->border_horiz_space) -
				    (left_inner_pad + right_inner_pad);
				    
				/*
				 * Don't relayout documents that have no
				 * elements, it causes errors.
				 */
				has_elements = lo_cell_has_elements(cell_ptr->cell);
				if ( has_elements == FALSE )
				{
					/*
					 * Simply record our new width in this case.
					 */
					 cell_struct->width = inside_width;
				}
				else
				{

					cell_struct = lo_RelayoutCell(context, state,
					    table->current_subdoc, cell_ptr,
					    cell_struct, inside_width,
					    cell_ptr->is_a_header, relayout);

					if (!cell_struct)	/* temporary fix for crash when cell_struct is null */
					{
						break;
					}
					cell_ptr->cell = cell_struct;
					cell_ptr->baseline = lo_GetCellBaseline(cell_struct);
					
					if (cell_ptr->rowspan == 1)
					{
						int32 tmp_val;

						/*
						 * We have changed the height of a single row cell, so we may need
						 * to update the row height later.
						 */
						changed_row_height = TRUE;
						
					    tmp_val = cell_ptr->baseline - row_max->min_dim;
					    if (tmp_val > 0)
					    {
							row_max->min_dim += tmp_val;
							if (row_ptr->vert_alignment ==
								LO_ALIGN_BASELINE)
							{
								row_max->dim += tmp_val;
							}
							/*
							 * Baseline spacing shouldn't grow the
							 * max height, except for baseline
							 * aligned rows as above.
							 */
							if (row_max->min_dim > row_max->dim)
							{
								row_max->min_dim = row_max->dim;
							}
					    }
					}
					else
					{
						*rowspan_pass = TRUE;
					}
				}
			}
			else
			{
				if (cell_ptr->rowspan > 1)
				{
					*rowspan_pass = TRUE;
				}
			}
			
			/*
			 * If this cell has anything inside it, see if it's height
			 * is the biggest for the row.
			 */
			has_elements = lo_cell_has_elements(cell_ptr->cell);
			if ( has_elements != FALSE )
			{
				int32 cell_row_height;

				/*
				 * What row height does this cell want?
				 */
			    cell_row_height = cell_struct->height +
					(top_inner_pad + bottom_inner_pad) +
					(2 * cell_struct->border_vert_space);
				
				/*
				 * Munge the height if the row is vertically aligned along
				 * the baseline and the cell only spans one row.
				 */
			    if (cell_ptr->rowspan == 1)
			    {
			    	if (row_ptr->vert_alignment == LO_ALIGN_BASELINE)
			    	{
				    	cell_row_height = (cell_row_height -
						    cell_ptr->baseline) + row_max->min_dim;
					}
				
					if ( cell_row_height > max_row_height )
					{
						/*
						 * Update to our new maximum and flag that it
						 * contains valid data.
						 */
						max_height_valid = TRUE;
						max_row_height = cell_row_height;
					}
			    }
			}
			col_max = col_max->next;
		}
		
		/*
		 * If we did relayout anything on that row which gave us a new
		 * valid max row height which is different to what we currently
		 * have, then use that.
		 */
		if ( max_height_valid != FALSE && changed_row_height != FALSE &&
			max_row_height != row_max->dim )
		{
			/*
			 * Can't reset height to be smaller than
			 * the minimum height.
			 */
			if (max_row_height >= row_max->min_dim)
			{
				row_max->dim = max_row_height;
			}
			else
			{
				row_max->dim = row_max->min_dim;
			}
		}
		row_max = row_max->next;
		row_ptr = row_ptr->next;
	}

}


static void
lo_cell_rowspan_pass(MWContext *context, lo_TableRec *table, lo_cell_data XP_HUGE *cell_array,
	lo_TableCell *blank_cell, int32 cell_pad)
{
	int32 x, y;
	int32 indx;
	lo_table_span *row_max;
	lo_table_span *col_max;
	lo_TableRow *row_ptr;
	lo_TableCell *cell_ptr;
	int32 top_inner_pad;
	int32 bottom_inner_pad;

	top_inner_pad = FEUNITS_X(table->inner_top_pad, context);
	bottom_inner_pad = FEUNITS_X(table->inner_bottom_pad, context);

	row_max = table->height_spans;
	row_ptr = table->row_list;
	for (y=0; y < table->rows; y++)
	{
		col_max = table->width_spans;
		for (x=0; x < table->cols; x++)
		{
			LO_CellStruct *cell_struct;

			indx = (y * table->cols) + x;
			cell_ptr = cell_array[indx].cell;
			if ((cell_ptr == blank_cell)||
				(cell_ptr == NULL))
			{
				col_max = col_max->next;
				continue;
			}
			cell_struct = cell_ptr->cell;

			if (cell_ptr->rowspan > 1)
			{
				int32 i;
				int32 height;
				int32 tmp_val;
				int32 cell_height;
				lo_table_span *max_ptr;

				max_ptr = row_max;

				height = max_ptr->dim;
				for (i=1; i < cell_ptr->rowspan; i++)
				{
				    max_ptr = max_ptr->next;
				    height = height + cell_pad +
					max_ptr->dim;
				}
				tmp_val = cell_ptr->baseline -
				    row_max->min_dim;
				if (tmp_val > 0)
				{
				    row_max->min_dim += tmp_val;
				    if (row_ptr->vert_alignment ==
						LO_ALIGN_BASELINE)
				    {
						row_max->dim += tmp_val;
						height += tmp_val;
				    }
				    /*
				     * Baseline spacing shouldn't grow the
				     * max height, except for baseline
				     * aligned rows as above.
				     */
				    if (row_max->min_dim > row_max->dim)
				    {
						row_max->min_dim = row_max->dim;
				    }
				}

				cell_height = cell_struct->height +
					(top_inner_pad + bottom_inner_pad) +
					(2 * cell_struct->border_vert_space);

				if (height < cell_height)
				{
					int32 add_height;
					int32 add;
					lo_table_span *add_ptr;

					add_ptr = row_max;
					add_height = cell_height -
						height;
					add = 0;
					while (add_ptr != max_ptr)
					{
						int32 newHeight;

						newHeight = add_height *
							add_ptr->dim /
							height;
						add_ptr->dim +=
							newHeight;
						add += newHeight;
						add_ptr->min_dim = add_ptr->dim;
						add_ptr = add_ptr->next;
					}
					add_ptr->dim +=
						(add_height - add);
					add_ptr->min_dim = add_ptr->dim;
				}
			}
			col_max = col_max->next;
		}
		row_max = row_max->next;
		row_ptr = row_ptr->next;
	}
}


static void
lo_free_cell_record(MWContext *context, lo_DocState *state, lo_TableCell *cell)
{
	if (cell == NULL)
	{
		return;
	}

	/*
	 * If this table cell is not nested inside another table cell
	 * or caption, free any re-parse tags stored on this cell.
	 */
	if ((cell->in_nested_table == FALSE) &&
		(cell->subdoc_tags != NULL)&&
		(state->is_a_subdoc != SUBDOC_CELL)&&
		(state->is_a_subdoc != SUBDOC_CAPTION))
	{
		PA_Tag *tptr;
		PA_Tag *tag;

		tptr = cell->subdoc_tags;
		while ((tptr != cell->subdoc_tags_end)&&(tptr != NULL))
		{
			tag = tptr;
			tptr = tptr->next;
			tag->next = NULL;
			PA_FreeTag(tag);
		}
		if (tptr != NULL)
		{
			tptr->next = NULL;
			PA_FreeTag(tptr);
		}
		cell->subdoc_tags = NULL;
		cell->subdoc_tags_end = NULL;
	}

	if (cell->cell != NULL) {
		cell->cell->table = NULL;
		cell->cell->table_row = NULL;
		cell->cell->table_cell = NULL;
	}	

	XP_DELETE(cell);
}


static void

lo_free_row_record(MWContext *context, lo_DocState *state,
			lo_TableRow *row, Bool partial)
{
	lo_TableCell *cell_ptr;
	lo_TableCell *cell;

	if (row->cell_list != NULL)
	{
		/*
		 * These are already freed on a completed table, and
		 * need to be freed on a partial table.
		 */

		if (partial != FALSE)
		{
			cell_ptr = row->cell_list;
			while (cell_ptr != NULL)
			{
				cell = cell_ptr;
				cell_ptr = cell_ptr->next;
				lo_free_cell_record(context, state, cell);
			}
		}

		row->cell_list = NULL;
		row->cell_ptr = NULL;
	}

    XP_FREEIF(row->backdrop.bg_color);
    XP_FREEIF(row->backdrop.url);

	XP_DELETE(row);
}


static void lo_FreeTableCaption( MWContext *context, lo_DocState *state, lo_TableRec *table )
{
	if (table->caption != NULL)
	{
		if (table->caption->cell_ele != NULL)
		{
			lo_FreeCaptionCell( context, state, table->caption->cell_ele );
			table->caption->cell_ele = NULL;
		}

		if ( table->caption->subdoc != NULL )
		{
			lo_FreePartialSubDoc ( context, state, table->caption->subdoc );
			table->caption->subdoc = NULL;
		}
		
		XP_DELETE(table->caption);
		table->caption = NULL;
	}
}

void lo_free_table_record(MWContext *context, lo_DocState *state,
			lo_TableRec *table, Bool partial)
{
	if (table->row_list != NULL)
	{
		lo_TableRow *row_ptr;
		lo_TableRow *row;

		row_ptr = table->row_list;
		while (row_ptr != NULL)
		{
			row = row_ptr;
			row_ptr = row_ptr->next;
			lo_free_row_record(context, state, row, partial);
		}
		table->row_list = NULL;
		table->row_ptr = NULL;
	}

	lo_FreeTableCaption( context, state, table );

	if (table->width_spans != NULL)
	{
		lo_table_span *span_ptr;
		lo_table_span *span;

		span_ptr = table->width_spans;
		while (span_ptr != NULL)
		{
			span = span_ptr;
			span_ptr = span_ptr->next;
			XP_DELETE(span);
		}
		table->width_spans = NULL;
		table->width_span_ptr = NULL;
	}

	if (table->height_spans != NULL)
	{
		lo_table_span *span_ptr;
		lo_table_span *span;

		span_ptr = table->height_spans;
		while (span_ptr != NULL)
		{
			span = span_ptr;
			span_ptr = span_ptr->next;
			XP_DELETE(span);
		}
		table->height_spans = NULL;
		table->height_span_ptr = NULL;
	}

    XP_FREEIF(table->backdrop.bg_color);
    XP_FREEIF(table->backdrop.url);

	/*
	 * Whether the partial flag is set or not, we always
	 * want to eliminate this subdoc and all its contents
	 * when the table is done.
	 */
	if (table->current_subdoc != NULL)
	{
		lo_FreePartialSubDoc(context, state, table->current_subdoc);
		table->current_subdoc = NULL;
	}

	if ( table->fixed_col_widths != NULL )
	{
		XP_FREE(table->fixed_col_widths);
	}
	
	/*
	 * Reset back pointer in table layout element
	 */
	if (table->table_ele != NULL) {
		table->table_ele->table = NULL;
	}

	XP_DELETE(table);
}



void
lo_FreePartialTable(MWContext *context, lo_DocState *state, lo_TableRec *table)
{
	lo_TableRow *row_ptr;
	lo_TableCell *cell_ptr;
	LO_SubDocStruct *subdoc;
	LO_CellStruct *cell_struct;

	if (table == NULL)
	{
		return;
	}

	row_ptr = table->row_list;
	while (row_ptr != NULL)
	{
		cell_ptr = row_ptr->cell_list;
		while (cell_ptr != NULL)
		{
			cell_struct = cell_ptr->cell;
			lo_FreePartialCell(context, state, cell_struct);
			cell_ptr->cell = NULL;
			cell_ptr = cell_ptr->next;
		}
		row_ptr = row_ptr->next;
	}

	if (table->caption != NULL)
	{
		subdoc = table->caption->subdoc;
		lo_FreePartialSubDoc(context, state, subdoc);
		table->caption->subdoc = NULL;
	}

	lo_free_table_record(context, state, table, TRUE);
}



void
lo_EndTable(MWContext *context, lo_DocState *state, lo_TableRec *table, Bool relayout)
{
	int32 save_doc_min_width;
	int32 x, y;
	int32 cell_x, cell_y;
	int32 indx;
	int32 cell_cnt;
	int32 ele_cnt;
	int32 table_width, min_table_width;
	int32 base_table_width, min_base_table_width;
	int32 table_height;
	int32 min_table_height;
	int32 width_limit;
	Bool relayout_pass;
	Bool rowspan_pass;
	Bool cut_to_window_width;
	lo_TableCell blank_cell;
	lo_cell_data XP_HUGE *cell_array;
	XP_Block cell_array_buff;
	lo_table_span *row_max;
	lo_table_span *col_max;
	lo_TableCell *cell_ptr;
	int32 cell_pad;
	int32 top_inner_pad;
	int32 bottom_inner_pad;
	int32 left_inner_pad;
	int32 right_inner_pad;
	int32 table_pad;
	Bool floating;
	int32 save_state_x, save_state_y;
	LO_Element *save_line_list;
#ifdef LOCAL_DEBUG
fprintf(stderr, "lo_EndTable called\n");
#endif /* LOCAL_DEBUG */

	cell_pad = FEUNITS_X(table->inter_cell_pad, context);
	table_pad = FEUNITS_X(TABLE_DEF_CELL_BORDER, context);
	
	top_inner_pad = FEUNITS_X(table->inner_top_pad, context);
	bottom_inner_pad = FEUNITS_X(table->inner_bottom_pad, context);
	left_inner_pad = FEUNITS_X(table->inner_left_pad, context);
	right_inner_pad = FEUNITS_X(table->inner_right_pad, context);
	
	relayout_pass = FALSE;
	cut_to_window_width = TRUE;
	floating = FALSE;

	/*
	 * So gcc won't complain
	 */
	save_state_x = 0;
	save_state_y = 0;
	save_line_list = NULL;
/*
	state->current_table = NULL;
*/

	cell_cnt = table->rows * table->cols;
	/*
	 * Empty tables are completely ignored!
	 */
	if (cell_cnt <= 0)
	{
		/*
		 * Clear table state, and free up this structure.
		 */
		state->current_table = NULL;

		/* 
		 * Don't wanna free table record any more because this information
		 * is used during relayout.
		 */
		/*
		if (table != NULL)
		{
			lo_free_table_record(context, state, table, FALSE);
		}		  
		*/
		return;
	}
#ifdef XP_WIN16
	/*
	 * It had better be the case that the size of the struct is a
	 * power of 2
	 */
	XP_ASSERT(sizeof(lo_cell_data) == 8);
	cell_array = (lo_cell_data XP_HUGE *)_halloc(cell_cnt, sizeof(lo_cell_data));

	/*
	 * There isn't a runtime routine that can initialize a huge array
	 */
	if (cell_cnt * sizeof(lo_cell_data) <= 0xFFFFL)
		memset(cell_array, 0, (cell_cnt * sizeof(lo_cell_data)));
	else {
		BYTE XP_HUGE *tmp = (BYTE XP_HUGE *)cell_array;

		for (indx = 0; indx < cell_cnt * sizeof(lo_cell_data); indx++)
			tmp[indx] = 0;
	}
#else
	cell_array_buff = XP_ALLOC_BLOCK(cell_cnt * sizeof(lo_cell_data));
	XP_LOCK_BLOCK(cell_array, lo_cell_data *, cell_array_buff);
	memset(cell_array, 0, (cell_cnt * sizeof(lo_cell_data)));
#endif

	lo_fill_cell_array(table, cell_array, &blank_cell, cell_pad,
		&relayout_pass);

	table_width = 0;
	min_table_width = 0;
	base_table_width = 0;
	min_base_table_width = 0;
	col_max = table->width_spans;
	while (col_max != NULL)
	{
		base_table_width += col_max->dim;
		min_base_table_width += col_max->min_dim;
		table_width = table_width + cell_pad + col_max->dim;
		min_table_width = min_table_width + cell_pad + col_max->min_dim;
		col_max = col_max->next;
	}
	table_width += cell_pad;
	min_table_width += cell_pad;
	table_width += (table->table_ele->border_left_width + 
		table->table_ele->border_right_width);
	min_table_width += (table->table_ele->border_left_width + 
		table->table_ele->border_right_width);
	if (table->draw_borders == TABLE_BORDERS_OFF)
	{
		table_width += (2 * table_pad);
		min_table_width += (2 * table_pad);
	}

	/*
	 * Take care of cells with percentage widths unless we're in
	 * a fixed layout table (has the COLS attribute) in which case
	 * the cell widths are already set.
	 */
	if ((table->has_percent_width_cells != FALSE) &&
		(table->fixed_cols == 0))
	{
		lo_percent_width_cells(table, cell_array, &blank_cell,
			cell_pad, table_pad, &table_width, &min_table_width,
			&base_table_width, &min_base_table_width);
		relayout_pass = TRUE;
	}

	/*
	 * Use state->left_margin here instead of state->win_left to take
	 * indent from lists into account.
	 */
	width_limit = (state->win_width - state->left_margin - state->win_right);
	if (table->width > 0)
	{
		width_limit = table->width;
	}
	/*
	 * Else, if we are a nested table in an infinite width
	 * layout space, we don't want to cut to "window width"
	 */
	else if (state->allow_percent_width == FALSE)
	{
		cut_to_window_width = FALSE;
	}

	/*
	 * If we're in a fixed layout table, then we never
	 * want to cut to "window width" 
	 */
	if ( table->fixed_cols > 0 )
	{
		cut_to_window_width = FALSE;
	}

	/*
	 * If the table is too small, we always need to relayout.
	 */
	if ((min_table_width >= width_limit))
	{
		lo_table_span *sub_ptr;

		relayout_pass = TRUE;
		sub_ptr = table->width_spans;
		while (sub_ptr != NULL)
		{
			sub_ptr->dim = sub_ptr->min_dim;
			sub_ptr = sub_ptr->next;
		}
	}
	else if ((table_width > width_limit)&&(cut_to_window_width != FALSE))
	{
		intn pass;
		Bool expand_failed;
		int32 add_width;
		int32 div_width;
		int32 div_width_next;
		int32 add;
		int32 total_to_add;
		lo_table_span *add_ptr;

		relayout_pass = TRUE;

		expand_failed = TRUE;
		pass = 0;
		total_to_add = width_limit - min_table_width;
		div_width = table_width;
		while ((total_to_add > 0)&&(pass < 10)&&(expand_failed !=FALSE))
		{
			int32 extra;
			int32 min_extra;

			expand_failed = FALSE;
			add_ptr = table->width_spans;
			add_width = total_to_add;
			total_to_add = 0;
			div_width_next = (table->cols + 1) * cell_pad;
			add = 0;
			while (add_ptr->next != NULL)
			{
				if ((pass > 0)&&
					(add_ptr->min_dim == add_ptr->dim))
				{
					add_ptr = add_ptr->next;
					continue;
				}
				extra = add_width * add_ptr->dim /
					div_width;
				if ((add_ptr->min_dim + extra) > add_ptr->dim)
				{
					expand_failed = TRUE;
					min_extra = add_ptr->dim -
						add_ptr->min_dim;
					extra = min_extra;
				}

				add_ptr->min_dim += extra;
				if (add_ptr->min_dim < add_ptr->dim)
				{
					div_width_next += add_ptr->dim;
				}
				add += extra;
				add_ptr = add_ptr->next;
			}
			if ((pass == 0)||(add_ptr->min_dim < add_ptr->dim))
			{
				if (expand_failed == FALSE)
				{
					extra = add_width - add;
				}
				else
				{
					extra = add_width * add_ptr->dim /
						div_width;
				}
				if ((add_ptr->min_dim + extra) > add_ptr->dim)
				{
					expand_failed = TRUE;
					min_extra = add_ptr->dim -
						add_ptr->min_dim;
					extra = min_extra;
				}
				add_ptr->min_dim += extra;
				if (add_ptr->min_dim < add_ptr->dim)
				{
					div_width_next += add_ptr->dim;
				}
				add += extra;
			}
			total_to_add = add_width - add;
			div_width = div_width_next;
			pass++;
		}
		if (total_to_add > 0)
		{
			add = 0;
			add_ptr = table->width_spans;
			while ((add_ptr->next != NULL)&&(total_to_add > 0))
			{
				if ((add_ptr->min_dim + total_to_add) >
					add_ptr->dim)
				{
					add = add_ptr->dim - add_ptr->min_dim;
					total_to_add = total_to_add - add;
					add_ptr->min_dim += add;
				}
				else
				{
					add_ptr->min_dim += total_to_add;
					total_to_add = 0;
				}
				add_ptr = add_ptr->next;
			}
			add_ptr->min_dim += total_to_add;
		}

		add_ptr = table->width_spans;
		while (add_ptr != NULL)
		{
			add_ptr->dim = add_ptr->min_dim;
			add_ptr = add_ptr->next;
		}
	}

	/*
	 * If the user specified a wider width than the minimum
	 * width then we grow the sucker.
	 */
	if ((table->width > 0)&&(width_limit > table_width))
	{
		int32 add_width;
		int32 add;
		int32 newWidth;
		int32 min_add_width;
		int32 min_add;
		lo_table_span *add_ptr;

		/* relayout to fill all the space we're adding */
		relayout_pass = TRUE;
		
		add_ptr = table->width_spans;
		add_width = width_limit - table_width;
		if ( add_width < 0 )
		{
			add_width = 0;
		}
		add = 0;
		min_add_width = width_limit - min_table_width;
		min_add = 0;
		while ((add_ptr != NULL)&&(add_ptr->next != NULL))
		{
			newWidth = add_width * add_ptr->dim / base_table_width;
			add_ptr->dim += newWidth;
			add += newWidth;

			/*
			 * We only want to resize the min_dim's of the spans
			 * if we're a fixed width table.
			 */
			if ( table->table_width_fixed )
			{
				newWidth = min_add_width * add_ptr->min_dim / min_base_table_width;
				add_ptr->min_dim += newWidth;
				min_add += newWidth;
			}
			add_ptr = add_ptr->next;
		}
		if ( table->table_width_fixed )
		{
			add_ptr->min_dim += (min_add_width - min_add);
		}
		add_ptr->dim += (add_width - add);
	}

	rowspan_pass = FALSE;

	if ((state->top_state->doc_state == state) ||
		(state->top_state->in_cell_relayout == TRUE))
	{

		if (state->top_state->doc_state == state)
		{
			state->top_state->in_cell_relayout = TRUE;
		}		

		if (relayout_pass != FALSE)
		{	
			lo_cell_relayout_pass(context, state, table, cell_array,
				&blank_cell, cell_pad, &rowspan_pass, relayout);	
		}

		if (rowspan_pass != FALSE)
		{
			lo_cell_rowspan_pass(context, table, cell_array, &blank_cell,
				cell_pad);
		}

		if (state->top_state->doc_state == state)
		{
			state->top_state->in_cell_relayout = FALSE;
		}
	}

	table_width = 0;
	min_table_width = 0;
	col_max = table->width_spans;
	while (col_max != NULL)
	{
		table_width = table_width + cell_pad + col_max->dim;
		min_table_width = min_table_width + cell_pad + col_max->min_dim;
		col_max = col_max->next;
	}
	table_width += cell_pad;
	table_width += (table->table_ele->border_left_width + 
		table->table_ele->border_right_width);
	min_table_width += cell_pad;
	min_table_width += (table->table_ele->border_left_width + 
		table->table_ele->border_right_width);

	table_height = 0;
	min_table_height = 0;
	row_max = table->height_spans;
	while (row_max != NULL)
	{
		table_height = table_height + cell_pad + row_max->dim;
		min_table_height = min_table_height + cell_pad + row_max->min_dim;
		row_max = row_max->next;
	}
	table_height += cell_pad;
	min_table_height += cell_pad;
	table_height += (table->table_ele->border_top_width + 
		table->table_ele->border_bottom_width);
	min_table_height += (table->table_ele->border_top_width + 
		table->table_ele->border_bottom_width);
	if (table->draw_borders == TABLE_BORDERS_OFF)
	{
		table_width += (2 * table_pad);
		min_table_width += (2 * table_pad);
		table_height += (2 * table_pad);
		min_table_height += (2 * table_pad);
	}


	/*
	 * If the user specified a taller table than the min
	 * height we need to adjust the heights of all the rows.
	 */
	if ((table->height > 0)&&(table->height > min_table_height))
	{
		int32 add_height;
		int32 add;
		lo_table_span *add_ptr;
		int32 newHeight;
		int32 min_add_height;
		int32 min_add;

		add_ptr = table->height_spans;
		add_height = table->height - table_height;
		if ( add_height < 0 )
		{
			add_height = 0;
		}
		min_add_height = table->height - min_table_height;
		add = 0;
		min_add = 0;
		while((add_ptr != NULL) && (add_ptr->next != NULL))
		{
			newHeight = add_height * add_ptr->dim / table_height;
			add_ptr->dim += newHeight;
			add += newHeight;
			
			newHeight = min_add_height * add_ptr->min_dim / table_height;
			add_ptr->min_dim += newHeight;
			min_add += newHeight;
			
			add_ptr = add_ptr->next;
		}
		add_ptr->dim += (add_height - add);
		add_ptr->min_dim += (min_add_height - min_add);

		/*
		 * We can't make the height be less than the sum
		 * of the row heights.
		 */
		if (table_height < table->height)
		{
			table_height = table->height;
		}
		min_table_height = table->height;
	}

	table->table_ele->width = table_width;
	table->table_ele->height = table_height;

	/*
	 * Here, if we are floating we don't do the linebreak.
	 * We do save some state info and then "fake" a linebreak
	 * so we can use common code to place the rest of the table.
	 */
	if (table->table_ele->ele_attrmask & LO_ELE_FLOATING)
	{
		save_line_list = state->line_list;
		save_state_x = state->x;
		save_state_y = state->y;
		floating = TRUE;
		state->x = state->left_margin;
		state->y = state->y + state->line_height;
		state->line_list = NULL;
	}
	else
	{
		/*
		lo_SetSoftLineBreakState(context, state, FALSE, 1);
		*/
		lo_SetLineBreakState ( context, state, FALSE, LO_LINEFEED_BREAK_SOFT, 1, relayout );
	}

	table->table_ele->x = state->x;
	table->table_ele->y = state->y;
	/* Keep the element IDs in order */
	table->table_ele->ele_id = NEXT_ELEMENT;
    
    /*cmanske - Save spacing param for drawing table selection in Composer */
    table->table_ele->inter_cell_space = table->inter_cell_pad;

	lo_AppendToLineList(context, state,
		(LO_Element *)table->table_ele, 0);

#ifdef EDITOR
    /*cmanske - build list of tables we are laying out
     * so Editor can readjust it's data after all is finished 
     */
    EDT_AddToRelayoutTables(context, table->table_ele);
#endif

	state->x += (cell_pad + table->table_ele->border_left_width);
	state->y += (cell_pad + table->table_ele->border_top_width);
	if (table->draw_borders == TABLE_BORDERS_OFF)
	{
		state->x += table_pad;
		state->y += table_pad;
	}

	if (table->caption != NULL)
	{
		int32 new_width;
		LO_SubDocStruct *subdoc;
		Bool has_elements;

		subdoc = table->caption->subdoc;

		col_max = table->width_spans;
		new_width = col_max->dim;
		while (col_max->next != NULL)
		{
			col_max = col_max->next;
			new_width = new_width + cell_pad + col_max->dim;
		}
		/*
		 * Don't relayout documents that have no
		 * elements, it causes errors.
		 */
		has_elements = lo_subdoc_has_elements(subdoc->state);
		if ((new_width < table->caption->max_width)&&
			(has_elements == FALSE))
		{
			int32 inside_width;

			inside_width = new_width -
			    (2 * subdoc->border_width) -
			    (2 * subdoc->border_horiz_space) -
			    (left_inner_pad + right_inner_pad);
			subdoc->width = inside_width;
		}
		else /* if (new_width < table->caption->max_width) */
		{
			int32 inside_width;

			inside_width = new_width -
			    (2 * subdoc->border_width) -
			    (2 * subdoc->border_horiz_space) -
			    (left_inner_pad + right_inner_pad);
			table->caption->subdoc = lo_RelayoutCaptionSubdoc(context,
				state, table->caption, subdoc, inside_width, FALSE);
			subdoc = table->caption->subdoc;

			table->caption->height = subdoc->height +
				(2 * subdoc->border_vert_space) +
				(top_inner_pad + bottom_inner_pad);
			table->caption->max_width = new_width;
		}
		/*
		else
		{
			table->caption->max_width = new_width;
		}
		*/
	}

	state->current_table = NULL;
	cell_x = state->x;
	cell_y = state->y;

	if ((table->caption != NULL)&&
		(table->caption->vert_alignment == LO_ALIGN_TOP))
	{
		LO_SubDocStruct *subdoc;

		cell_x = state->x;
		subdoc = table->caption->subdoc;
		subdoc->x = cell_x;
		subdoc->y = table->table_ele->y;
		subdoc->x_offset = (int16)subdoc->border_horiz_space;
		subdoc->y_offset = (int32)subdoc->border_vert_space;
		subdoc->width = table->caption->max_width -
			(2 * subdoc->border_horiz_space);
		subdoc->height = table->caption->height -
			(2 * subdoc->border_vert_space);
		ele_cnt = lo_align_subdoc(context, state,
			(lo_DocState *)subdoc->state, subdoc, table, NULL);
		if (ele_cnt > 0)
		{
			LO_CellStruct *cell_ele;
			
			if (relayout == FALSE)
			{
				/*
				cell_ele = lo_SquishSubDocToCell(context, state,
					subdoc, TRUE);
				*/
				cell_ele = lo_SquishSubDocToCell(context, state,
					subdoc, FALSE);
				cell_ele->isCaption = TRUE;
				table->caption->cell_ele = cell_ele;
			}
			else
			{
				cell_ele = table->caption->cell_ele;
				lo_UpdateCaptionCellFromSubDoc(context, state, subdoc, cell_ele);
			}
			/* table->caption->subdoc = NULL; */

			if (cell_ele == NULL)
			{
				lo_AppendToLineList(context, state,
					(LO_Element *)subdoc, 0);
			}
			else
			{
                /*cmanske - Save spacing param for drawing cell selection by FEs */
                /* should we use cell_pad (converted to FE units) instead? */
                cell_ele->inter_cell_space = table->inter_cell_pad;
				lo_AppendToLineList(context, state,
					(LO_Element *)cell_ele, 0);
			}
			cell_x = state->x;
			cell_y = cell_y + table->caption->height + cell_pad;

			table->table_ele->y_offset = cell_y -
				table->table_ele->y;
			cell_y += (cell_pad + table->table_ele->border_top_width);
			if (table->draw_borders == TABLE_BORDERS_OFF)
			{
				cell_y += table_pad;
			}
		}
		else
		{
			LO_CellStruct *cell_ele;

			/*
			 * Free up the useless subdoc
			 */
			cell_ele = lo_SquishSubDocToCell(context, state,
						subdoc, TRUE);
			table->caption->subdoc = NULL;

			if (cell_ele != NULL)
			{
				lo_FreeElement(context,
					(LO_Element *)cell_ele, TRUE);
			}
		}
	}

	row_max = table->height_spans;
	for (y=0; y < table->rows; y++)
	{
		cell_x = state->x;
		col_max = table->width_spans;
		for (x=0; x < table->cols; x++)
		{
			LO_CellStruct *cell_struct;
			int32 new_x, new_y;

			indx = (y * table->cols) + x;
			cell_ptr = cell_array[indx].cell;
			if ((cell_ptr == &blank_cell)||(cell_ptr == NULL))
			{
				cell_x = cell_x + col_max->dim + cell_pad;
				col_max = col_max->next;
				continue;
			}
			cell_struct = cell_ptr->cell;
/*
			cell_struct->x = cell_x;
			cell_struct->y = cell_y;
			cell_struct->x_offset = (int16)cell_struct->border_horiz_space;
			cell_struct->y_offset = (int32)cell_struct->border_vert_space;
*/

			new_x = cell_x + (int16)cell_struct->border_horiz_space + cell_struct->border_width;
			new_y = cell_y + (int32)cell_struct->border_vert_space + cell_struct->border_width;

			cell_struct->width = col_max->dim;
			if (cell_ptr->colspan > 1)
			{
				int32 i;
				lo_table_span *max_ptr;

				max_ptr = col_max;

				for (i=1; i < cell_ptr->colspan; i++)
				{
					max_ptr = max_ptr->next;
					cell_struct->width = cell_struct->width +
						cell_pad + max_ptr->dim;
				}
			}
			cell_struct->width = cell_struct->width -
				(2 * cell_struct->border_horiz_space);
			cell_struct->height = row_max->dim;
			if (cell_ptr->rowspan > 1)
			{
				int32 i;
				lo_table_span *max_ptr;

				max_ptr = row_max;

				for (i=1; i < cell_ptr->rowspan; i++)
				{
					max_ptr = max_ptr->next;
					cell_struct->height = cell_struct->height +
						cell_pad + max_ptr->dim;
				}
			}
			cell_struct->height = cell_struct->height -
				(2 * cell_struct->border_vert_space);

			lo_ShiftCell(cell_struct,
				cell_ptr->cell_base_x, cell_ptr->cell_base_y);
			cell_ptr->cell_base_x = 0;
			cell_ptr->cell_base_y = 0;
			ele_cnt = lo_align_cell(context, state, cell_ptr,
					cell_struct, table, row_max);
			/*
			if (ele_cnt > 0)
			*/
			{
				LO_CellStruct *cell_ele;
				int32 shift_x, shift_y;



				shift_x = new_x - cell_struct->x -
					cell_struct->x_offset -
					cell_struct->border_width;
				shift_y = new_y - cell_struct->y -
					cell_struct->y_offset -
					cell_struct->border_width;
				cell_struct->x = cell_x;
				cell_struct->y = cell_y;
				cell_struct->x_offset = (int16)cell_struct->border_horiz_space;
				cell_struct->y_offset = (int32)cell_struct->border_vert_space;
				cell_ele = cell_struct;

/*
				lo_ShiftCell(cell_ele, shift_x, shift_y);
*/
				lo_ShiftCell(cell_ele, new_x, new_y);
				/*
				 * Keep element ids sequential.
				 */
				if (cell_ele != NULL)
				{
					cell_ele->ele_id = NEXT_ELEMENT;
					lo_RenumberCell(state, cell_ele);
				}

				if (cell_ele == NULL)
				{
					lo_AppendToLineList(context, state,
						(LO_Element *)cell_struct, 0);
				}
				else
				{
                    /*cmanske - Save spacing param for drawing cell selection by FEs */
                    cell_ele->inter_cell_space = table->inter_cell_pad;
					lo_AppendToLineList(context, state,
						(LO_Element *)cell_ele, 0);
				}

				if ( relayout == FALSE )
				{
					/*
					 * Cell backgrounds now sit in their own layer. This is needed
					 * for selection to work correctly.
					 * Cell backgrounds can exist in the main _BODY layer,
					 * so we need to special case the parent layer of the
					 * cell background,
					 */
					if (context->compositor &&
						(cell_ele->backdrop.bg_color || cell_ele->backdrop.url))
					{
						lo_TopState *top_state = state->top_state;
						CL_Layer *parent_layer = lo_CurrentLayer(state);
						if (parent_layer == top_state->doc_layer)
							parent_layer = top_state->body_layer;
						cell_ele->cell_bg_layer =
							lo_CreateCellBackgroundLayer(context, cell_ele,
														 parent_layer);
					}
					else
					{
						cell_ele->cell_bg_layer = NULL;
					}
				}
			}
			/*
			else
			{
			*/
				/*
				LO_CellStruct *cell_ele;
				*/
				/*
				 * Free up the useless cell
				 */			
			/*
				cell_ele = cell_struct;
				if (cell_ele != NULL)
				{
					lo_FreeElement(context,
						(LO_Element *)cell_ele, TRUE);
				}
				
			}
			*/
			cell_x = cell_x + col_max->dim + cell_pad;
			col_max = col_max->next;
		}
		cell_y = cell_y + row_max->dim + cell_pad;
		row_max = row_max->next;
	}
	cell_x = cell_x + table->table_ele->border_left_width;
	cell_y = cell_y + table->table_ele->border_top_width;
	if (table->draw_borders == TABLE_BORDERS_OFF)
	{
		cell_x += table_pad;
		cell_y += table_pad;
	}

	if ((table->caption != NULL)&&
		(table->caption->vert_alignment != LO_ALIGN_TOP))
	{
		LO_SubDocStruct *subdoc;

		subdoc = table->caption->subdoc;
		subdoc->x = state->x;
		subdoc->y = cell_y;
		subdoc->x_offset = (int16)subdoc->border_horiz_space;
		subdoc->y_offset = (int32)subdoc->border_vert_space;
		subdoc->width = table->caption->max_width -
			(2 * subdoc->border_horiz_space);
		subdoc->height = table->caption->height -
			(2 * subdoc->border_vert_space);
		ele_cnt = lo_align_subdoc(context, state,
			(lo_DocState *)subdoc->state, subdoc, table, NULL);
		if (ele_cnt > 0)
		{
			LO_CellStruct *cell_ele;

			if (relayout == FALSE)
			{
				/*
				cell_ele = lo_SquishSubDocToCell(context, state,
						subdoc, TRUE);
				*/
				cell_ele = lo_SquishSubDocToCell(context, state,
						subdoc, FALSE);
				cell_ele->isCaption = TRUE;
				table->caption->cell_ele = cell_ele;
			}
			else 
			{
				cell_ele = table->caption->cell_ele;
				lo_UpdateCaptionCellFromSubDoc(context, state, subdoc, cell_ele);
			}
			
			/* table->caption->subdoc = NULL; */

			if (cell_ele == NULL)
			{
				lo_AppendToLineList(context, state,
					(LO_Element *)subdoc, 0);
			}
			else
			{
                /*cmanske - Save spacing param for drawing cell selection by FEs */
                cell_ele->inter_cell_space = table->inter_cell_pad;
				lo_AppendToLineList(context, state,
					(LO_Element *)cell_ele, 0);
			}
			cell_y = cell_y + table->caption->height + cell_pad;
		}
		else
		{
			LO_CellStruct *cell_ele;

			/*
			 * Free up the useless subdoc
			 */
			cell_ele = lo_SquishSubDocToCell(context, state,
						subdoc, TRUE);
			table->caption->subdoc = NULL;
			
			if (cell_ele != NULL)
			{
				lo_FreeElement(context,
					(LO_Element *)cell_ele, TRUE);
			}
		}
	}

	/*
	 * This table may have contained named anchors.
	 * This will correct their positions in the name_list,
	 * and unblock us if we were blocked on them.
	 */
	lo_CheckNameList(context, state, table->table_ele->ele_id);

	/*
	 * The usual stuff for a table.
	 */
	if (floating == FALSE)
	{
		lo_AlignStack *aptr;
		int32 indent;

		state->x = cell_x;
		state->baseline = 0;
		state->line_height = cell_y - state->y;
		state->linefeed_state = 0;
		state->at_begin_line = FALSE;
		state->trailing_space = FALSE;
		state->cur_ele_type = LO_SUBDOC;

		table->table_ele->line_height = state->line_height;

		/*
		 * Find how far the table might be indented from
		 * being inside a list.
		 */
		indent = state->list_stack->old_left_margin - state->win_left;
		if (indent < 0)
		{
			indent = 0;
		}

		save_doc_min_width = state->min_width;
		if (relayout == FALSE) 
		{
			lo_SoftLineBreak(context, state, FALSE);
		}
		else
		{
			lo_rl_AddBreakAndFlushLine( context, state, LO_LINEFEED_BREAK_SOFT, LO_CLEAR_NONE, FALSE );
		}

		if ((min_table_width + indent) > save_doc_min_width)
		{
			save_doc_min_width = min_table_width + indent;
		}
		state->min_width = save_doc_min_width;
		
		/*
		 * Pop the table's alignment.
		 */
		aptr = lo_PopAlignment(state);
		if (aptr != NULL)
		{
			XP_DELETE(aptr);
		}
	}
	/*
	 * Else this is a floating table, rip it out of the "faked"
	 * line list, stuff itin the margin, and restore our state
	 * to where we left off.
	 */
	else
	{
		int32 push_right;
		int32 line_height;
                LO_Element *tptr;
                LO_Element *last;

		line_height = cell_y - state->y;
		push_right = 0;
		if (table->table_ele->alignment == LO_ALIGN_RIGHT)
		{
			push_right = state->right_margin - cell_x;
		}

		table->table_ele->x_offset +=
			(int16)table->table_ele->border_horiz_space;
		table->table_ele->y_offset +=
			(int32)table->table_ele->border_vert_space;

		last = NULL;
		tptr = state->line_list;
		while (tptr != NULL)
		{
			tptr->lo_any.x += push_right;
			if (tptr->type == LO_CELL)
			{
				tptr->lo_any.x +=
					table->table_ele->border_horiz_space;
				tptr->lo_any.y +=
					table->table_ele->border_vert_space;
				lo_ShiftCell((LO_CellStruct *)tptr,
					(push_right +
					table->table_ele->border_horiz_space),
					table->table_ele->border_vert_space);
			}
			last = tptr;
			tptr->lo_any.line_height = line_height;
			tptr = tptr->lo_any.next;
		}

		/*
		 * Stuff the whole line list into the float list, and
		 * restore the line list to its pre-table state.
		 */
		if (state->line_list != NULL)
		{
			last->lo_any.next = state->float_list;
			state->float_list = state->line_list;
			state->line_list = NULL;
		}

		if (relayout == FALSE)
		{
			lo_AppendFloatInLineList(state, (LO_Element *)table->table_ele, save_line_list );
		}
		else
		{
			state->line_list = save_line_list;
		}
		

		table->table_ele->line_height = line_height;
		table->table_ele->expected_y = table->table_ele->y;
		table->table_ele->y = -1;

		lo_AddMarginStack(state,
			table->table_ele->x, table->table_ele->y,
                        table->table_ele->width, table->table_ele->line_height,
                        table->table_ele->border_left_width + 
                        	table->table_ele->border_right_width,
                        table->table_ele->border_vert_space,
			table->table_ele->border_horiz_space,
                        (intn)table->table_ele->alignment);

		/*
		 * Restore state to pre-table values.
		 */
		state->x = save_state_x;
		state->y = save_state_y;

		/*
		 * All the doc_min_width stuff makes state->min_width
		 * be correct for tables nested inside tables.
		 */
		save_doc_min_width = state->min_width;

		/*
		 * Standard float stuff, if we happen to be at the start
		 * of a line, place the table now.
		 */
		if (state->at_begin_line != FALSE)
		{
			lo_FindLineMargins(context, state, (! relayout));
			state->x = state->left_margin;
		}

		if (min_table_width > save_doc_min_width)
		{
			save_doc_min_width = min_table_width;
		}
		state->min_width = save_doc_min_width;

	}

	/*  Now cell records get freed when the LO_TABLE element in the line list gets recycled
	for (y=0; y < table->rows; y++)
	{
		for (x=0; x < table->cols; x++)
		{
			indx = (y * table->cols) + x;
			cell_ptr = cell_array[indx].cell;
			if ((cell_ptr != &blank_cell)&&(cell_ptr != NULL))
			{
				lo_free_cell_record(context, state, cell_ptr);
			}
		}
	}
	*/

	
#ifdef XP_WIN16
	_hfree(cell_array);
#else
	XP_UNLOCK_BLOCK(cell_array_buff);
	XP_FREE_BLOCK(cell_array_buff);
#endif

	/* 
	 * Don't wanna free the table record any more because this information
	 * is used during relayout.
	 */
	/*

	lo_free_table_record(context, state, table, FALSE);

	*/
}

/*
 * This function relaysout the tags in a table cell. It knows how to handle
 * certain types of elements that can be resused directly rather than thrown
 * away and relayedout from scratch (the default case).
 */
void
lo_RelayoutTags(MWContext *context, lo_DocState *state, PA_Tag * tag_ptr,
	PA_Tag * tag_end_ptr, LO_Element * elem_list)
{
	PA_Tag * next_tag;
	Bool save_diff_state;
	int32 save_state_pushes;
	int32 save_state_pops;
	lo_TopState *top_state;
	PA_Tag *tag_list;
	
	next_tag = NULL;
	top_state = state->top_state;

	/*
	 * Save our parent's state levels
	 */
	save_diff_state = top_state->diff_state;
	save_state_pushes = top_state->state_pushes;
	save_state_pops = top_state->state_pops;

	while (tag_ptr != tag_end_ptr)
	{
		PA_Tag *tag;
		lo_DocState *sub_state;
		lo_DocState *up_state;
		lo_DocState *tmp_state;
		Bool may_save;

		tag = tag_ptr;
		tag_ptr = tag_ptr->next;
		tag->next = NULL;
		tag_list = tag_ptr;

		up_state = NULL;
		sub_state = state;
		while (sub_state->sub_state != NULL)
		{
			up_state = sub_state;
			sub_state = sub_state->sub_state;
		}

		if ((sub_state->is_a_subdoc == SUBDOC_CELL)||
			(sub_state->is_a_subdoc == SUBDOC_CAPTION))
		{
			may_save = TRUE;
		}
		else
		{
			may_save = FALSE;
		}

		/*
		 * Reset these so we can tell if anything happened
		 */
		top_state->diff_state = FALSE;
		top_state->state_pushes = 0;
		top_state->state_pops = 0;

#ifdef MOCHA
		sub_state->in_relayout = TRUE;
#endif
		/*
		 * If we're not currently searching for a tag, then find the next
		 * one to look for.
		 */
		if ( next_tag == NULL && elem_list != NULL )
		{
			next_tag = lo_FindReuseableElement ( context, state, &elem_list );
		}
				
		/*
		 * Can we reuse this current element?
		 */
		if ( (tag == next_tag) && (elem_list != NULL) )
		{			
			LO_Element *eptr;
			LO_Element *enext;
			
			/*
			 * Yup, remove it from the list and call the object layout code to
			 * do the relayout.
			 */
			eptr = elem_list;
			enext = eptr->lo_any.next;
			
			elem_list = enext;
			
			if ( enext != NULL )
			{
				enext->lo_any.prev = NULL;
			}
			
			eptr->lo_any.next = 0L;
			eptr->lo_any.prev = 0L;
			
			switch ( eptr->type )
			{
				case LO_EMBED:
					lo_RelayoutEmbed ( context, sub_state, &eptr->lo_embed, tag );
					break;
#ifdef JAVA
				case LO_JAVA:
					lo_RelayoutJavaApp ( context, sub_state, tag, &eptr->lo_java );
					break;
#endif /* JAVA */
				
				default:
					/*
					 * We goofed and got something we don't know how to relayout
					 */
					lo_relayout_recycle ( context, state, eptr );
					lo_LayoutTag(context, sub_state, tag);
					break;
			}
			
			/*
			 * Force us to look for the next reusable element.
			 */
			next_tag = NULL;
		}
		else
		{
			/*
			 * Nope, call the standard layout code.
			 */
			lo_LayoutTag(context, sub_state, tag);
		}

		tmp_state = lo_CurrentSubState(state);
#ifdef MOCHA
		if (tmp_state == sub_state)
		{
			sub_state->in_relayout = FALSE;
		}
#endif

		tmp_state = lo_CurrentSubState(state);
#ifdef MOCHA
		if (tmp_state == sub_state)
		{
			sub_state->in_relayout = FALSE;
		}
#endif

		if (may_save != FALSE)
		{
			int32 state_diff;
			
			/* how has our state level changed? */
			state_diff = top_state->state_pushes - top_state->state_pops;
			
			/*
			 * That tag popped us up one state level.  If this new
			 * state is still a subdoc, save the tag there.
			 */
			if (state_diff == -1)
			{
				if ((tmp_state->is_a_subdoc == SUBDOC_CELL)||
				    (tmp_state->is_a_subdoc == SUBDOC_CAPTION))
				{
                    /* if we just popped a table we need to insert
                     * a dummy end tag to pop the dummy start tag
                     * we shove on the stack after createing a table
                     */
   					PA_Tag *new_tag = LO_CreateStyleSheetDummyTag(tag);
					if(new_tag)
					{
						lo_SaveSubdocTags(context, tmp_state, new_tag);
					}

				    lo_SaveSubdocTags(context, tmp_state, tag);
				}
				else
				{
				    PA_FreeTag(tag);
				}
			}
			/*
			 * Else that tag put us in a new subdoc on the same
			 * level.  It needs to be saved one level up,
			 * if the parent is also a subdoc.
			 */
			else if (( up_state != NULL ) &&
				( top_state->diff_state != FALSE ) &&
				( state_diff == 0 ))
			{
				if ((up_state->is_a_subdoc == SUBDOC_CELL)||
				     (up_state->is_a_subdoc == SUBDOC_CAPTION))
				{
				    lo_SaveSubdocTags(context, up_state, tag);
				}
				else
				{
				    PA_FreeTag(tag);
				}
			}
			/*
			 * Else we are still in the same subdoc
			 */
			else if (( top_state->diff_state == FALSE ) &&
				( state_diff == 0 ))
			{
				lo_SaveSubdocTags(context, sub_state, tag);
			}
			/*
			 * Else that tag started a new, nested subdoc.
			 * Add the starting tag to the parent.
			 */
			else if (( state_diff == 1 ))
			{
				lo_SaveSubdocTags(context, sub_state, tag);
				/*
				 * Since we have extended the parent chain,
				 * we need to reset the child to the new
				 * parent end-chain.
				 */
				if ((tmp_state->is_a_subdoc == SUBDOC_CELL)||
				    (tmp_state->is_a_subdoc == SUBDOC_CAPTION))
				{
					PA_Tag *new_tag;
					
					tmp_state->subdoc_tags =
						sub_state->subdoc_tags_end;

					/* add an aditional dummy tag so that style sheets
					 * can use it to query styles from for this entry
					 * that created a table
					 */
					new_tag = LO_CreateStyleSheetDummyTag(tag);
					if(new_tag)
					{
						lo_SaveSubdocTags(context, tmp_state, new_tag);
					}
				}
			}
			/*
			 * This can never happen.
			 */
			else
			{
				PA_FreeTag(tag);
			}
		}
		tag_ptr = tag_list;
	}

	/*
	 * Restore our parent's state levels
	 */
	top_state->diff_state = save_diff_state;
	top_state->state_pushes = save_state_pushes;
	top_state->state_pops = save_state_pops;
}


/*
 * Walk the element list, throwing them away until we find one
 * that can be reused.
 */
PA_Tag *
lo_FindReuseableElement(MWContext *context, lo_DocState *state, LO_Element ** elem_list)
{
	LO_Element * eptr;
	LO_Element * enext;
	PA_Tag * tag;
	
	tag = NULL;
	eptr = *elem_list;
	enext = NULL;
	
	/*
	 * Keep going until we've gone through all the elements
	 * or found one that we can reuse.
	 */
	while ( (eptr != NULL) && (tag == NULL) )
	{		
		enext = eptr->lo_any.next;
		
		/*
		 * Any element found by this switch statement will attempt to be relayed out
		 * using the same element structure. DO NOT put anything in here that can
		 * have atributes set by style sheets until the lo_PreLayoutTag function
		 * can correctly handle stylesheets.
		 */
		switch ( eptr->type )
		{
			case LO_EMBED:
				tag = eptr->lo_embed.tag;
				break;
#ifdef JAVA
			case LO_JAVA:
				tag = eptr->lo_java.tag;
				break;
#endif /* JAVA */
		}
		
		/*
		 * If we didn't find a tag from that element, then dispose of it and
		 * keep looking.
		 */
		if ( tag == NULL )
		{
			/*
			 * We dunno how to reuse this type of element, so throw
			 * it away and someone else will relay it out from scratch.
			 */				
			eptr->lo_any.prev = NULL;
			eptr->lo_any.next = NULL;
			lo_relayout_recycle ( context, state, eptr );
			if ( enext != NULL )
			{
				enext->lo_any.prev = NULL;
			}
			eptr = enext;
		}
	}
	/*
	 * Advance the element list to the new head
	 */
	*elem_list = eptr;
	
	return tag;
}

/* 
 * Functions separated out of lo_BeginTableAttributes
 */

void lo_PositionTableElement(lo_DocState *state, LO_TableStruct *table_ele) 
{
	table_ele->ele_id = NEXT_ELEMENT;
	table_ele->x = state->x;
	table_ele->x_offset = 0;
	table_ele->y = state->y;
	table_ele->y_offset = 0;
	table_ele->width = 0;
	table_ele->height = 0;
	table_ele->line_height = 0;

	table_ele->next = NULL;
	table_ele->prev = NULL;

	/*
	 * Push our alignment state if we're not floating
	 */
	
	if ( !(table_ele->ele_attrmask & LO_ELE_FLOATING) )
	{
		lo_PushAlignment(state, P_TABLE_DATA, table_ele->alignment);
	}
	
}


void lo_InitTableRecord( lo_TableRec *table )
{
	/* Reset row and col counters */
	table->rows = 0;
	table->cols = 0;

	/* Reset width span array */
	lo_ResetWidthSpans(table);

	table->height_span_ptr = NULL;
}

static void lo_ResetWidthSpans( lo_TableRec *table )
{
	lo_table_span *span = table->width_spans;

	while (span != NULL)
	{
		span->dim = 1;
		span->min_dim = 1;
		span->span = 0;		
		span = span->next;
	}

}

void lo_SetTableDimensions( lo_DocState *state, lo_TableRec *table, int32 allow_percent_width, int32 allow_percent_height)
{
	int32 val;

	if (table->percent_width > 0) 
	{
		val = table->percent_width;
		if (allow_percent_width == FALSE)
		{
			val = 0;
		}
		else
		{
			val = (state->win_width - state->win_left -
				state->win_right) * val / 100;
		}
		if (val < 0)
		{
			val = 0;
		}
		table->width = val;
	}
	
	if (table->percent_height > 0)
	{
		val = table->percent_height;
		if (allow_percent_height == FALSE)
		{
			val = 0;
		}
		else
		{
			val = (state->win_height - state->win_top -
				state->win_bottom) * val / 100;
		}
		if (val < 0)
		{
			val = 0;
		}
		table->height = val;
	}
}

void lo_CalcFixedColWidths( MWContext *context, lo_DocState *state, lo_TableRec *table)
{
	int32 cols = table->fixed_cols;
	if (cols > 0) {
		int32 count;
		int32 table_width;
		
		table_width = lo_ComputeInternalTableWidth ( context, table, state );
		
		/* Split the space up evenly */
		table->default_cell_width = table_width / cols;
		
		/* and we have all the table width left to play with */
		table->fixed_width_remaining = table_width;
		
		/* Initialize our width array */
		for ( count = 0; count < cols; ++count )
		{
			table->fixed_col_widths[ count ] = 0;
		}
	}
}

void lo_UpdateStateAfterBeginTable( lo_DocState *state, lo_TableRec *table)
{
	state->current_table = table;
}

/* Relayout version of lo_BeginTableRowAttributes()  */
void lo_UpdateTableStateForBeginRow(lo_TableRec *table, lo_TableRow *table_row)
{
	table_row->row_done = FALSE;
	table_row->cells = 0;
	table_row->cell_list = NULL;
	table_row->cell_ptr = NULL;
	/* table_row->next = NULL;

	if (table->row_list == NULL)
	{
		table->row_list = table_row;
		table->row_ptr = table_row;
	}
	else
	{
		table->row_ptr->next = table_row;
		table->row_ptr = table_row;
	}
	*/

	table->width_span_ptr = NULL;
	
}

/* 
 * Functions for breaking up lo_BeginTableCellAttributes()
 */
void lo_InitForBeginCell(lo_TableRow *table_row, lo_TableCell *table_cell)
{
	table_cell->must_relayout = FALSE;
	table_cell->cell_done = FALSE;
	
	if (table_row->cell_list == NULL)
	{
		table_row->cell_list = table_cell;
		table_row->cell_ptr = table_cell;
	}
	else
	{
		table_row->cell_ptr->next = table_cell;
		table_row->cell_ptr = table_cell;
	}
}

void lo_InitSubDocForBeginCell( MWContext *context, lo_DocState *state, lo_TableRec *table )
{
	lo_TableCell *table_cell = table->row_ptr->cell_ptr;
	lo_BeginCellSubDoc(context, 
						state, 
						table,
						NULL,
                        NULL,
                        LO_TILE_BOTH,
						NULL,
						NULL,
						NULL,
						NULL,
 						table_cell->is_a_header, 
						table->draw_borders,
						TRUE);

	/*
	 * We added a new state.
	 */
	lo_PushStateLevel ( context );
}

static void lo_UpdateCaptionCellFromSubDoc( MWContext *context, lo_DocState *state, LO_SubDocStruct *subdoc, LO_CellStruct *cell_ele)
{
	int32 dx = 0;
	int32 dy = 0;

	lo_CreateCellFromSubDoc(context, state, subdoc, cell_ele, &dx, &dy);
	lo_ShiftCell(cell_ele, dx, dy);
	lo_RenumberCell(state, cell_ele);
}

/* Only deletes the cell itself.  Assumes that stuff the cell points to will get deleted
   elsewhere */
static void lo_FreeCaptionCell( MWContext *context, lo_DocState *state, LO_CellStruct *cell_ele)
{
	cell_ele->next = NULL;
	cell_ele->prev = NULL;
	cell_ele->cell_list = NULL;
	cell_ele->cell_list_end = NULL;
	cell_ele->cell_float_list = NULL;
	cell_ele->table_cell = NULL;
	cell_ele->table_row = NULL;
	cell_ele->table = NULL;

	lo_FreeElement(context, (LO_Element *)cell_ele, TRUE);

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