/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/* $XConsortium: Layout.c /main/31 1996/10/25 12:00:15 cde-hp $ */
/************************************<+>*************************************
 ****************************************************************************
 **
 **   File:        Layout.c
 **
 **   Project:     CDE Info System
 **
 **   Description: Lays out the information on a canvas.
 **
 **  (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 Hewlett-Packard Company
 **
 **  (c) Copyright 1993, 1994 Hewlett-Packard Company
 **  (c) Copyright 1993, 1994 International Business Machines Corp.
 **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
 **  (c) Copyright 1993, 1994 Novell, Inc.
 **
 **
 ****************************************************************************
 ************************************<+>*************************************/

/*
 * system includes
 */
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>

/*
 * Canvas Engine includes
 */
#include "CanvasP.h"
#include "CanvasSegP.h"

/*
 * private includes
 */
#include "bufioI.h"
#include "CanvasI.h"
#include "CvStringI.h"
#include "LayoutUtilI.h"
#include "SelectionI.h"
#include "VirtFuncsI.h"

/******************************************************************************
 *
 * Private Macros
 *
 *****************************************************************************/
#define	ObjHorizOrient(x)	(_DtCvContainerOrientOfSeg(x))
#define	ObjVertOrient(x)	(_DtCvContainerVOrientOfSeg(x))
#define	TxtHorizJustify(x)	(_DtCvContainerJustifyOfSeg(x))
#define	TxtVertJustify(x)	(_DtCvContainerVJustifyOfSeg(x))
#define	Border(x)		(_DtCvContainerBorderOfSeg(x))
#define	BrdWidth(x)		(_DtCvContainerLineWidthOfSeg(x))
#define	BrdData(x)		(_DtCvContainerLineDataOfSeg(x))
#define	NotJoining(x)		(False == (x)->info.join)
#define	JoinSet(x)		(True == (x)->info.join)

#define CheckAddToHyperList(a, b) \
	_DtCvCheckAddHyperToTravList(a, b, _DtCvTRUE, \
			&(layout->info.lst_vis), \
			&(layout->info.lst_hyper), &(layout->info.cur_len))

/*****************************************************************************
 *		Private Defines
 *****************************************************************************/
/*
 * Defines for the dimension arrays
 */
#define	DIMS_LEFT	0
#define	DIMS_RIGHT	1

#define	DIMS_LM		0
#define	DIMS_CENTER	1
#define	DIMS_RM		2

#define	DIMS_TOP	0
#define	DIMS_BOTTOM	2

#define	DIMS_WIDTH	0
#define	DIMS_HEIGHT	1
#define	DIMS_YPOS	1

#define	DIMS_TC		0
#define	DIMS_BC		1


/*
 */
#define	GROW_SIZE	10

/******************************************************************************
 *
 * Private typedefs
 *
 *****************************************************************************/
/*
 * top/bottom dimension array
 *                  -------------------------------------------
 *                 / DIMS_HEIGHT / DIMS_HEIGHT / DIMS_HEIGHT /|
 *                /   DIMS_YPOS /   DIMS_YPOS /   DIMS_YPOS / |
 *               /-------------/-------------/-------------/  |
 *              / DIMS_WIDTH  / DIMS_WIDTH  / DIMS_WIDTH  /| /|
 *             --------------|-------------|-------------| |/ |
 * DIMS_TOP    |   DIMS_LM   | DIMS_CENTER |   DIMS_RM   | | /|
 *             |-------------|-------------|-------------|/|/ |
 *  unused     |             |             |             | | /
 *             |-------------|-------------|-------------|/|/
 * DIMS_BOTTOM |   DIMS_LM   | DIMS_CENTER |   DIMS_RM   | /
 *             ------------------------------------------|/
 */
typedef	_DtCvUnit	TopDims[DIMS_BOTTOM+1][DIMS_RM+1][DIMS_HEIGHT+1];

/* 
 * left/right side dimension array - contains only the height or y_pos.
 *             ----------------------------
 * DIMS_TOP    |  DIMS_LEFT | DIMS_RIGHT  |
 *             |------------|-------------|
 * DIMS_CENTER |  DIMS_LEFT | DIMS_RIGHT  |
 *             |------------|-------------|
 * DIMS_BOTTOM |  DIMS_LEFT | DIMS_RIGHT  |
 *             ----------------------------
 */
typedef	_DtCvUnit	SideDims[DIMS_BOTTOM+1][DIMS_RIGHT+1];

/*
 * corner dimension array - contains only the height or y_pos.
 *            ----------------------------
 * DIMS_TC    |  DIMS_LEFT | DIMS_RIGHT  |
 *            |------------|-------------|
 * DIMS_BC    |  DIMS_LEFT | DIMS_RIGHT  |
 *            ----------------------------
 */
typedef	_DtCvUnit	CornerDims[DIMS_BC+1][DIMS_RIGHT+1];

/*
 * flow dimension array
 *            -------------|--------------
 * DIMS_LEFT  | DIMS_WIDTH | DIMS_HEIGHT |
 *            |            |  DIMS_YPOS  |
 *            |------------|-------------|
 * DIMS_RIGHT | DIMS_WIDTH | DIMS_HEIGHT |
 *            |            |  DIMS_YPOS  |
 *            ----------------------------
 */
typedef	_DtCvUnit	FlowDims[DIMS_RIGHT+1][DIMS_HEIGHT+1];

/*
 * margin data - important for determining flowing
 */
typedef	struct	_dataPoint {
	_DtCvUnit	left;
	_DtCvUnit	right;
	_DtCvUnit	y_pos;
	_DtCvUnit	x_units;
	struct _dataPoint *next_pt;
} DataPoint;

/*
 * count information
 */
typedef	struct	_cntInfo {
	int		beg_txt;
	int		end_txt;
	int		beg_ln;
	int		end_ln;
	int		my_lines;
	int		beg_brk;
	int		end_brk;
} CntInfo;

/*
 * group information
 */
typedef	struct	_grpInfo {
	_DtCvUnit	min_x;
	_DtCvUnit	max_x;
	_DtCvUnit	top_y;
	_DtCvUnit	bot_y;
	CntInfo		cnt;
	struct _grpInfo *next_info;
} GrpInfo;

/*
 * layout information per container
 */
typedef	struct	_layFrmtInfo {
	_DtCvUnit	height;
	_DtCvUnit	width;
	CntInfo		cnt;
	struct _layFrmtInfo *next_info;
} LayFrmtInfo;

/*
 * the layout information carried around
 */
typedef	struct	_layoutInfo {
	_DtCvUnit	 max_width;	/* the current max width             */
	_DtCvUnit	 id_Ypos;	/* the y coordinate of the id found. */
	_DtCvUnit	 left;		/* the current left margin for the
					   current active container (incl br)*/
	_DtCvUnit	 right;		/* the current right margin for the
					   current active container (incl br)*/
	_DtCvUnit	 first;		/* the current first indent          */
	_DtCvUnit	 lmargin;	/* the current absolute left  margin */
	_DtCvUnit	 rmargin;	/* the current absolute right margin */
	_DtCvUnit	 divisor;	/* the current margin divisor        */
	_DtCvUnit	 string_end;
	_DtCvUnit	 sub_end;
	_DtCvUnit	 super_end;
	_DtCvValue	 id_found;	/* indicates if the id has been found*/
	_DtCvValue	 super_script;
	_DtCvValue	 sub_script;
	_DtCvValue	 stat_flag;
	_DtCvValue	 margin_non_zero;
	_DtCvValue	 brdr_flag;     /* within container with a border    */
	_DtCvValue	 table_flag;    /* within table                      */
	_DtCvFrmtOption	 txt_justify;
	unsigned int	 cur_start;	/* offset into the current segment
					   to process                        */
	_DtCvSegmentI	*lst_rendered;	/* indicates the last string/region  */
	char		*target_id;	/* if non-null, the id to search for */
        DataPoint	*data_pts;
	GrpInfo		*grp_lst;	/* list of groups		*/
	_DtCvLayoutInfo	 info;
} LayoutInfo;

/*
 * information for laying out cells in a table.
 */
typedef	struct	{
	int		 col_spn;
	int		 row_spn;
	_DtCvUnit	 pos_x;
	LayFrmtInfo	 info;
	_DtCvSegmentI	*cell_seg;
} CellInfo;

/*
 * column description information for a table.
 */
typedef	struct _columnSpec {
	_DtCvUnit	min;
	_DtCvUnit	max;
	_DtCvUnit	actual;
	_DtCvValue	hanger;
	_DtCvFrmtOption	justify;
} ColumnSpec;

/*
 * row description information
 */
typedef	struct	_rowSpec {
	int		 column;
	_DtCvUnit	 y_adj;
	_DtCvUnit	 height;
	_DtCvUnit	 lst_height;
	char		*next_id;
} RowSpec;

/*****************************************************************************
 *		Private Variables/Data
 *****************************************************************************/
static const LayFrmtInfo DefLayFrmtInfo = { 0, 0, {-1, -1, -1, -1, 0}, NULL };
static const GrpInfo     DefGrpInfo     = { 0, 0, 0, 0, {-1, -1, -1, -1, 0}};

static	const LayoutInfo DefInfo = {
	0,		/* _DtCvUnit     max_width;    */
	0,		/* _DtCvUnit	 id_Ypos;      */
	0,		/* _DtCvUnit	 left;         */
	0,		/* _DtCvUnit	 right;        */
	0,		/* _DtCvUnit	 first;        */
	0,		/* _DtCvUnit	 lmargin;      */
	0,		/* _DtCvUnit	 rmargin;      */
	1,		/* _DtCvUnit	 divisor;      */
	0,		/* _DtCvUnit	 string_end;   */
	0,		/* _DtCvUnit	 sub_end;      */
	0,		/* _DtCvUnit	 super_end;    */
	False,		/* _DtCvValue	 id_found;     */
	False,		/* _DtCvValue	 super_script; */
	False,		/* _DtCvValue	 sub_script;   */
	False,		/* _DtCvValue	 stat_flag;    */
	False,		/* _DtCvValue	 margin_non_zero; */
	False,		/* _DtCvValue	 brdr_flag;    */
	False,		/* _DtCvValue	 table_flag;   */
	_DtCvJUSTIFY_LEFT,	/* _DtCvFrmtOption txt_justify;   */
	0,		/* unsigned int	 cur_start;    */
	NULL,		/* _DtCvSegmentI *lst_rendered; */
	NULL,		/* char		*target_id;    */
        NULL,		/* DataPoint	*data_pts;     */
        NULL,		/* GrpInfo  	*data_pts;     */
};

static const DataPoint	DefDataPt = { 0, 0, _CEFORMAT_ALL, 0, NULL};

static const double	HeadDivisor = 10000.0;

static const char *PeriodStr = ".";

static const char  *DefWidth[2] = { "", NULL };

static	const	_DtCvSegmentI	BlankTableCell =
  {
	_DtCvCONTAINER,		/* type         */
	-1,			/* link_idx     */
	  {			/* container info */
	    NULL,		  /* id           */
	    NULL,		  /* justify_char */
	    _DtCvDYNAMIC,	  /* type         */
	    _DtCvBORDER_NONE,	  /* border       */
	    _DtCvJUSTIFY_LEFT,	  /* justify      */
	    _DtCvJUSTIFY_TOP,	  /* vjustify     */
	    _DtCvJUSTIFY_LEFT,	  /* orient       */
	    _DtCvJUSTIFY_TOP,	  /* vorient      */
	    _DtCvWRAP_NONE,	  /* flow         */
	    0,			  /* percent      */
	    0,			  /* leading      */
	    0,			  /* fmargin      */
	    0,			  /* lmargin      */
	    0,			  /* rmargin      */
	    0,			  /* tmargin      */
	    0,			  /* bmargin      */
	    _DtCvBORDER_NONE,  /* bdr_info     */
	    NULL 		  /* seg_list     */
	  },
	NULL,			/* next_seg     */
	NULL,			/* next_disp    */
	NULL,			/* client_use   */
	NULL 			/* internal_use */
  };

/*****************************************************************************
 *		Private Function Declarations
 *****************************************************************************/
static	void		AdjustForBorders(
				_DtCanvasStruct	*canvas,
				LayoutInfo	*layout,
				_DtCvFrmtOption	 brdr,
				_DtCvUnit	 line_width,
				_DtCvUnit	*ret_bot,
				_DtCvUnit	*ret_right);
static	void		AdjustHeadPosition(
				_DtCanvasStruct	*canvas,
				LayoutInfo	*layout,
				_DtCvSegmentI	*p_seg,
				TopDims		*top_bot,
				SideDims	*side,
				CornerDims	*corner,
				FlowDims	*flow,
				LayFrmtInfo	*info,
				_DtCvUnit	 base_y,
				_DtCvUnit	 base_left,
				_DtCvUnit	 block_width,
				_DtCvUnit	 left_margin,
				_DtCvUnit	 right_margin);
static	void		AdjustObjectPosition(
				_DtCanvasStruct	*canvas,
				LayoutInfo	*layout,
				_DtCvFrmtOption	 justify,
				int		 start_txt,
				int		 start_ln,
				int		 start_brk,
				int		 end_txt,
				int		 end_ln,
				int		 end_brk,
				int		 brdr_cnt,
				_DtCvUnit	 height_adj,
				_DtCvUnit	 x_adj,
				_DtCvUnit	 y_adj,
				_DtCvUnit	 internal_y);
static	void		CheckSaveInfo (
				_DtCanvasStruct	*canvas,
				LayoutInfo	*layout,
				_DtCvSegmentI	*new_seg,
				int		 start);
static	void		DetermineFlowConstraints(
				LayoutInfo	*layout,
				FlowDims	 flow_dims,
				_DtCvUnit	 left_margin,
				_DtCvUnit	 right_margin,
				_DtCvUnit	 start_y,
				DataPoint	*left_pt,
				DataPoint	*right_pt);
static	void		DetermineHeadPositioning(
				TopDims	*top_bot,
				SideDims	*side,
				CornerDims	*corner,
				FlowDims	*flow,
				_DtCvUnit	 start_y,
				_DtCvUnit	 max_top,
				_DtCvUnit	 block_size,
				_DtCvUnit	*ret_side_size);
static	void		DetermineMaxDims(
				TopDims		*top_bot,
				CornerDims	*corner,
				_DtCvUnit	 left_margin,
				_DtCvUnit	 right_margin,
				_DtCvUnit	*top_height,
				_DtCvUnit	*bot_height,
				_DtCvUnit	*max_width);
static	int		DrawBorders(
				_DtCanvasStruct	*canvas,
				LayoutInfo	*layout,
				_DtCvFrmtOption	 brdr,
				_DtCvPointer	 data,
				_DtCvUnit	 line_width,
				_DtCvUnit	 top_y,
				_DtCvUnit	 bot_y,
				_DtCvUnit	 left_x,
				_DtCvUnit	 right_x);
static void		FormatCell(
				_DtCanvasStruct	*canvas,
				LayoutInfo	*layout,
				_DtCvSegmentI	*cell_seg,
				_DtCvUnit	 span_width,
				_DtCvUnit	 min_height,
				DataPoint	 base_pt,
				int		*ret_ln,
				_DtCvUnit	*ret_width,
				_DtCvUnit	*ret_height,
				_DtCvValue	*ret_tab_flag);
static	void		InitDimArrays(
				TopDims		*top_bot,
				SideDims	*side,
				CornerDims	*corner,
				FlowDims	*flow);
static _DtCvStatus	LayoutCanvasInfo (
				_DtCanvasStruct	*canvas,
				LayoutInfo	*layout,
				_DtCvUnit	 divisor,
				char		*target_id);
static _DtCvValue	LinesMayChange(
				_DtCanvasStruct	*canvas,
				int		 start_ln,
				int		 end_ln,
				int		 brdr_cnt);
static _DtCvUnit	MoveGroup (
				_DtCanvasStruct	*canvas,
				LayoutInfo	*layout,
				GrpInfo		*tst_grp,
				_DtCvUnit	 needed);
static _DtCvUnit	MoveLines (
				_DtCanvasStruct	*canvas,
				LayoutInfo	*layout,
				int		 idx,
				_DtCvUnit	 top_y,
				_DtCvUnit	 bot_y,
				_DtCvUnit	 needed);
static _DtCvUnit	MoveText (
				_DtCanvasStruct	*canvas,
				LayoutInfo	*layout,
				int		 idx,
				_DtCvUnit	 top_y,
				_DtCvUnit	 bot_y,
				_DtCvUnit	 needed);
static void		ProcessContainer(
				_DtCanvasStruct	*canvas,
				LayoutInfo	*layout,
				_DtCvSegmentI	*con_seg,
				_DtCvUnit	 min_y,
				_DtCvUnit	*ret_width,
				_DtCvUnit	*ret_max_x,
				int		*ret_cnt);
static	void		ProcessSegmentList(
				_DtCanvasStruct	*canvas,
				LayoutInfo	*layout,
				_DtCvSegmentI	*cur_seg,
				_DtCvUnit	 min_y,
				_DtCvUnit	*ret_width,
				_DtCvUnit	*ret_max_x,
				int		**ret_vert);

/******************************************************************************
 *
 * Private Functions
 *
 *****************************************************************************/
/******************************************************************************
 * Function: AdjustTextPositions
 *
 * changes the baseline and text_x positioning of the text line by the
 * offsets indicated.
 *****************************************************************************/
static void
AdjustTextPositions(
    _DtCanvasStruct     *canvas,
    int			 beg,
    int			 end,
    _DtCvUnit		 x_offset,
    _DtCvUnit		 y_offset)
{
    if ( 0 != y_offset || 0 != x_offset)
      {
        while (beg < end)
          {
	    canvas->txt_lst[beg].baseline += y_offset;
	    canvas->txt_lst[beg].text_x   += x_offset;
	    beg++;
          }
      }
}

/******************************************************************************
 * Function: AdjustLinePositions
 *
 * changes the x & positions and the max x & y values by the offsets indicated.
 *****************************************************************************/
static void
AdjustLinePositions(
    _DtCanvasStruct     *canvas,
    int			 beg,
    int			 end,
    _DtCvUnit		 x_offset,
    _DtCvUnit		 y_offset)
{
    if ( 0 != y_offset || 0 != x_offset)
      {
        while (beg < end)
          {
	    canvas->line_lst[beg].pos_y += y_offset;
	    canvas->line_lst[beg].max_y += y_offset;
	    canvas->line_lst[beg].pos_x += x_offset;
	    canvas->line_lst[beg].max_x += x_offset;

	    beg++;
          }
      }
}

/******************************************************************************
 * Function: AdjustPgBrk
 *
 * changes the y position of a page break.
 *****************************************************************************/
static void
AdjustPgBrk(
    _DtCanvasStruct     *canvas,
    int			 beg,
    int			 end,
    _DtCvUnit		 y_offset)
{
    if (0 != y_offset)
      {
        while (beg < end)
	    canvas->pg_breaks[beg++] += y_offset;
      }
}

/******************************************************************************
 * Function: MaxXOfLine
 *
 *  MaxXOfLine determines the max X of a line segment.
 *
 *****************************************************************************/
/******************************************************************************
 * Function: MaxXOfLine
 *
 *  MaxXOfLine determines the max X of a line segment.
 *
 *****************************************************************************/
static _DtCvUnit
MaxXOfLine(
    _DtCanvasStruct     *canvas,
    _DtCvDspLine        *line)
{
    _DtCvValue   lastLinkVisible = FALSE;
    int          count        = line->length;
    int          start        = line->byte_index;
    int          len;
    int          lnkInd = -1;
    _DtCvUnit    xPos;
    _DtCvUnit    tmpWidth;
    _DtCvSegmentI *pSeg;

    xPos = _DtCvGetStartXOfLine(line, &pSeg);

    while (pSeg != NULL && count > 0)
      {
        xPos = _DtCvAdvanceXOfLine(canvas, pSeg, xPos,
						&lnkInd, &lastLinkVisible);

        _DtCvGetWidthOfSegment(canvas, pSeg, start, count,
							&len, &tmpWidth, NULL);
        xPos  += tmpWidth;
        count -= len;
        start  = 0;
        pSeg   = pSeg->next_disp;
      }

    return xPos;

} /* End MaxXOfLine */

/*****************************************************************************
 * Function:	void GetLinkInfo ()
 *
 * Parameters:
 *		canvas		Specifies the handle for the canvas.
 *
 * Returns:	A handle to the canvas or NULL if an error occurs.
 *
 * Purpose:
 *
 *****************************************************************************/
static _DtCvValue
GetLinkInfo (
    _DtCvHandle	  canvas_handle,
    int		  indx,
    _DtCvUnit	 *ret_x,
    _DtCvUnit	 *ret_y,
    _DtCvUnit	 *ret_width,
    _DtCvUnit	 *ret_height)
{
    int			 len;
    int			 line;
    int			 count;
    int			 startChar;
    int			 lnkIndx   = -1;
    _DtCvUnit		 startX;
    _DtCanvasStruct	*canvas    = (_DtCanvasStruct *) canvas_handle;
    _DtCvSegmentI	*pSeg;
    _DtCvValue           lstVisible = False;
    _DtCvValue		 found      = False;
    _DtCvValue		 junk;
    _DtCvUnit  endX = 0;
    void                *pChar;

    /*
     * get the line index
     */
    line        = canvas->trav_lst[indx].idx;

    /*
     * get some information from the line
     */
    pSeg        = canvas->txt_lst[line].seg_ptr;
    count       = canvas->txt_lst[line].length;
    startChar   = canvas->txt_lst[line].byte_index;
    startX      = canvas->txt_lst[line].text_x;
    *ret_y      = canvas->txt_lst[line].baseline - canvas->txt_lst[line].ascent;
    *ret_height = canvas->txt_lst[line].ascent + canvas->txt_lst[line].descent;

    while (count > 0 && !found && pSeg != NULL)
      {
	if (startX < canvas->txt_lst[line].text_x)
	    startX = canvas->txt_lst[line].text_x;

	/*
	 * adjust the starting position by the link space
	 */
	junk = _DtCvIsSegVisibleLink(pSeg);
	lstVisible = _DtCvModifyXpos (canvas->link_info, pSeg, junk,
		    lstVisible, lnkIndx, &startX);
	/*
	 * adjust the starting position by the traversal space
	 */
	junk = _DtCvIsSegALink(pSeg);
	(void) _DtCvModifyXpos (canvas->traversal_info, pSeg, junk,
		    ((_DtCvValue) True), lnkIndx, &startX);

	lnkIndx = pSeg->link_idx;

	/*
	 * skip no-op
	 */
	if (_DtCvIsSegNoop(pSeg))
	    len = 0;
  
	/*
	 * check region
	 */
	else if (_DtCvIsSegRegion(pSeg))
	  {
	    len  = 1;
	    endX = startX + _DtCvWidthOfRegionSeg(pSeg);
	  }
	else
	  {
	    /*
	     * initialize the pointer to the string
	     */
	    pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg),
					_DtCvIsSegWideChar(pSeg), startChar);
 
	    /*
	     * get the length of the current string.
	     * If it is longer than the line count indicates,
	     * it must be wrapped to the next line. We are
	     * only interested in in the part of the line
	     * that is on the line selected.
	     */
	    len = _DtCvStrLen (pChar, _DtCvIsSegWideChar(pSeg));
	    if (len > count)
		len = count;
	 
	    /*
	     * calculate the ending pixel postion for
	     * this string segment.
	     */
	    endX = startX + _DtCvGetStringWidth(canvas,pSeg,pChar,len);

	  }

	/*
	 * test to see if the selected segment was this segment.
	 */
	if (pSeg == canvas->trav_lst[indx].seg_ptr)
	  {
	    found = True;
	    *ret_x     = startX;
	    *ret_width = endX - startX;
	  }
	else
	  {
	    /*
	     * go to the next segment.
	     */
	    pSeg = pSeg->next_disp;

	    /*
	     * adjust for the new begining.
	     */
	    startX    = endX;
	    count     = count - len;
	    startChar = 0;
	  }
      }

    return found;
}
/******************************************************************************
 * Function: CheckId
 *
 * Check to see if the id matches the target id.
 * If so, set the 'found id' flag and indicate its y coordinate.
 *****************************************************************************/
static void
CheckId(
    LayoutInfo		*layout,
    char		*id)
{
    if (layout->target_id != NULL && NULL != id &&
			_DtCvStrCaseCmpLatin1(id, layout->target_id) == 0)
      {
        layout->id_Ypos  = layout->info.y_pos;
        layout->id_found = True;
      }
}

/******************************************************************************
 * Function: CheckSetLineStart
 *
 * Check to see if the line information is at the beginning.
 * If so, set the pointers to the current segment and offset.
 *****************************************************************************/
static void
CheckSetLineStart(
    LayoutInfo		*layout,
    _DtCvSegmentI	*cur_seg)
{
    if (layout->info.line_bytes == 0)
      {
	layout->info.line_seg   = cur_seg;
	layout->info.line_start = 0;
      }
}

/******************************************************************************
 * Function: CheckForPageBreak
 *
 * Check to see if there is a page break on this segment.
 * If so, remember its position.
 *****************************************************************************/
static int
CheckForPageBreak(
    _DtCanvasStruct	*canvas,
    _DtCvSegmentI	*cur_seg,
    _DtCvUnit		 position)
{
    /*
     * does this segment cause a page break?
     */
    if (_DtCvIsSegPageBreak(cur_seg))
      {
	/*
	 * check to see if there is room to save the page break.
	 * if not, allocate more room.
	 */
	if (canvas->brk_cnt >= canvas->brk_max)
	  {
	    canvas->brk_max += GROW_SIZE;
	    if (NULL != canvas->pg_breaks)
	        canvas->pg_breaks = (_DtCvUnit *) realloc (
					(void *) canvas->pg_breaks,
					(sizeof(_DtCvUnit) * canvas->brk_max));
	    else
	        canvas->pg_breaks = (_DtCvUnit *) malloc (
					(sizeof(_DtCvUnit) * canvas->brk_max));
	  }

	/*
	 * failed to allocate memory, abort.
	 */
	if (NULL == canvas->pg_breaks)
	  {
	    canvas->brk_max = 0;
	    canvas->brk_cnt = 0;
	    return 1;
	  }

	/*
	 * save the y position of the page break for later.
	 */
	canvas->pg_breaks[canvas->brk_cnt++] = position;
      }

    return 0;
}

/******************************************************************************
 * Function:	SetBeginCounts
 *
 * Parameters:
 *		canvas	Specifies the canvas.
 *		f_info	Specifies the layout count info.
 *
 * Returns:	Nothing
 *
 * Purpose:	Initializes the layout count information for a container.
 *****************************************************************************/
static	void
SetBeginCounts (
    _DtCanvasStruct	*canvas,
    CntInfo		*cnt_info)
{
    /*
     * text counts
     */
    cnt_info->beg_txt = canvas->txt_cnt;
    cnt_info->end_txt = canvas->txt_cnt;

    /*
     * line counts
     */
    cnt_info->beg_ln = canvas->line_cnt;
    cnt_info->end_ln = canvas->line_cnt;

    /*
     * break counts
     */
    cnt_info->beg_brk = canvas->brk_cnt;
    cnt_info->end_brk = canvas->brk_cnt;
}

/******************************************************************************
 * Function:	SetEndCounts
 *
 * Parameters:
 *		canvas	Specifies the canvas.
 *		f_info	Specifies the layout count info.
 *
 * Returns:	Nothing
 *
 * Purpose:	Sets the ending layout counts for a container.
 *****************************************************************************/
static	void
SetEndCounts (
    _DtCanvasStruct	*canvas,
    CntInfo		*cnt_info,
    int			 end_ln)
{
    /*
     * text counts
     */
    cnt_info->end_txt = canvas->txt_cnt;

    /*
     * line counts
     */
    cnt_info->end_ln = canvas->line_cnt;

    /*
     * break counts
     */
    cnt_info->end_brk = canvas->brk_cnt;

    /*
     * the number of lines for this container.
     * If negative, indicates the first line is for the
     * bottom of the container and goes the length.
     */
    cnt_info->my_lines = end_ln;
}

/******************************************************************************
 * Function:	SkipToNumber
 *
 * Returns:
 *****************************************************************************/
static	void
SkipToNumber (
    const char	**string)
{
    if (string != NULL)
      {
        const char *str = *string;

        while (*str == ' ' && *str != '\0')
	    str++;
        *string = str;
      }
}

/******************************************************************************
 * Function:	GetValueFromString
 *
 * Returns:
 *****************************************************************************/
static	int
GetValueFromString (
    const char	**string,
    int		  def_num)
{
    int  value = def_num;

    if (string != NULL && *string != NULL && **string != '\0')
      {
        const char *str = *string;

	if ('0' <= *str && *str <= '9')
	    value = atoi(str);

	while ('0' <= *str && *str <= '9')
	    str++;

	while (*str != ' ' && *str != '\0' && (*str < '0' || *str > '9'))
	    str++;

        *string = str;
      }

    return value;
}

/******************************************************************************
 * Function:	PushDataPoint
 *
 * Returns:
 *****************************************************************************/
static	void
PushDataPoint (
    LayoutInfo		*layout,
    DataPoint		*data_pt)
{
    data_pt->x_units = 0;
    data_pt->next_pt = layout->data_pts;
    layout->data_pts = data_pt;
}

/******************************************************************************
 * Function:	InsertDataPoint
 *
 * Returns:
 *****************************************************************************/
static	void
InsertDataPoint (
    LayoutInfo		*layout,
    DataPoint		*data_pt)
{
    DataPoint	*lastPt = NULL;
    DataPoint	*nextPt = layout->data_pts;

    while (nextPt != NULL &&
	nextPt->y_pos != _CEFORMAT_ALL &&
	(data_pt->y_pos == _CEFORMAT_ALL || nextPt->y_pos < data_pt->y_pos))
      {
	lastPt = nextPt;
	nextPt = nextPt->next_pt;
      }

    data_pt->next_pt = nextPt;
    data_pt->x_units = 0;

    if (lastPt == NULL)
        layout->data_pts = data_pt;
    else
	lastPt->next_pt = data_pt;
}

/******************************************************************************
 * Function:	RemoveDataPoint
 *
 * Returns:
 *****************************************************************************/
static	void
RemoveDataPoint (
    LayoutInfo		*layout,
    DataPoint		*data_pt)
{
    DataPoint	*lastPt = NULL;
    DataPoint	*curPt  = layout->data_pts;

    while (curPt != NULL && curPt != data_pt)
      {
	lastPt = curPt;
	curPt  = curPt->next_pt;
      }

    if (curPt != NULL)
      {
        data_pt->x_units = layout->info.cur_max_x - data_pt->left;
        if (lastPt == NULL)
	    layout->data_pts = curPt->next_pt;
        else
	    lastPt->next_pt  = curPt->next_pt;
      }
}

/******************************************************************************
 * Function:	GetCurrentDataPoint
 *
 * Returns:
 *****************************************************************************/
static	void
GetCurrentDataPoint (
    LayoutInfo		*layout,
    DataPoint		*data_pt)
{
    data_pt->left  = 0;
    data_pt->right = 0;
    data_pt->y_pos = _CEFORMAT_ALL;

    if (layout->data_pts != NULL)
	*data_pt = *(layout->data_pts);
}

/******************************************************************************
 * Function:	SetMargins
 *
 * Purpose: Sets the margins.
 *****************************************************************************/
static	void
SetMargins (
    LayoutInfo		*layout)
{
    layout->lmargin = 0;
    layout->rmargin = 0;
    layout->info.format_y = _CEFORMAT_ALL;

    if (layout->data_pts != NULL)
      {
	/*
	 * base 
	 */
	layout->lmargin = layout->data_pts->left;
	layout->rmargin = layout->data_pts->right;

        layout->info.format_y = layout->data_pts->y_pos;
      }

    layout->lmargin += layout->left;
    layout->rmargin += layout->right;
}

/******************************************************************************
 * Function:	SetTextPosition
 *
 * Purpose: Sets the text beginning position to the left margin.
 *	    If 'first' is true, adds its value to the text beginning
 *	    position value.
 *****************************************************************************/
static	void
SetTextPosition (
    LayoutInfo		*layout,
    _DtCvValue		 first)
{
    layout->info.text_x_pos = layout->lmargin;
    if (first == True)
        layout->info.text_x_pos += layout->first;
}

/******************************************************************************
 * Function:	CheckFormat
 *
 * Purpose: Checks to see if the flowing txt boundaries have been exceeded.
 *          If a boundary has been exceeded, then removes that boundary
 *	    information from the stack until it finds a valid boundary point.
 *
 *	    Calls SetMargins to set the correct margin
 *	    Calls SetTextPosition to set the text beginning position.
 *****************************************************************************/
static	void
CheckFormat (
    LayoutInfo		*layout,
    _DtCvValue		 first)
{
    while (layout->data_pts != NULL &&
		layout->data_pts->y_pos != _CEFORMAT_ALL &&
				layout->data_pts->y_pos < layout->info.y_pos)
	RemoveDataPoint (layout, layout->data_pts);

    SetMargins(layout);
    SetTextPosition(layout, first);
}

/******************************************************************************
 * Function: SaveLine
 *
 * Initializes a line table element to the segment it should display.
 *****************************************************************************/
static void
SaveLine (
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    int			 direction,
    _DtCvPointer	 data,
    _DtCvUnit		 line_width,
    _DtCvUnit		 x,
    _DtCvUnit		 y,
    _DtCvUnit		 length)
{
    int       i  = canvas->line_cnt;
    _DtCvUnit x2 = x;
    _DtCvUnit y2 = y;

    if (i >= canvas->line_max)
      {
	canvas->line_max += GROW_SIZE;
	if (canvas->line_lst)
	    canvas->line_lst = (_DtCvLineSeg *) realloc (
				(void *) canvas->line_lst,
				(sizeof(_DtCvLineSeg) * canvas->line_max));
	else
	    canvas->line_lst = (_DtCvLineSeg *) malloc (
				(sizeof(_DtCvLineSeg) * canvas->line_max));
/*
 * NOTE....should this routine return a value?
 * If (re)alloc error occurs, this simply ignores the problem.
 */
	if (canvas->line_lst == NULL)
	  {
	    canvas->line_max = 0;
	    canvas->line_cnt = 0;
	    return;
	  }
      }

    /*
     * does this line exceed the current maximum?
     */
    if (_DtCvLINE_HORZ == direction)
      {
	x2 += length;
	y2 += line_width;
      }
    else
      {
	x2 += line_width;
	y2 += length;
      }

    if (layout->info.max_x_pos < x2)
	layout->info.max_x_pos = x2;

    if (layout->info.cur_max_x < x2)
	layout->info.cur_max_x = x2;

    /*
     * save the line information
     */
    canvas->line_lst[i].dir    = direction;
    canvas->line_lst[i].pos_x  = x;
    canvas->line_lst[i].max_x  = x2;
    canvas->line_lst[i].pos_y  = y;
    canvas->line_lst[i].max_y  = y2;
    canvas->line_lst[i].width  = line_width;
    canvas->line_lst[i].data   = data;

    canvas->line_cnt++;
}

/******************************************************************************
 * Function: SaveInfo
 *
 * Purpose: Saves the current information into the txt line struct.
 *          Checks to see if this line exceeds the flowing text
 *          boundary and resets the internal global margins.
 *****************************************************************************/
static void
SaveInfo (
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvSegmentI	*new_seg,
    int			 start)
{
    _DtCvSaveInfo (canvas, &(layout->info),
		  layout->max_width, layout->rmargin, layout->txt_justify);

    while (layout->info.delayed_search_saves > 0) {
	_DtCvSetSearchEntryInfo(canvas, canvas->txt_cnt - 1);
	layout->info.delayed_search_saves--;
    }

    layout->super_end       = 0;
    layout->sub_end         = 0;
    layout->super_script    = False;
    layout->sub_script      = False;
    layout->info.line_seg   = new_seg;
    layout->info.line_start = start;
    layout->info.cur_len    = 0;

    CheckFormat(layout, FALSE);
}

/******************************************************************************
 * Function: CheckSaveInfo
 *
 * Purpose: Checks to see if there is any information to save and saves
 *          it if there is any.
 *****************************************************************************/
static void
CheckSaveInfo (
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvSegmentI	*new_seg,
    int			 start)
{
    if (layout->info.line_bytes)
	SaveInfo (canvas, layout, new_seg, start);
}

/******************************************************************************
 * Function: ProcessStringSegment
 *
 * chops a string segment up until its completely used.
 *
 *****************************************************************************/
static void
ProcessStringSegment(
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvSegmentI	*cur_seg)
{
    layout->cur_start = 0;
    if (!(_DtCvIsSegSuperScript(cur_seg) || _DtCvIsSegSubScript(cur_seg))
	&& (layout->super_script == True || layout->sub_script == True))
      {
	layout->super_end = 0;
	layout->sub_end   = 0;
	layout->super_script = False;
	layout->sub_script   = False;
      }

    while (_DtCvProcessStringSegment(canvas, &(layout->info),
			layout->max_width, layout->lmargin, layout->rmargin,
			cur_seg , &(layout->cur_start),
			layout->txt_justify, layout->stat_flag) == 1)
	CheckFormat(layout, False);

} /* End ProcessStringSegment */


/******************************************************************************
 * Function: _DtCvUnit ResolveHeight(
 *
 * Parameters:
 *
 * Purpose: Determines the height of a row that is spanned.
 *****************************************************************************/
static _DtCvUnit
ResolveHeight(
    RowSpec  *row_info,
    CellInfo *cell_info,
    int       max_cols,
    int       row,
    int       span)
{
    int i;
    int col;
    int cell;
    int topRow;
    int topCell;
    int zeroed;
    int total;
    int rowHeight = 0;

    for (col = 0, cell = row * max_cols; col < max_cols; col++, cell++)
      {
	/*
	 * if we have a row spanning cell in this column,
	 * but it isn't from a previous column, try to fill out
	 * the height using it.
	 */
	if (cell_info[cell].row_spn == -1 && cell_info[cell].col_spn != -1)
	  {
	    /*
	     * back up to the cell information containing the row
	     * span value.
	     */
	    topRow  = row;
	    topCell = cell;
	    do
	      {
		topCell--;
		topRow--;
	      } while (cell_info[topCell].row_spn == -1);

	    /*
	     * from here, start calculating the height of the row
	     * spanning cell, and find out how big the row must
	     * be to contain it.
	     */
	    i      = cell_info[topCell].row_spn;
	    zeroed = span + 1;
	    total  = 0;
	    while (i > 0 && zeroed > 0)
	      {
		total += row_info[topRow].height;
		if (row_info[topRow].height == 0)
		   zeroed--;
		i--;
		topRow++;
	      }

	    /*
	     * if zeroed is greater than zero, that means only the
	     * allowd number of row heights were zeroed out (the ones
	     * we are looking for).
	     * go ahead and calculate a new height. Otherwise,
	     * it may be tried later.
	     */
	    if (zeroed > 0)
	      {
		/*
		 * make sure we get a positive value out of
		 * this for our height.
		 */
		if (cell_info[topCell].info.height > total)
		  {
		    total = cell_info[topCell].info.height - total;
		    total = total / span + (total % span ? 1 : 0);
		  }

		/*
		 * make sure we take the biggest value possible
		 * for this row. If it needs to be smaller for
		 * cells, we'll adjust the positioning within
		 * the cell.
		 */
		if (rowHeight < total)
		    rowHeight = total;
	      }
	  }
      }

   row_info[row].height = rowHeight;
   return rowHeight;
}

/******************************************************************************
 * Function: void AdjustHeight(
 *
 * Parameters:
 *
 * Purpose: Determines the height of rows that are spanned but the 
 *          the spanner needs more room than the calculated row height
 *          allow.
 *****************************************************************************/
static void
AdjustHeight(
   CellInfo	*cell_info,
   RowSpec	*row_specs,
   int		 max_cols,
   int		 row,
   int		 col)
{
    int    cell = row * max_cols + col;
    int    i, j;
    _DtCvUnit   total;
    _DtCvUnit   adjustValue;
    _DtCvUnit   value;

    if (cell_info[cell].col_spn == -1 || cell_info[cell].row_spn == -1)
	return;

    for (j = row, i = 0, total = 0; i < cell_info[cell].row_spn; i++, j++)
	total += row_specs[j].height;

    /*
     * adjust the row height to include all of the row spanning cell.
     */
    if (total < cell_info[cell].info.height)
      {
	_DtCvUnit   totalUsed = 0;

	/*
	 * first, try to grow the cells on a percentage basis.
	 * This way, a small cell will grow the same amount relative
	 * to the larger cells.
	 */
	adjustValue = cell_info[cell].info.height - total;

	/*
	 * now if total is zero, we'll get a divide by zero error.
	 * So check for this and set total to the number of rows spanned.
	 */
	if (0 == total)
	    total = cell_info[cell].row_spn;

	for (i = 0; i < cell_info[cell].row_spn; i++)
	  {
	    value = (((row_specs[row + i].height * 100) / total) * adjustValue)
				/ 100;
	    row_specs[row + i].height += value;
	    totalUsed                 += value;
	  }

	/*
	 * if didn't use all the size up - apply it evenly.
	 */
	if (totalUsed < adjustValue)
	  {
	    adjustValue = adjustValue - totalUsed;
	    for (i = 0, j = cell_info[cell].row_spn;
		adjustValue > 0 && i < cell_info[cell].row_spn; i++, j--)
	      {
		value = adjustValue / j + (adjustValue % j ? 1 : 0);
		row_specs[row + i].height += value;
		adjustValue               -= value;
		totalUsed                 += value;
	      }
	  }

	total = totalUsed;
      }
}

/******************************************************************************
 * Function: void ReFormatCell(
 *
 * Parameters:
 *		canvas		Specifies the specific information about the
 *				rendering area.
 *		layout		Specifies the currently active information
 *				affecting the layout of information.
 *
 * Purpose: Based on a height and width, relay out the information in a cell.
 *****************************************************************************/
static void
ReFormatCell(
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    CellInfo		*this_cell,
    ColumnSpec		*col_specs,
    int			 col,
    _DtCvUnit		 new_height,
    _DtCvUnit		 new_y)
{
    int         i;
    int         saveTxt   = canvas->txt_cnt;
    int         saveLn    = canvas->line_cnt;
    _DtCvUnit   saveYpos  = layout->info.y_pos;
    _DtCvUnit   saveMaxX  = layout->info.cur_max_x;
    _DtCvUnit	cellWidth = 0;
    _DtCvUnit	junk;
    _DtCvValue	junkValue;
    DataPoint	basePt;

    /*
     * reset the y_pos to the correct placement.
     * reset the line counts to the original values.
     * since we aren't changing the width, the number
     * of lines used will not change.
     */
    layout->info.y_pos = new_y;
    canvas->txt_cnt    = this_cell->info.cnt.beg_txt;
    canvas->line_cnt   = this_cell->info.cnt.beg_ln;

    /*
     * determine the maximum width for the cell.
     */
    for (i = this_cell->col_spn; i > 0; i--)
	cellWidth += col_specs[col++].actual;

    /*
     * get the current left and right margins.
     */
    GetCurrentDataPoint(layout, &basePt);

    /*
     * re-format the cell
     */
    FormatCell(canvas, layout, this_cell->cell_seg, cellWidth,
				new_height,
				basePt, &i, &cellWidth, &junk, &junkValue);

    /*
     * calculate the new height.
     */
    this_cell->info.height = layout->info.y_pos - new_y;

    /*
     * if the new cell does not use the same number of lines as the
     * old formatting did, zero the length.
     */
    while (canvas->txt_cnt < this_cell->info.cnt.end_txt)
	canvas->txt_lst[canvas->txt_cnt++].length = 0;

    /*
     * restore the saved counters
     */
    canvas->txt_cnt        = saveTxt;
    canvas->line_cnt       = saveLn;
    layout->info.y_pos     = saveYpos;
    layout->info.cur_max_x = saveMaxX;
}


/******************************************************************************
 * Function: void FormatCell(
 *
 * Parameters:
 *		canvas		Specifies the specific information about the
 *				rendering area.
 *		layout		Specifies the currently active information
 *				affecting the layout of information.
 *		span_width	Specifies the desired size constraining
 *				the layout of information.
 *		base_pt		Specifies the base margins.
 *		this_cell	Specifies the cell structure to fill out.
 *
 * Purpose: Determines the width and height of the cell. Also the begin/end
 *          counts on text, graphics and lines.
 *****************************************************************************/
static void
FormatCell(
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvSegmentI	*cell_seg,
    _DtCvUnit		 span_width,
    _DtCvUnit		 min_height,
    DataPoint		 base_pt,
    int			*ret_ln,
    _DtCvUnit		*ret_width,
    _DtCvUnit		*ret_height,
    _DtCvValue		*ret_tab_flag)
{
    _DtCvStatus only1Col  = True;
    _DtCvUnit   maxX;
    _DtCvUnit   minY      = -1;
    _DtCvUnit   saveYpos  = layout->info.y_pos;
    _DtCvUnit   saveRight = layout->right;
    _DtCvUnit   saveMaxX  = layout->info.cur_max_x;
    _DtCvSegmentI *nextSeg;

    /*
     * set the limits/margins
     * assume the left_margin has been set by a previous call.
     */
    layout->right = layout->max_width - base_pt.left - base_pt.right
				- layout->left - span_width;
    /*
     * set the minimum Y for the container.
     */
    if (0 < min_height)
	minY = saveYpos + min_height;

    /*
     * If a segment was specified for this cell, format it.
     */
    if (cell_seg != NULL && &BlankTableCell != cell_seg)
      {
        ProcessContainer(canvas, layout, cell_seg, minY,
					ret_width, &maxX, ret_ln);

	/*
	 * check to see if the only thing in this container is a
	 * table.
	 */
	nextSeg = _DtCvContainerListOfSeg(cell_seg);
	*ret_tab_flag = True;
	while (True == *ret_tab_flag && NULL != nextSeg)
	  {
	     /*
	      * check to see if there is any segments that aren't one
	      * column tables.
	      */
	     if (_DtCvIsSegTable(nextSeg) &&
					1 < _DtCvNumColsOfTableSeg(nextSeg))
		only1Col = False;

	     else if (!(_DtCvIsSegTable(nextSeg) || _DtCvIsSegNoop(nextSeg)))
		*ret_tab_flag = False;

	     nextSeg = _DtCvNextSeg(nextSeg);
	  }

	if (True == *ret_tab_flag && True == only1Col)
	    *ret_tab_flag = False;
      }

    /*
     * Calculate the height and return it.
     */
    *ret_height = layout->info.y_pos - saveYpos;

    /*
     * restore the right margin
     */
    layout->right          = saveRight;

    if (layout->info.cur_max_x < saveMaxX)
        layout->info.cur_max_x = saveMaxX;
}

/******************************************************************************
 * Function: AdjustFrmtTxtOption
 *
 *****************************************************************************/
static void
AdjustFrmtTxtOption(
    _DtCvSegmentI	*p_seg,
    _DtCvFrmtOption	 option)
{
    if (p_seg != NULL && _DtCvIsSegContainer(p_seg))
      {
	TxtHorizJustify(p_seg) = option;

	p_seg = _DtCvContainerListOfSeg(p_seg);
	while (p_seg != NULL)
	  {
	    AdjustFrmtTxtOption(p_seg, option);
	    p_seg = p_seg->next_seg;
	  }
      }
}

/******************************************************************************
 * Function: ResolveCell
 *
 *****************************************************************************/
static _DtCvValue
ResolveCell(
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvSegmentI	*table,
    ColumnSpec		*col_specs,
    RowSpec		*row_specs,
    int			 col,
    int			 row,
    int			 max_cols,
    int			 max_rows,
    CellInfo		*ret_info)
{
    int i;
    char  *id = NULL;
    char  *idRefs;
    char  *ptr = NULL;
    char   c = 0;
    int    count;
    int    len;
    int    done;
    int    brdCnt;
    int    myCol = col;
    int	   cell  = row * max_cols + col;
    _DtCvUnit   cellWidth;
    _DtCvUnit   retWidth;
    _DtCvUnit   retHeight;
    _DtCvUnit   saveTop = 0;
    CellInfo	*thisCell = &ret_info[cell];
    DataPoint	 basePt;
    _DtCvValue	 reformat = False;
    _DtCvValue	 retTabFlag = False;
    _DtCvSegmentI **f_data = _DtCvCellsOfTableSeg(table);

    /*
     * if this column is spanned, skip
     */
    if (thisCell->col_spn == -1 || thisCell->row_spn == -1)
	return False;

    GetCurrentDataPoint(layout, &basePt);

    if (thisCell->cell_seg == NULL)
      {
	idRefs = row_specs[row].next_id;

        /*
         * find the end of the id
         */
        done      = False;

        /*
         * set the starting info
         */
        thisCell->col_spn = 1;
        thisCell->pos_x   = basePt.left + layout->left;

        while (!done && col < max_cols)
	  {
            ptr       = idRefs;
            id        = idRefs;

            /*
             * move the ptr to the next id,
	     * counting the characters in this id at the same time.
             */
	    len = 0;
            while (NULL != ptr && *ptr != ' ' && *ptr != '\0')
	      {
                ptr++;
		len++;
	      }

	    /*
	     * set idRefs to the next id.
	     */
            idRefs = ptr;
            while (NULL != idRefs && *idRefs == ' ')
                idRefs++;

            /*
             * Is this id and the next the same? If so,
             * it spans the columns
             */
            if (0 != len && _DtCvStrNCaseCmpLatin1(id, idRefs, len) == 0 &&
				(idRefs[len] == ' ' || idRefs[len] == '\0'))
              {
                col++;
                thisCell->col_spn++;
		ret_info[++cell].col_spn = -1;
              }
            else
                done = True;
          }

	row_specs[row].next_id = idRefs;

	/*
	 * find the segment
	 */
	if (NULL != id && '\0' != *id)
	  {
	    c       = *ptr;
            *ptr    = '\0';
	    while (f_data != NULL && NULL != *f_data &&
	        _DtCvStrCaseCmpLatin1(_DtCvContainerIdOfSeg(*f_data),id) != 0)
	        f_data++;
	    /*
	     * make sure to break the link to the next segment.
	     * Otherwise, the formatting routines will format too much
	     * for the cell.
	     */
	    if (NULL != f_data && NULL != *f_data)
	      {
	        _DtCvNextSeg(*f_data) = NULL;

		/*
		 * assign the data to this cell.
		 */
		thisCell->cell_seg = *f_data;
	      }
	    else /* there is no id for this cell, use a blank container */
	        thisCell->cell_seg = (struct _dtCvSegment*) &BlankTableCell;
	  }
	else /* there is no id for this cell, use a blank container */
	    thisCell->cell_seg = (struct _dtCvSegment*) &BlankTableCell;

	/*
	 * how many rows does this cell span?
	 */
	len = 0;
	if (NULL != id)
	    len = strlen(id);

	for (done = False, count = 1, i = row + 1;
				0 < len && i < max_rows && False == done; i++)
	  {
	    done = True;
	    if (_DtCvStrNCaseCmpLatin1(id, row_specs[i].next_id, len) == 0 &&
		(row_specs[i].next_id[len] == ' ' ||
					row_specs[i].next_id[len] == '\0'))
	      {
		int    k;

		done = False;
		count++;

		/*
		 * invalidate the columns spanned in this row
		 */
		for (k = 0; k < thisCell->col_spn && k + myCol < max_cols; k++)
		  {
		    ret_info[i * max_cols + myCol + k].col_spn = -1;
		    ret_info[i * max_cols + myCol + k].row_spn = -1;
		  }
		
		idRefs  = row_specs[i].next_id;
		do
		  {
		    /*
		     * skip the current id.
		     */
		    while (*idRefs != ' ' && *idRefs != '\0')
			idRefs++;

		    /*
		     * skip the space to the next id.
		     */
		    while (*idRefs == ' ')
			idRefs++;

		    /*
		     * now test to see if this is a match
		     * cycle if so. quit if the end of string
		     * or not a match.
		     */
		  } while (*idRefs != '\0' &&
			_DtCvStrNCaseCmpLatin1(id, idRefs, len) == 0 &&
				(idRefs[len] == ' ' || idRefs[len] == '\0'));

		/*
		 * the next non-spanned column in this row will use this id.
		 */
		row_specs[i].next_id = idRefs;
	      }
	  }

	if (NULL != id && '\0' != *id)
	    *ptr = c;

	thisCell->row_spn = count;
      }

    for (i = 0, cellWidth = 0; i < thisCell->col_spn; i++)
	cellWidth += col_specs[myCol + i].actual;

    /*
     * set the start line and text information
     */
    SetBeginCounts(canvas, &(thisCell->info.cnt));

    /*
     * check to see if this cell is overhung by a previous cell.
     * If so, zero the top margin. But remember and restore it
     * because resizing may eliminate the need for the overhang!
     */
    if (&BlankTableCell != thisCell->cell_seg)
      {
	saveTop = _DtCvContainerTMarginOfSeg(thisCell->cell_seg);
	if (_DtCvTRUE == col_specs[myCol].hanger
		&& 0 != row_specs[row].y_adj && myCol > row_specs[row].column)
	    _DtCvContainerTMarginOfSeg(thisCell->cell_seg) = 0;
      }

    /*
     * Format the cell
     */
    FormatCell(canvas, layout, thisCell->cell_seg, cellWidth,
			row_specs[row].height, basePt,
				&brdCnt, &retWidth, &retHeight, &retTabFlag);

    if (&BlankTableCell != thisCell->cell_seg)
	_DtCvContainerTMarginOfSeg(thisCell->cell_seg) = saveTop;

    /*
     * set some ending information
     */
    thisCell->info.height = retHeight;
    thisCell->info.width  = retWidth;
    SetEndCounts(canvas, &(thisCell->info.cnt), brdCnt);

    /*
     * check the height against previous heights
     */
    if (row_specs[row].height < retHeight && thisCell->row_spn == 1)
	row_specs[row].height = retHeight;

    /*
     * check for run over of the desired widths
     */
    if (retWidth > cellWidth)
      {
	int j;
	_DtCvUnit  cellMax;
	_DtCvUnit  maxSlop;
	_DtCvUnit  value;
	_DtCvUnit  percent;
	_DtCvUnit  slopUsed = 0;
	_DtCvUnit  slop   = retWidth - cellWidth; /* the amount of room
						   required of the neighbors */

	/*
	 * set the reformat flag
	 */
	if (False == retTabFlag)
	    reformat = True;

	/*
	 * determine the maximum size the column can occupy
	 */
	for (j = 0, cellMax = 0;
			j < thisCell->col_spn && j + myCol < max_cols; j++)
	    cellMax += col_specs[myCol+j].max;
	
	/*
	 * determine the maximum available space from the neighbors.
	 */
	for (j = myCol + thisCell->col_spn, maxSlop = 0; j < max_cols; j++)
	    maxSlop = maxSlop + col_specs[j].actual - col_specs[j].min;

	/*
	 * If the slop demanded is larger than available,
	 * simply reduced the other column specifications to their smallest
	 * values.
	 */
	if (slop >= maxSlop)
	  {
	    for (j = myCol + thisCell->col_spn; j < max_cols; j++)
	        col_specs[j].actual = col_specs[j].min;

	    /*
	     * Is it allowed for this column to 'hang over' the others?
	     *
	     * And is it the first one? I.e. don't allow more than one
	     * cell per row to overhang it's neighbors.
	     *
	     * And only allow it if the vertical text justification places
	     * the text at the top of the cell.
	     */
	    if (_DtCvTRUE == col_specs[myCol].hanger
		&& 0 == row_specs[row].y_adj
		&& _DtCvJUSTIFY_TOP == TxtVertJustify(thisCell->cell_seg))
	      {
		/*
		 * now set the other columns to start a little lower
		 * in their objects. Remove the bottom margin from the
		 * adjustment. One would hope that each of the containers
		 * for the columns in the row have the same bottom margin
		 * so that the overhung cell will push the next row down
		 * by the appropriate amount.
		 */
		row_specs[row].y_adj  = retHeight -
				_DtCvContainerBMarginOfSeg(thisCell->cell_seg);
		row_specs[row].column = myCol;
		retWidth = cellMax;

		/*
		 * clear the reformat flag
		 */
		reformat = False;
	      }
	  }
	else if (slop > 0)
	  {
	    /*
	     * the maximum slop available from the neighbors is
	     * enough. Now take space from my neighbors based
	     * on their orginal size. I.e. the larger they
	     * are, the more I take from them.
	     */
	    for (j = myCol + thisCell->col_spn; j < max_cols; j++)
	      {
	        percent   = col_specs[j].actual - col_specs[j].min;
	        value     = slop * percent / maxSlop;
	        slopUsed += value;
	        col_specs[j].actual -= value;
	      }

	    /*
	     * if any more slop is needed, grab it on a strictly
	     * straight basis.
	     */
	    do {
	        slop -= slopUsed;
	        for (j = myCol + thisCell->col_spn, slopUsed = 0;
					slop > slopUsed && j < max_cols; j++)
	          {
	            if (col_specs[j].actual > col_specs[j].min)
	              {
	                col_specs[j].actual--;
	                slopUsed++;
	              }
	          }
	      } while (slopUsed > 0 && slop > slopUsed);
	  }

	/*
	 * set the column width in the controlling column struct.
	 */
	if (thisCell->col_spn == 1)
	  {
	    col_specs[myCol].actual = retWidth;
	  }
	else
	  {
	    /*
	     * how much to spread among the columns?
	     */
	    slop = cellMax - retWidth;

	    /*
	     * if the aggragate max width is smaller than required,
	     * allocate the excess among the columns.
	     */
	    if (slop < 0)
	      {
		/*
		 * set the desired to the max and calculate the leftover
		 * slop and the maximum desired size.
		 */
		for (j = 0, slop = retWidth, maxSlop = 0;
			myCol + j < max_cols && j < thisCell->col_spn; j++)
		  {
		    col_specs[j].actual = col_specs[myCol + j].max;
		    slop    -= col_specs[myCol + j].max;
		    maxSlop += col_specs[myCol + j].max;
		  }
		/*
		 * now allocate the leftover slop to each colum
		 * based on the maximum desired size.
		 */
		for (j = 0, slopUsed = 0;
			slop > slopUsed && myCol + j < max_cols
						&& j < thisCell->col_spn; j++)
		  {
		    value = slop * col_specs[myCol + j].max / maxSlop;
		    if (((slop*col_specs[myCol+j].max) % maxSlop) >= (maxSlop/2))
			value++;
		    col_specs[myCol + j].actual += value;
		    slopUsed += value;
		  }
	      }
	    else if (slop > 0)
	      {
	        slopUsed = 0;
	        for (j = myCol;
			j < max_cols && j < myCol + thisCell->col_spn; j++)
	          {
	            percent   = col_specs[j].max - col_specs[j].actual;
	            value     = slop * percent / maxSlop;
	            slopUsed += value;
	            col_specs[j].actual += value;
	          }

	        do {
	            slop -= slopUsed;
	            for (j = myCol, slopUsed = 0; slop > slopUsed &&
			j < max_cols && j < myCol + thisCell->col_spn; j++)
	              {
	                if (col_specs[j].actual < col_specs[j].max)
	                  {
	                    col_specs[j].actual++;
	                    slopUsed++;
	                  }
	              }
	          } while (slopUsed > 0 && slop > slopUsed);
	      }
	    else /* if (slop == 0) */
	      {
		for (j = 0; myCol + j < max_cols && j < thisCell->col_spn; j++)
		    col_specs[j].actual = col_specs[myCol + j].max;
	      }
	  }
      }

    return reformat;
}

/******************************************************************************
 * Function: ProcessTable
 *
 *****************************************************************************/
static void
ProcessTable(
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvSegmentI	*table,
    _DtCvUnit		 min_y)
{
    int		   a;
    int		   b;
    int		   c;
    int		   col;
    int		   row;
    int		   cell;
    int		   divisor;
    int		   maxCols;
    int		   maxRows;
    int		   maxRowSpn;
    int		   saveLnStart  = canvas->line_cnt;
    int		   saveTxtStart = canvas->txt_cnt;
    int		   saveTravCnt  = canvas->trav_cnt;
    _DtCvUnit	   workWidth;
    _DtCvUnit	   newLeft;
    _DtCvUnit	   saveLeft    = layout->left;
    _DtCvUnit	   saveYpos    = layout->info.y_pos;
    _DtCvUnit	   tableYpos;
    _DtCvUnit	   newHeight;
    _DtCvUnit	   newWidth;
    _DtCvUnit	   oldAlignPos;
    short	   anchorRow = -1;

    const char	 **widthStr;
    const char	  *saveAlignChar = layout->info.align_char;
    char	 **rowIds;
    char	  *alignCharacters = NULL;
    char	   alignBuf[16];

    _DtCvFrmtOption   saveTxtJustify = layout->txt_justify;
    _DtCvFrmtOption   colJustify  = _DtCvJUSTIFY_LEFT;
    _DtCvFrmtOption  *colJustSpec;

    _DtCvValue	   oldFound  = layout->id_found;
    _DtCvValue	   haveBrds  = False;
    _DtCvValue	   saveState = layout->table_flag;
    _DtCvValue	   saveAlignFlag = layout->info.align_flag;
    _DtCvValue	   saveAlignPos  = layout->info.align_pos;
    _DtCvValue	   redo;

    CellInfo	 defCell;
    CellInfo	*cellInfo = &defCell;
    ColumnSpec	 defColumn;
    ColumnSpec	*colSpecs = &defColumn;
    RowSpec	 defRow;
    RowSpec	*rowSpecs = &defRow;
    DataPoint	 basePt;
    GrpInfo	 grpInfo = DefGrpInfo;

    /*
     * get the base margins that the table will be working in.
     */
    GetCurrentDataPoint(layout, &basePt);

    /*
     * find out how many rows there are.
     */
    for (rowIds = _DtCvCellIdsOfTableSeg(table), maxRows = 0;
			rowIds != NULL && rowIds[maxRows] != NULL;  maxRows++);

    if (maxRows == 0)
	return;

    /*
     * get the number of columns and the column widths
     */
    maxCols     = _DtCvNumColsOfTableSeg(table);
    widthStr    = (const char **)_DtCvColWOfTableSeg(table);
    colJustSpec = _DtCvColJustifyOfTableSeg(table);

    if (maxCols < 1)
	maxCols = 1;

    if (widthStr == NULL)
	widthStr = DefWidth;

    /*
     * determine the width the table has to work with.
     */
    workWidth = layout->max_width - basePt.left - basePt.right -
						layout->left - layout->right;
    if (workWidth < 0)
	workWidth = 0;

    /*
     * turn the string specifying column widths into units.
     */
    if (maxCols != 1)
      {
        colSpecs = (ColumnSpec *) malloc (sizeof(ColumnSpec) * maxCols);
	if (colSpecs == NULL)
	    return;
      }
    if (maxRows != 1)
      {
        rowSpecs = (RowSpec *) malloc (sizeof(RowSpec) * maxRows);
	if (rowSpecs == NULL)
	  {
	    if (maxCols > 1)
	        free(colSpecs);
	    return;
	  }
      }
    if (maxRows != 1 || maxCols != 1)
      {
        cellInfo = (CellInfo *) malloc (sizeof(CellInfo) * maxCols * maxRows);
	if (cellInfo == NULL)
	  {
	    if (maxCols > 1)
	        free(colSpecs);
	    if (maxRows > 1)
	        free(rowSpecs);
	    return;
	  }
      }

    /*
     * for each column, process the width specification.
     *    '+Optimal,Take,Give'
     *
     *     +       - means the cell can 'hang over' its neighbors.
     *               It will take everything it can get and then
     *               push the other below it. (Labeled lists).
     *     Optimal - The desired percentage of the available space to
     *               use for the column.
     *     Take    - The percentage amount the column will take from
     *               other columns to make itself 'fit'.
     *     Give    - The percentage amount the column is willing to give up
     *               to other columns for them to 'fit'.
     */
    for (col = 0, divisor = 0; col < maxCols; col++)
      {
	const char  *nxtWidth = *widthStr;

	/*
	 * move to the meat of the width specification string.
	 */
	SkipToNumber(&nxtWidth);

	/*
	 * set the correct 'allow hangers' flag.
	 */
	colSpecs[col].hanger  = _DtCvFALSE;
	if ('+' == *nxtWidth)
	  {
	    colSpecs[col].hanger = _DtCvTRUE;
	    nxtWidth++;
	  }

	/*
	 * now process the O,G,T specification.
	 */
	a = GetValueFromString(&nxtWidth, 1); if (a < 1) a = 1;
	b = GetValueFromString(&nxtWidth, 0); if (b < 0) b = 0;
	c = GetValueFromString(&nxtWidth, b); if (c > a) c = a;

	/*
	 * for now just get the base percentages.
	 */
	colSpecs[col].min     = a - c;
	colSpecs[col].actual  = a;
	colSpecs[col].max     = a + b;

	/*
	 * get the column justification.
	 */
	if (NULL != colJustSpec)
	  {
	    colJustify = *colJustSpec;
	    colJustSpec++;
	  }
	colSpecs[col].justify = colJustify;
	if (_DtCvINHERIT == colJustify)
	    colSpecs[col].justify = saveTxtJustify;

	/*
	 * up the divisor value.
	 */
	divisor += colSpecs[col].actual;

	/*
	 * skip to the next set of width specifications
	 */
	if (col + 1 < maxCols && NULL != widthStr[1])
	    widthStr++;

	/*
	 * initialize the cell information for the rows in this column
	 */
	for (row = 0; row < maxRows; row++)
	  {
	    cellInfo[row * maxCols + col].cell_seg = NULL;
	    cellInfo[row * maxCols + col].col_spn  = 0;
	    cellInfo[row * maxCols + col].row_spn  = 0;
	    cellInfo[row * maxCols + col].info     = DefLayFrmtInfo;
	  }
      }

    /*
     * initialize the row specs
     */
    newHeight = 0;
    if (-1 != min_y && 1 == maxRows)
        newHeight = min_y - saveYpos;
    for (row = 0; row < maxRows; row++)
      {
	rowSpecs[row].column  = -1;
	rowSpecs[row].y_adj   = 0;
	rowSpecs[row].height  = newHeight;
	rowSpecs[row].height  = newHeight;
	rowSpecs[row].next_id = rowIds[row];
      }

    /*
     * now figure the real values
     */
    if (divisor < 1)
	divisor = 1;
    for (col = 0; col < maxCols; col++)
      {
	colSpecs[col].min    = workWidth * colSpecs[col].min    / divisor;
	colSpecs[col].actual = workWidth * colSpecs[col].actual / divisor;
	colSpecs[col].max    = workWidth * colSpecs[col].max    / divisor;

	if (colSpecs[col].min < 1)
	    colSpecs[col].min = 1;
	if (colSpecs[col].actual < 1)
	    colSpecs[col].actual = 1;
	if (colSpecs[col].max < 1)
	    colSpecs[col].max = 1;
      }

    /*
     * now process the table.
     */
    tableYpos = layout->info.y_pos;
    maxRowSpn = 1;
    alignCharacters = _DtCvJustifyCharsOfTableSeg(table);

    /*
     * set up the state of table processing and
     * the beginning line/text counts.
     */
    layout->table_flag = True;
    SetBeginCounts(canvas, &(grpInfo.cnt));

    /*
     * now process each column, row by row.
     * Doing it row by row allows the columns to shake out their
     * sizing with less reformatting.
     */
    for (col = 0; col < maxCols; col++)
      {
	/*
	 * remember where this column starts.
	 */
	saveTravCnt  = canvas->trav_cnt;
        saveLnStart  = canvas->line_cnt;
        saveTxtStart = canvas->txt_cnt;
        newLeft      = layout->left;
	colJustify   = layout->txt_justify;
	layout->txt_justify = colSpecs[col].justify;

	/*
	 * initialize the JUSTIFY_NUM or JUSTIFY_CHAR information
	 */
	layout->info.align_pos  = 0;
	layout->info.align_char = PeriodStr;
	if (_DtCvJUSTIFY_CHAR == layout->txt_justify)
	  {
	    /*
	     * are any alignment characters specified?
	     */
	    if (NULL != alignCharacters && '\0' != *alignCharacters)
	      {
		int len = mblen(alignCharacters, MB_CUR_MAX);

		/*
		 * copy the character into a buffer
		 */
		strncpy(alignBuf, alignCharacters, len);
		alignBuf[len] = '\0';
		layout->info.align_char = alignBuf;

		/*
		 * are there more characters? If so increment for the
		 * next column that may have JUSTIFY_CHAR. Otherwise,
		 * leave alone and re-use for other columns.
		 */
		if ('\0' != alignCharacters[len])
		    alignCharacters += len;
	      }

	    /* no...then default */
	    else
		layout->txt_justify = _DtCvJUSTIFY_LEFT;
	  }

        do {
	    /*
	     * reset the counts to the start
	     */
	    canvas->trav_cnt = saveTravCnt;
	    canvas->line_cnt = saveLnStart;
	    canvas->txt_cnt  = saveTxtStart;
	    layout->left     = newLeft;

	    /*
	     * for each row, format the cell in this columns
	     */
	    for (row = 0, redo = False, cell = col;
				row < maxRows && redo == False;
						row++, cell += maxCols)
	      {
		/*
		 * remember the old height
		 */
		rowSpecs[row].lst_height = rowSpecs[row].height;

		/*
		 * set the alignment flag for each column in the row.
		 */
		layout->info.align_flag = False;
		if (_DtCvJUSTIFY_CHAR == layout->txt_justify ||
					_DtCvJUSTIFY_NUM == layout->txt_justify)
		    layout->info.align_flag = True;

		/*
		 * remember the alignment position
		 */
		oldAlignPos = layout->info.align_pos;

		/*
		 * layout all the cells jammed to the top of the table.
		 * later, they get moved down to their position.
		 */
	        layout->info.y_pos = tableYpos;
	        redo = ResolveCell(canvas, layout, table,
				    colSpecs, rowSpecs, col, row,
				    maxCols, maxRows, cellInfo);
		/*
		 * check for maximum row span
		 */
		if (maxRowSpn < cellInfo[cell].row_spn)
		    maxRowSpn = cellInfo[cell].row_spn;

		/*
		 * did the cell have borders?
		 */
		if (0 != cellInfo[cell].info.cnt.my_lines)
		    haveBrds = True;

	        /*
	         * check to see if the specified anchor has been found in
	         * this row. If so, save some information for later use.
	         */
	        if (anchorRow == -1 && oldFound != layout->id_found)
		    anchorRow = row;

		/*
		 * check to see if the alignment position has changed.
		 * for all but the first row!
		 */
		if (0 != row && oldAlignPos != layout->info.align_pos)
		    redo = True;
	      }

	    if (True == redo)
	      {
		for (a = 0; a < row; a++)
		  {
		    /*
		     * restore the old row heights for this column
		     */
		    rowSpecs[a].height = rowSpecs[a].lst_height;

		    /*
		     * reset the hanging cell information.
		     */
		    if (col == rowSpecs[a].column)
		      {
		        rowSpecs[a].column = -1;
		        rowSpecs[a].y_adj  = 0;
		      }
		  }
	      }

          } while (redo == True);
	
	/*
	 * push the next column to the right by size of this column
	 */
	layout->left += colSpecs[col].actual;

	/*
	 * restore the horizontal text justification
	 */
	layout->txt_justify = colJustify;
      }

    /*
     * set the ending counts for the lines/text in the table.
     * and save the information as long as we are not a table
     * that is a descendant of some container with a border and
     * our cells include borders and we're going to have to honor
     * boundaries.
     */
    SetEndCounts(canvas, &(grpInfo.cnt), 0);
    if (_DtCvUSE_BOUNDARY_MOVE == canvas->constraint &&
				False == layout->brdr_flag && True == haveBrds)
      {
	GrpInfo *info = (GrpInfo *) malloc (sizeof(GrpInfo));

	/*
	 * warning - nothing done if malloc error.
	 */
	if (NULL != info)
	  {
	    /*
	     * initialize to the line counts for the table
	     */
	    *info = grpInfo;

	    /*
	     * set the linked list information
	     */
	    info->next_info = layout->grp_lst;
	    layout->grp_lst = info;
	  }
      }

    /*
     * Now go back and search for zeroed row heights and fill them in
     * based on spanned rows. This can only happen if maxRowSpn is
     * greater than 1! Otherwise, a row height really did end up zero!
     */
    if (maxRowSpn > 1)
      {
	/*
	 * try to resolve the zero height rows
	 */
	for (a = 1, redo = True; redo && a < maxRowSpn; a++)
	  {
	    redo = False;
	    for (row = 0; row < maxRows; row++)
	      {
	        if (rowSpecs[row].height == 0 &&
		    ResolveHeight(rowSpecs, cellInfo, maxCols, row, a) == 0)
			redo = True;
	      }
	  }
	
	/*
	 * if any of the rows comes up unresolved, force to an average
	 * line height.
         *
         * But only do this if the first cell *does not* span all the rows
         * and columns.
	 */
	if (redo &&
		cellInfo[0].row_spn != maxRows && cellInfo[0].col_spn != maxCols)
	  {
	    for (row = 0; row < maxRows; row++)
	      {
	        if (rowSpecs[row].height == 0)
		    rowSpecs[row].height = canvas->metrics.line_height;
	      }
	  }

        /*
         * Now, double check that the row heights will accommodate
	 * all the cells.
         */
        for (row = 0; row < maxRows; row++)
	    for (col = 0; col < maxCols; col++)
	        AdjustHeight(cellInfo, rowSpecs, maxCols, row, col);
      }

    /*
     * now check that the minimum heights used for the rows matches
     * or exceeds the minimum y position required.
     */
    if (-1 != min_y)
      {
	_DtCvUnit  pad;

	for (newHeight = 0, row = 0; row < maxRows; row++)
	    newHeight += rowSpecs[row].height;

	if (tableYpos + newHeight < min_y)
	  {
	    newHeight = tableYpos - min_y;
	    for (row = 0; 0 < newHeight && row < maxRows; row++)
	      {
		pad                   = (newHeight/(maxRows-row));
		rowSpecs[row].height += pad;
		newHeight            -= pad;
	      }
	  }
      }

    /*
     * now reposition the cells based on the final row heights.
     */
    layout->info.y_pos = tableYpos;
    for (tableYpos = 0, cell = 0, row = 0; row < maxRows;
	tableYpos = tableYpos + rowSpecs[row].height + rowSpecs[row].y_adj,
	row++)
      {
	/*
	 * check to see if the specified anchor has been found in this
	 * row. If so, adjust the found position.
	 */
	if (anchorRow == row)
	    layout->id_Ypos += tableYpos;

	for (col = 0, layout->left = saveLeft; col < maxCols;
				layout->left += colSpecs[col++].actual, cell++)
	  {
	    if (cellInfo[cell].cell_seg != NULL)
	      {
		/*
		 * calculate the new height
		 */
		for (newHeight = 0, a = 0; a < cellInfo[cell].row_spn; a++)
		  {
		    newHeight += rowSpecs[row + a].height;
		    if (col != rowSpecs[row + a].column)
			newHeight += rowSpecs[row + a].y_adj;
		  }

		/*
		 * calculate the new width.
		 */
		for (newWidth = 0, a = 0; a < cellInfo[cell].col_spn; a++)
		    newWidth += colSpecs[col + a].actual;

		/*
		 * now get the overhang space for this cell
		 */
		workWidth = 0;
		if (col > rowSpecs[row].column)
		    workWidth = rowSpecs[row].y_adj;

		/*
		 * if the heights (and/or width for spanning columns)
		 * are different, check to see if the
		 * cell contains lines that may be affected by the
		 * height adjustment.
		 *
		 * It is strongly assumed that if a table specifies that
		 * a cell can hang over it's neighbors that it will *NOT*
		 * have borders (whereby LinesMayChange is false) and
		 * newWidth will not be greater than the cell's width.
		 */
		if ((newHeight > cellInfo[cell].info.height &&
			True == LinesMayChange(canvas,
					cellInfo[cell].info.cnt.beg_ln,
					cellInfo[cell].info.cnt.end_ln,
					cellInfo[cell].info.cnt.my_lines))
				||
			(1 < cellInfo[cell].col_spn &&
				newWidth > cellInfo[cell].info.width))
		    ReFormatCell(canvas, layout, &cellInfo[cell], colSpecs,
					col, newHeight,
					layout->info.y_pos + tableYpos);

		else
		    /* adjust the cell rather than reformatting */
	            AdjustObjectPosition(canvas, layout,
				TxtVertJustify(cellInfo[cell].cell_seg),
				cellInfo[cell].info.cnt.beg_txt ,
				cellInfo[cell].info.cnt.beg_ln  ,
				cellInfo[cell].info.cnt.beg_brk ,
				cellInfo[cell].info.cnt.end_txt ,
				cellInfo[cell].info.cnt.end_ln  ,
				cellInfo[cell].info.cnt.end_brk ,
				cellInfo[cell].info.cnt.my_lines,
				newHeight - cellInfo[cell].info.height,
				0,
				tableYpos, workWidth);
	      }
	  }
      }

    /*
     * increment the maximum y.
     */
    layout->info.y_pos += tableYpos;
    layout->left        = saveLeft;

    if (maxCols > 1)
        free(colSpecs);
    if (maxRows > 1)
        free(rowSpecs);
    if (maxRows > 1 || maxCols > 1)
        free(cellInfo);

    layout->txt_justify = saveTxtJustify;
    layout->table_flag  = saveState;

    /*
     * restore the alignment information
     */
    layout->info.align_flag = saveAlignFlag;
    layout->info.align_char = saveAlignChar;
    layout->info.align_pos  = saveAlignPos;
}

/******************************************************************************
 * Function: UpdateDimensionArrays
 *
 * Purpose: Based on the object's orientation and justification,
 *	    update the correct dimension array(s).
 *
 *****************************************************************************/
static void
UpdateDimensionArrays(
    _DtCvSegmentI *p_seg,
    _DtCvUnit	 width,
    _DtCvUnit	 height,
    TopDims	*top_bot,
    SideDims	*side,
    CornerDims	*corner,
    FlowDims	*flow,
    _DtCvUnit	*max_left,
    _DtCvUnit	*max_right)
{
    int		    i;
    int		    j;
    int		   *marginPtr;
    _DtCvFrmtOption orient   = ObjHorizOrient(p_seg);
    _DtCvFrmtOption vOrient  = ObjVertOrient(p_seg);

    /*
     * modify the width that headSize should be
     */
    j = DIMS_LM;
    i = DIMS_TOP;
    switch (orient)
      {
	case _DtCvJUSTIFY_RIGHT_MARGIN:
		j++;
	case _DtCvJUSTIFY_CENTER:
		j++;
	case _DtCvJUSTIFY_LEFT_MARGIN:
		if (vOrient == _DtCvJUSTIFY_BOTTOM)
		    i = DIMS_BOTTOM;

		if ((*top_bot)[i][j][DIMS_WIDTH] < width)
		    (*top_bot)[i][j][DIMS_WIDTH] = width;

		if (_DtCvWRAP_JOIN != _DtCvContainerFlowOfSeg(p_seg))
		    (*top_bot)[i][j][DIMS_HEIGHT] += height;
		break;

      }

    /*
     * check left & right margins.
     */
    marginPtr = max_left;
    j = DIMS_LEFT;
    i = DIMS_TOP;
    switch(orient)
      {

	case _DtCvJUSTIFY_RIGHT_CORNER:
	case _DtCvJUSTIFY_RIGHT:
		j = DIMS_RIGHT;
		marginPtr = max_right;

	case _DtCvJUSTIFY_LEFT_CORNER:
	case _DtCvJUSTIFY_LEFT:
		if (vOrient != _DtCvJUSTIFY_TOP)
		    i++;
		if (vOrient == _DtCvJUSTIFY_BOTTOM)
		    i++;

		/*
		 * push i to zero or 4
		 */
		if (orient == _DtCvJUSTIFY_RIGHT_CORNER ||
					    orient == _DtCvJUSTIFY_LEFT_CORNER)
		  {
		    if (i) i = DIMS_BC;
		    (*corner)[i][j] += height;
		    if (*marginPtr < width)
		        *marginPtr = width;
		  }
		else if (_DtCvContainerFlowOfSeg(p_seg) != _DtCvWRAP)
		  {
		    (*side)[i][j] += height;
		    if (*marginPtr < width)
		        *marginPtr = width;
		  }
		else
		  {
		    (*flow)[j][DIMS_HEIGHT] += height;
		    if ((*flow)[j][DIMS_WIDTH] < width)
			(*flow)[j][DIMS_WIDTH] = width;
		  }
		break;
      }
}

/******************************************************************************
 * Function: DetermineMaxDims
 *
 *****************************************************************************/
static void
DetermineMaxDims(
    TopDims	*top_bot,
    CornerDims	*corner,
    _DtCvUnit	 left_margin,
    _DtCvUnit	 right_margin,
    _DtCvUnit	*top_height,
    _DtCvUnit	*bot_height,
    _DtCvUnit	*max_width)
{
    int	j;
    _DtCvUnit     topWidth;
    _DtCvUnit     botWidth;

    /*
     * now process all the information gathered about the (sub)headings
     * to determine the bounding box for the (head) txt. Start by figuring
     * out the maximums for the dimensions.
     *
     * figure the current top and bottom max widths.
     */
    topWidth  = left_margin + right_margin;
    botWidth  = left_margin + right_margin;
    *top_height = 0;
    *bot_height = 0;
    for (j = DIMS_LM; j <= DIMS_RM; j++)
      {
	topWidth = topWidth + (*top_bot)[DIMS_TOP]   [j][DIMS_WIDTH];
	botWidth = botWidth + (*top_bot)[DIMS_BOTTOM][j][DIMS_WIDTH];

	if (*top_height < (*top_bot)[DIMS_TOP][j][DIMS_HEIGHT])
	    *top_height = (*top_bot)[DIMS_TOP][j][DIMS_HEIGHT];

	if (*bot_height < (*top_bot)[DIMS_BOTTOM][j][DIMS_HEIGHT])
	    *bot_height = (*top_bot)[DIMS_BOTTOM][j][DIMS_HEIGHT];
      }

    /*
     * for the maximum top and bottom heights, take into
     * consideration the corner values
     */
    if (*top_height < (*corner)[DIMS_TC][DIMS_LEFT])
	*top_height = (*corner)[DIMS_TC][DIMS_LEFT];
    if (*top_height < (*corner)[DIMS_TC][DIMS_RIGHT])
	*top_height = (*corner)[DIMS_TC][DIMS_RIGHT];

    if (*bot_height < (*corner)[DIMS_BC][DIMS_LEFT])
	*bot_height = (*corner)[DIMS_BC][DIMS_LEFT];
    if (*bot_height < (*corner)[DIMS_BC][DIMS_RIGHT])
	*bot_height = (*corner)[DIMS_BC][DIMS_RIGHT];

    *max_width = topWidth;
    if (*max_width < botWidth)
	*max_width = botWidth;

}

/******************************************************************************
 * Function: DetermineFlowConstraints
 *
 *****************************************************************************/
static void
DetermineFlowConstraints(
    LayoutInfo	*layout,
    FlowDims	 flow_dims,
    _DtCvUnit	 left_margin,
    _DtCvUnit	 right_margin,
    _DtCvUnit	 start_y,
    DataPoint	*left_pt,
    DataPoint	*right_pt)
{
    _DtCvUnit	leftSide  = flow_dims[DIMS_LEFT][DIMS_HEIGHT];
    _DtCvUnit	rightSide = flow_dims[DIMS_RIGHT][DIMS_HEIGHT];

    /*
     * Now, if there is flowing text required, put points on the
     * stack to indicate them.
     */
    left_margin  += flow_dims[DIMS_LEFT][DIMS_WIDTH];
    right_margin += flow_dims[DIMS_RIGHT][DIMS_WIDTH];

    GetCurrentDataPoint(layout, left_pt);
    GetCurrentDataPoint(layout, right_pt);
    left_pt->left   += left_margin;
    left_pt->right  += right_margin;
    left_pt->y_pos   = _CEFORMAT_ALL;
    right_pt->left  += left_margin;
    right_pt->right += right_margin;
    right_pt->y_pos  = _CEFORMAT_ALL;

    while (leftSide > 0 || rightSide > 0)
      {
	if (leftSide > 0)
	  {
	    if (rightSide == 0 || leftSide <= rightSide)
	      {
		left_pt->right = right_margin;
		left_pt->y_pos = start_y + leftSide;
		if (leftSide != rightSide)
		    left_margin = 0;
		leftSide = 0;
		InsertDataPoint(layout, left_pt);
	      }
	  }

	if (rightSide > 0)
	  {
	    if (leftSide == 0 || leftSide > rightSide)
	      {
		right_pt->left  = left_margin;
		right_pt->y_pos = start_y + rightSide;
		if (leftSide != rightSide)
		    right_margin = 0;
		rightSide = 0;
		InsertDataPoint(layout, right_pt);
	      }
	  }
      }
}

/******************************************************************************
 * Function: DetermineHeadPositioning
 *
 *****************************************************************************/
static void
DetermineHeadPositioning(
    TopDims	*top_bot,
    SideDims	*side,
    CornerDims	*corner,
    FlowDims	*flow,
    _DtCvUnit	 start_y,
    _DtCvUnit	 max_top,
    _DtCvUnit	 block_size,
    _DtCvUnit	*ret_side_size)
{
    int    i;
    _DtCvUnit   leftSideHeight  = 0;
    _DtCvUnit   rightSideHeight = 0;
    _DtCvUnit   sideHeight      = 0;

    /*
     * determine the maximum side heights
     */
    for (i = DIMS_TOP; i <= DIMS_BOTTOM; i++)
      {
	leftSideHeight  += (*side)[i][DIMS_LEFT];
	rightSideHeight += (*side)[i][DIMS_RIGHT];
      }

    /*
     * determine the maximum side height
     */
    sideHeight = block_size;
    if (sideHeight < leftSideHeight)
	sideHeight = leftSideHeight;
    if (sideHeight < rightSideHeight)
	sideHeight = rightSideHeight;
    if (sideHeight < (*flow)[DIMS_LEFT][DIMS_HEIGHT])
        sideHeight  = (*flow)[DIMS_LEFT][DIMS_HEIGHT];
    if (sideHeight < (*flow)[DIMS_RIGHT][DIMS_HEIGHT])
        sideHeight = (*flow)[DIMS_RIGHT][DIMS_HEIGHT];

    /*
     * calculate the starting Y position for each of the positions
     * reuse the arrays that were used to save the max dimension values.
     */
    for (i = DIMS_LM; i <= DIMS_RM; i++)
      {
	(*top_bot)[DIMS_TOP]   [i][DIMS_YPOS] = start_y;
	(*top_bot)[DIMS_BOTTOM][i][DIMS_YPOS] = start_y + max_top + sideHeight;
      }

    (*corner)[DIMS_TC][DIMS_LEFT]  = start_y;
    (*corner)[DIMS_TC][DIMS_RIGHT] = start_y;

    (*corner)[DIMS_BC][DIMS_LEFT]  = start_y + max_top + sideHeight;
    (*corner)[DIMS_BC][DIMS_RIGHT] = start_y + max_top + sideHeight;

    (*side)[DIMS_TOP][DIMS_LEFT]  = start_y + max_top;
    (*side)[DIMS_TOP][DIMS_RIGHT] = start_y + max_top;

    (*flow)[DIMS_LEFT ][DIMS_YPOS] = start_y + max_top;
    (*flow)[DIMS_RIGHT][DIMS_YPOS] = start_y + max_top;

    (*side)[DIMS_CENTER][DIMS_LEFT]  = start_y + max_top +
			(sideHeight - (*side)[DIMS_CENTER][DIMS_LEFT]) / 2;
    (*side)[DIMS_CENTER][DIMS_RIGHT] = start_y + max_top +
			(sideHeight - (*side)[DIMS_CENTER][DIMS_RIGHT]) / 2;

    (*side)[DIMS_BOTTOM][DIMS_LEFT]  = start_y + max_top +
			sideHeight - (*side)[DIMS_BOTTOM][DIMS_LEFT];
    (*side)[DIMS_BOTTOM][DIMS_RIGHT] = start_y + max_top +
			sideHeight - (*side)[DIMS_BOTTOM][DIMS_RIGHT];

    if (ret_side_size != NULL)
	*ret_side_size = sideHeight;
}

/******************************************************************************
 * Function: AdjustHead
 *
 * Parameters:
 *		base_left	Specifies the x position that the controller
 *				occupying 'left_margin' space would start
 *				at.
 *		block_width	Specifies the body's width for which to
 *				center or align a controller with.
 *		left_margin	Specifies the space used by a controller
 *				that is on the left side of the body.
 *		right_margin	Specifies the space used by a controller
 *				on the right side of the body.
 *
 *****************************************************************************/
static void
AdjustHeadPosition(
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvSegmentI	*p_seg,
    TopDims		*top_bot,
    SideDims		*side,
    CornerDims		*corner,
    FlowDims		*flow,
    LayFrmtInfo		*info,
    _DtCvUnit		 base_y,
    _DtCvUnit		 base_left,
    _DtCvUnit		 block_width,
    _DtCvUnit		 left_margin,
    _DtCvUnit		 right_margin)
{
    int		  i         = DIMS_TOP;		/* also DIMS_TC */
    int		  j         = DIMS_LEFT;	/* also DIMS_LM */
    int		  divisor   = 2;
    _DtCvUnit	  adjustX   = 0;
    _DtCvUnit	  adjustY   = 0;
    _DtCvUnit	  newY      = 0;
    _DtCvUnit	  headWidth = info->width;
    _DtCvFrmtOption  orient    = ObjHorizOrient(p_seg);
    _DtCvFrmtOption  vOrient   = ObjVertOrient(p_seg);

    if (_DtCvContainerPercentOfSeg(p_seg) == 10000
			&& orient == _DtCvJUSTIFY_CENTER
			&& TxtHorizJustify(p_seg) == _DtCvJUSTIFY_LEFT)
	headWidth = block_width;

    switch (orient)
      {
	case _DtCvJUSTIFY_RIGHT_MARGIN:
		divisor = 1;
		j++;
	case _DtCvJUSTIFY_CENTER:
		adjustX = (block_width - headWidth) / divisor;
		j++;
	case _DtCvJUSTIFY_LEFT_MARGIN:
		adjustX += left_margin;
		if (vOrient == _DtCvJUSTIFY_BOTTOM)
		    i = DIMS_BOTTOM;

		newY  = (*top_bot)[i][j][DIMS_YPOS];
		(*top_bot)[i][j][DIMS_YPOS] += info->height;
		break;

	case _DtCvJUSTIFY_RIGHT_CORNER:
		adjustX = block_width + left_margin;
		j       = DIMS_RIGHT;

	case _DtCvJUSTIFY_LEFT_CORNER:
		if (vOrient == _DtCvJUSTIFY_BOTTOM)
		    i = DIMS_BC;

		newY = (*corner)[i][j];
		(*corner)[i][j] += info->height;
		break;

	case _DtCvJUSTIFY_RIGHT:
		adjustX = block_width + left_margin;
		j = DIMS_RIGHT;

	case _DtCvJUSTIFY_LEFT:
		if (vOrient != _DtCvJUSTIFY_TOP)
		    i++;
		if (vOrient == _DtCvJUSTIFY_BOTTOM)
		    i++;

		if (_DtCvContainerFlowOfSeg(p_seg) == _DtCvWRAP)
		  {
		    if (orient == _DtCvJUSTIFY_LEFT)
			adjustX += left_margin;
		    else
			adjustX -= headWidth;

		    newY = (*flow)[j][DIMS_YPOS];
		    (*flow)[j][DIMS_YPOS] += info->height;
		  }
		else
		  {
		    newY = (*side)[i][j];
		    (*side)[i][j] += info->height;
		  }
		break;
      }

    adjustY  = newY - base_y;
    adjustX += base_left;

    /*
     * adjust the text positions
     */
    AdjustTextPositions(canvas, info->cnt.beg_txt, info->cnt.end_txt,
								adjustX, adjustY);

    /*
     * adjust the lines positions
     */
    AdjustLinePositions(canvas, info->cnt.beg_ln, info->cnt.end_ln,
								adjustX, adjustY);

    /*
     * adjust the page breaks, but only if necessary.
     */
    AdjustPgBrk(canvas, info->cnt.beg_brk, info->cnt.end_brk, adjustY);
}

/******************************************************************************
 * Function: InitDimArrays
 *
 *****************************************************************************/
static void
InitDimArrays(
    TopDims		*top_bot,
    SideDims		*side,
    CornerDims		*corner,
    FlowDims		*flow)
{
    int		    i;
    int		    j;

    for (i = DIMS_TOP; i <= DIMS_BOTTOM; i++)
      {
	for (j = DIMS_LM; j <= DIMS_RM; j++)
	  {
	    (*top_bot)[i][j][DIMS_WIDTH] = 0;
	    (*top_bot)[i][j][DIMS_HEIGHT] = 0;
	  }
	for (j = DIMS_LEFT; j <= DIMS_RIGHT; j++)
	    (*side)[i][j] = 0;
      }
    for (i = DIMS_LEFT; i <= DIMS_RIGHT; i++)
      {
	for (j = DIMS_WIDTH; j <= DIMS_HEIGHT; j++)
	 {
	    (*corner)[i][j] = 0;
	    (*flow)[i][j]   = 0;
	 }
      }
}

/******************************************************************************
 * Function: ProcessController
 *****************************************************************************/
static LayFrmtInfo *
ProcessController(
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvSegmentI	*cur_seg)
{
    int		   getLn;
    int		   saveTravCnt  = canvas->trav_cnt;
    _DtCvUnit	   saveYPos     = layout->info.y_pos;
    _DtCvUnit	   saveMaxWidth = layout->max_width;
    _DtCvUnit	   saveLeft     = layout->left;
    _DtCvUnit	   saveRight    = layout->right;
    _DtCvUnit	   maxWidth;
    _DtCvUnit	   maxXPos;
    _DtCvUnit	   myMaxWidth;
    LayFrmtInfo   *frmtInfo;
    DataPoint	   basePt;
    DataPoint	   zeroPt;
    _DtCvValue	   redo;

    /*
     * Controllers always break the formatting sequence.
     * So save any information in the buffer, reset the margins and
     * add the appropriate lines, and check for going over boundaries.
     */
    CheckSaveInfo(canvas, layout, cur_seg, 0);
    CheckFormat(layout, True);

    /*
     * Get the controller specific information.
     * disallow some of the orientation & vOrient combinations
     */
    if ((ObjHorizOrient(cur_seg) == _DtCvJUSTIFY_CENTER
			&& ObjVertOrient(cur_seg) != _DtCvJUSTIFY_BOTTOM)
		||
	(ObjVertOrient(cur_seg) == _DtCvJUSTIFY_CENTER
			&& ObjHorizOrient(cur_seg) != _DtCvJUSTIFY_LEFT
			&& ObjHorizOrient(cur_seg) != _DtCvJUSTIFY_RIGHT))
	ObjVertOrient(cur_seg) = _DtCvJUSTIFY_TOP;

    if (_DtCvContainerFlowOfSeg(cur_seg) == _DtCvWRAP
			&&
	    (ObjVertOrient(cur_seg) != _DtCvJUSTIFY_TOP
				||
		(ObjVertOrient(cur_seg) == _DtCvJUSTIFY_TOP
			&& ObjHorizOrient(cur_seg) != _DtCvJUSTIFY_LEFT
			&& ObjHorizOrient(cur_seg) != _DtCvJUSTIFY_RIGHT)))
	_DtCvContainerFlowOfSeg(cur_seg) = _DtCvWRAP_NONE;

    if (_DtCvContainerFlowOfSeg(cur_seg) == _DtCvWRAP_JOIN
			&&
	    (ObjVertOrient(cur_seg) != _DtCvJUSTIFY_TOP
		|| ObjHorizOrient(cur_seg) != _DtCvJUSTIFY_LEFT_MARGIN))
      {
	ObjVertOrient(cur_seg)  = _DtCvJUSTIFY_TOP;
	ObjHorizOrient(cur_seg) = _DtCvJUSTIFY_LEFT_MARGIN;
      }
    
    /*
     * malloc a formatting dimension structure and initialize it with
     * default values. This will be returned to the caller.
     */
    frmtInfo  = (LayFrmtInfo *) malloc (sizeof(LayFrmtInfo));
    *frmtInfo = DefLayFrmtInfo;

    /*
     * the controller object begins here.
     */
    SetBeginCounts(canvas, &(frmtInfo->cnt));

    /*
     * set the parent's data point in the stack
     */
    GetCurrentDataPoint(layout, &basePt);

    /*
     * calculate the amount of space the controller can occupy.
     * first calculate the amount of space to work with.
     * then truncate to zero if necessary.
     * then use the percentage of that remaining area as the space
     *      the controller's segments can occupy.
     */
    myMaxWidth = layout->max_width - basePt.left - basePt.right
							- saveLeft - saveRight;
    if (myMaxWidth < 0)
	myMaxWidth = 0;

    myMaxWidth = (_DtCvUnit) (((double) myMaxWidth)
			* ((double) _DtCvContainerPercentOfSeg(cur_seg))
			/ HeadDivisor);


    /*
     * Format the controller at a 'zero'ed point.
     * The lines it generates will be moved later to their correct position.
     */
    layout->left  = 0;
    layout->right = 0;
    zeroPt = DefDataPt;
    PushDataPoint(layout, &zeroPt);

    /*
     * now process as a regular container
     */
    do {
	/*
	 * set some counts and flags (necessary for a redo).
	 */
	redo = False;
	canvas->trav_cnt    = saveTravCnt;
	canvas->line_cnt    = frmtInfo->cnt.beg_ln;
	canvas->txt_cnt     = frmtInfo->cnt.beg_txt;
        layout->max_width   = myMaxWidth;
        layout->info.y_pos  = 0;

	/*
	 * process the container
	 */
        ProcessContainer(canvas,layout,cur_seg,-1,&maxWidth,&maxXPos,&getLn);

	/*
	 * check to see if we need to reformat because the minimum size
	 * is larger than we asked for.
	 */
	if (maxXPos + _DtCvContainerRMarginOfSeg(cur_seg) > myMaxWidth)
	  {
	    redo       = True;
	    myMaxWidth = maxXPos + _DtCvContainerRMarginOfSeg(cur_seg);
	  }
      } while (True == redo);
    
    /*
     * remove this element's data points from the stack.
     */
    RemoveDataPoint(layout, &zeroPt);

    /*
     * set the ending counts for the items in this container.
     */
    SetEndCounts(canvas, &(frmtInfo->cnt), getLn);
    frmtInfo->width  = myMaxWidth;
    frmtInfo->height = layout->info.y_pos;

    /*
     * does this controller want to join with the lines in a non-controller?
     */
    if (_DtCvWRAP_JOIN == _DtCvContainerFlowOfSeg(cur_seg)
				&& frmtInfo->cnt.beg_txt != canvas->txt_cnt)
	_DtCvSetJoinInfo(&(layout->info), True, canvas->txt_cnt - 1);

    /*
     * Restore the previous information
     */
    if (NULL != layout->lst_rendered)
	layout->lst_rendered->next_disp = NULL;

    layout->left           = saveLeft;
    layout->right          = saveRight;
    layout->max_width      = saveMaxWidth;
    layout->lst_rendered   = NULL;
    layout->info.y_pos     = saveYPos;

    return frmtInfo;
}

/******************************************************************************
 * Function: AdjustForBorders
 *
 * Initializes the display line and graphic tables.
 *****************************************************************************/
static void
AdjustForBorders(
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvFrmtOption	 brdr,
    _DtCvUnit		 line_width,
    _DtCvUnit		*ret_bot,
    _DtCvUnit		*ret_right)
{
    /*
     * if the line_width is zero, make it 1 so that is really takes
     * up some space.
     */
    if (0 == line_width)
	line_width = 1;

    /*
     * set the flag for processing a border
     */
    if (_DtCvBORDER_NONE != brdr)
	layout->brdr_flag = True;

    /*
     * check to see if this element has a border. If so, adjust the
     * boundaries.
     */
    if (brdr == _DtCvBORDER_FULL || brdr == _DtCvBORDER_HORZ
				 || brdr == _DtCvBORDER_TOP
				 || brdr == _DtCvBORDER_TOP_LEFT
				 || brdr == _DtCvBORDER_TOP_RIGHT)
        layout->info.y_pos += line_width;

    *ret_bot = 0;
    if (brdr == _DtCvBORDER_FULL || brdr == _DtCvBORDER_HORZ
				 || brdr == _DtCvBORDER_BOTTOM
				 || brdr == _DtCvBORDER_BOTTOM_LEFT
				 || brdr == _DtCvBORDER_BOTTOM_RIGHT)
        *ret_bot = line_width;

    if (brdr == _DtCvBORDER_FULL || brdr == _DtCvBORDER_VERT
				 || brdr == _DtCvBORDER_LEFT
				 || brdr == _DtCvBORDER_TOP_LEFT
				 || brdr == _DtCvBORDER_BOTTOM_LEFT)
        layout->left += line_width;

    *ret_right = 0;
    if (brdr == _DtCvBORDER_FULL || brdr == _DtCvBORDER_VERT
				 || brdr == _DtCvBORDER_RIGHT
				 || brdr == _DtCvBORDER_TOP_RIGHT
				 || brdr == _DtCvBORDER_BOTTOM_RIGHT)
      {
        layout->right += line_width;
	*ret_right     = line_width;
      }
}

/******************************************************************************
 * Function: DrawBorders
 *
 * Parameters:
 *		canvas		Specifies the virtual canvas on which
 *				lines are drawn.
 *		layout		Specifies the current layout information.
 *		brdr		Specifies the type of border.
 *		top_y		Specifes the top y of the bounding box for
 *				the object. Any border drawn should be
 *				completely below this y.
 *		bot_y		Specifes the bottom y of the bounding box
 *				for the object. Any border drawn should be
 *				completely below this y.
 *		left_x		Specifies the left x of the bounding box
 *				for the object. Any border drawn should be
 *				completely to the right of this x.
 *		right_x		Specifies the right x of the bounding box
 *				for the object. Any border drawn should be
 *				completely to the left of this x.
 *
 *          left_x               right_x
 *            |                     |
 *            v                     v
 * top_y ---> xxxxxxxxxxxxxxxxxxxxxx
 *            xxxxxxxxxxxxxxxxxxxxxx
 *            xx------------------xx
 *            xx|                |xx  (xx represents the line.)
 *            xx|                |xx
 *            xx|                |xx
 *            xx|                |xx
 *            xx------------------xx
 * bot_y ---> xxxxxxxxxxxxxxxxxxxxxx
 *            xxxxxxxxxxxxxxxxxxxxxx
 *
 *****************************************************************************/
static int
DrawBorders(
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvFrmtOption	 brdr,
    _DtCvPointer	 data,
    _DtCvUnit		 line_width,
    _DtCvUnit		 top_y,
    _DtCvUnit		 bot_y,
    _DtCvUnit		 left_x,
    _DtCvUnit		 right_x)
{
    int         mod       = 1;
    int		cnt       = canvas->line_cnt;
    _DtCvUnit	width;

    /*
     * if line_width is zero, make it 1 so that it really takes
     * up some space.
     */
    if (0 == line_width)
	line_width = 1;

    /*
     * calculate the width of the element.
     */
    width = right_x - left_x;

    /*
     * If borders are specified, draw them
     */
    if (brdr != _DtCvBORDER_NONE)
      {
	/*
	 * now do the horizontal borders. the coordinates are the top,
	 * left most unit of the line.
	 */
	switch(brdr)
	  {
	    case _DtCvBORDER_FULL:
	    case _DtCvBORDER_HORZ:
	    case _DtCvBORDER_BOTTOM:
	    case _DtCvBORDER_BOTTOM_LEFT:
	    case _DtCvBORDER_BOTTOM_RIGHT:
			SaveLine(canvas, layout, _DtCvLINE_HORZ,
						data, line_width,
						left_x, bot_y, width);
			mod = -1;
			if (brdr == _DtCvBORDER_BOTTOM
					|| brdr == _DtCvBORDER_BOTTOM_LEFT
					|| brdr == _DtCvBORDER_BOTTOM_RIGHT)
			    break;
    
	    case _DtCvBORDER_TOP:
	    case _DtCvBORDER_TOP_LEFT:
	    case _DtCvBORDER_TOP_RIGHT:
			SaveLine(canvas, layout, _DtCvLINE_HORZ,
						data, line_width,
						left_x, top_y, width);
	  }
    
	/*
	 * for vertical lines, the coordinates are the top, right most
	 * unit of the line.
	 */
	switch(brdr)
	  {
	    case _DtCvBORDER_FULL:
	    case _DtCvBORDER_BOTTOM_LEFT:
			/*
			 * include the line width in length for a full
			 * border.
			 */
			bot_y += line_width;

	    case _DtCvBORDER_VERT:
	    case _DtCvBORDER_LEFT:
	    case _DtCvBORDER_TOP_LEFT:
			SaveLine(canvas, layout, _DtCvLINE_VERT,
						data, line_width,
						left_x, top_y, bot_y - top_y);
			if (brdr == _DtCvBORDER_LEFT
					|| brdr == _DtCvBORDER_TOP_LEFT
					|| brdr == _DtCvBORDER_BOTTOM_LEFT)
			    break;
    
	    case _DtCvBORDER_BOTTOM_RIGHT:
			/*
			 * if we didn't fall thru from above, we need to
			 * add the extension to the bottom to get the
			 * full length for the right vertical line.
			 */
			if (brdr == _DtCvBORDER_BOTTOM_RIGHT)
			    bot_y += line_width;

	    case _DtCvBORDER_RIGHT:
	    case _DtCvBORDER_TOP_RIGHT:
			SaveLine(canvas, layout, _DtCvLINE_VERT,
						data, line_width,
				right_x - line_width, top_y, bot_y - top_y);
	  }
      }

    return ((canvas->line_cnt - cnt) * mod);
}

/******************************************************************************
 * Function: AdjustObjectPosition
 *
 * Parameters:
 *		canvas		Specifies the virtual canvas on which
 *				lines are drawn.
 *		justify		Specifies the vertical adjustment for the
 *				object.
 *		start_txt	Specifies the start index of the text list.
 *		start_gr	Specifies the start index of the graphics list.
 *		start_ln	Specifies the start index of the line list.
 *		end_txt		Specifies the end indext of the text list.
 *		end_gr		Specifies the end indext of the graphics list.
 *		end_ln		Specifies the end indext of the line list.
 *		height_adj	Specifies the internal height adjust value.
 *				Depending on the justify type, this may
 *				add to y_adj for text and regions, bottom
 *				lines and the height of vertical lines.
 *		y_adj		Specifies the y position adjustment.
 *				Lines, text and regions are moved this
 *				amount.
 *		internal_y	Specifies the internal y position adjustment.
 *				height_adj includes this value. Text and
 *				regions are moved this amount.
 *
 * Return:	nothing.
 *
 *****************************************************************************/
static void
AdjustObjectPosition(
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvFrmtOption	 justify,
    int			 start_txt,
    int			 start_ln,
    int			 start_brk,
    int			 end_txt,
    int			 end_ln,
    int			 end_brk,
    int			 brdr_cnt,
    _DtCvUnit		 height_adj,
    _DtCvUnit		 x_adj,
    _DtCvUnit		 y_adj,
    _DtCvUnit		 internal_y)
{
    int   mod  = 1;
    _DtCvUnit  yOff = 0;

    /*
     * If border count is negative, indicates the first line in the
     * list is bottom line. This requires special handling in adjusting
     * its position. Set flags accordingly.
     */
    if (brdr_cnt < 0)
      {
	mod = -1;
	brdr_cnt = -brdr_cnt;
      }

    /*
     * calculate the offset value within the object for other objects
     * contained in this object.
     */
    if (justify != _DtCvJUSTIFY_TOP)
      {
	yOff = height_adj - internal_y;
        if (justify == _DtCvJUSTIFY_CENTER)
            yOff /= 2;
      }
    
    yOff += y_adj;
    yOff += internal_y;

    /*
     * don't modify the border lines around this object yet.
     */
    end_ln -= brdr_cnt;

    /*
     * modify the border lines of the objects contained within
     * this object.
     */
    AdjustLinePositions(canvas, start_ln, end_ln, x_adj, yOff);

    /*
     * now adjust the border lines around this object.
     */
    start_ln = end_ln;
    end_ln += brdr_cnt;
    AdjustLinePositions(canvas, start_ln, end_ln, x_adj, y_adj);

    /*
     * now fix the lines if they've changed height and move
     * the first line to its bottom position if necessary.
     */
    if (0 != height_adj)
      {
        while (start_ln < end_ln)
          {
	    /*
	     * indicates the bottom line is the first line in the
	     * list. Move it down the height adjustment.
	     */
	    if (mod < 0)
	      {
                canvas->line_lst[start_ln].pos_y += height_adj;
                canvas->line_lst[start_ln].max_y += height_adj;

		mod = 1;
	      }
	    /*
	     * stretch the vertical lines
	     */
	    else if (canvas->line_lst[start_ln].dir == _DtCvLINE_VERT)
                canvas->line_lst[start_ln].max_y += height_adj;

            start_ln++;
          }
      }

    /*
     * adjust the position of the text within this object.
     */
    AdjustTextPositions (canvas, start_txt, end_txt, x_adj, yOff);

    /*
     * adjust the position of the text within this object.
     */
    AdjustPgBrk (canvas, start_brk, end_brk, yOff);
}

/******************************************************************************
 * Function: LinesMayChange
 *
 * Parameters:
 *		canvas		Specifies the virtual canvas on which
 *				lines are drawn.
 *		start_ln	Specifies the start index of the line list.
 *		end_ln		Specifies the end indext of the line list.
 *
 * Return:	True	if there is a vertical line as a child of a container.
 *		False	if there are no vertical lines in the child of a
 *			container/cell.
 *
 *****************************************************************************/
static _DtCvValue
LinesMayChange(
    _DtCanvasStruct	*canvas,
    int			 start_ln,
    int			 end_ln,
    int			 brdr_cnt)
{
    /*
     * If border count is negative, indicates that one of the lines
     * is for the bottom of the container/cell. Ignore for now.
     * we want to check the lines in the container/cell.
     */
    if (brdr_cnt < 0)
	brdr_cnt = -brdr_cnt;

    /*
     * get rid of the line count for this object,
     * AdjustObjectPosition can take care of it.
     */
    end_ln -= brdr_cnt;

    /*
     * Now check for vertical lines that would
     * be affected by a height change.
     */
    while (start_ln < end_ln)
      {
	if (_DtCvLINE_VERT == canvas->line_lst[start_ln].dir)
	    return True;
        start_ln++;
      }

    return False;
}

/******************************************************************************
 * Function: ProcessContainer
 *
 * Initializes the display line and graphic tables.
 *****************************************************************************/
static void
ProcessContainer(
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvSegmentI	*con_seg,
    _DtCvUnit		 min_y,
    _DtCvUnit		*ret_width,
    _DtCvUnit		*ret_max_x,
    int			*ret_cnt)
{
    int		   getLn;
    const char	  *saveJustifyChar = layout->info.align_char;
    _DtCvUnit	   yPad         = 0;
    _DtCvUnit	   xPad         = 0;
    _DtCvUnit	   maxWidth     = 0;
    _DtCvUnit	   maxXPos      = 0;
    _DtCvUnit	   myMinY       = -1;
    _DtCvUnit	   saveLeft     = layout->left;
    _DtCvUnit	   saveRight    = layout->right;
    _DtCvUnit	   saveLead     = layout->info.leading;
    _DtCvUnit	   saveFirst    = layout->first;
    _DtCvUnit	   saveYpos     = layout->info.y_pos;
    _DtCvValue	   saveStatic   = layout->stat_flag;
    _DtCvValue	   saveBrdr     = layout->brdr_flag;
    _DtCvFrmtOption   saveJustify  = layout->txt_justify;
    LayFrmtInfo    frmtInfo;
    DataPoint	   basePt;
    DataPoint	   curPt;

    /*
     * check to see if this element breaks the formatting sequence.
     * If so save any information in the buffer, reset the margins and
     * add the appropriate lines, and check for going over boundaries.
     */
    if (NotJoining(layout))
      {
	CheckSaveInfo(canvas, layout, con_seg, 0);
	CheckFormat(layout, True);
      }

    /*
     * check to see if this segment is the segment we want as our first
     * visible line.
     */
    CheckId(layout, _DtCvContainerIdOfSeg(con_seg));

    /*
     * Set beginning text and line counts
     */
    frmtInfo = DefLayFrmtInfo;
    SetBeginCounts(canvas, &(frmtInfo.cnt));

    /*
     * Get the first indent and set the current container pointer to me.
     */
    layout->first = _DtCvContainerFMarginOfSeg(con_seg) / layout->divisor;
    layout->left  = _DtCvContainerLMarginOfSeg(con_seg) / layout->divisor;
    layout->right = _DtCvContainerRMarginOfSeg(con_seg) / layout->divisor;
    layout->info.leading = _DtCvContainerLeadingOfSeg(con_seg);

    /*
     * check to see if we violate the horiz_pad_hint on the left, right or
     * first margins.
     */
    if (canvas->metrics.horiz_pad_hint > _DtCvContainerLMarginOfSeg(con_seg))
	layout->left = _DtCvContainerLMarginOfSeg(con_seg);
    else if (layout->left < canvas->metrics.horiz_pad_hint)
	layout->left = canvas->metrics.horiz_pad_hint;

    if (canvas->metrics.horiz_pad_hint > _DtCvContainerRMarginOfSeg(con_seg))
	layout->right = _DtCvContainerRMarginOfSeg(con_seg);
    else if (layout->right < canvas->metrics.horiz_pad_hint)
	layout->right = canvas->metrics.horiz_pad_hint;

    if (canvas->metrics.horiz_pad_hint > _DtCvContainerFMarginOfSeg(con_seg))
	layout->first = _DtCvContainerFMarginOfSeg(con_seg);
    else if (layout->first < canvas->metrics.horiz_pad_hint)
	layout->first = canvas->metrics.horiz_pad_hint;

    /*
     * check to see if there is more squeeze room available.
     */
    if (layout->left > canvas->metrics.horiz_pad_hint
			|| layout->right > canvas->metrics.horiz_pad_hint
			|| layout->first > canvas->metrics.horiz_pad_hint)
	layout->margin_non_zero = True;

    /*
     * set the formatting type for this container
     */
    layout->stat_flag = False;
    if (_DtCvContainerTypeOfSeg(con_seg) == _DtCvLITERAL)
	layout->stat_flag = True;

    /*
     * check to see if this element breaks the formatting sequence.
     * If so, add lines, etc.
     */
    if (NotJoining(layout))
      {
        /*
         * Adjust margins and y position for bordering
         */
        AdjustForBorders (canvas, layout, Border(con_seg), BrdWidth(con_seg),
								&yPad, &xPad);

        _DtCvAddSpace(_DtCvContainerTMarginOfSeg(con_seg),
							&(layout->info.y_pos));
	/*
	 * check for flow limits.
	 */
        CheckFormat(layout, True);

        /*
         * get the parent's data point in the stack
         * and add the current container's left and right to it.
         */
        GetCurrentDataPoint(layout, &basePt);
        basePt.left  += saveLeft;
        basePt.right += saveRight;

	/*
	 * if we don't inherit the the text justification
	 * set the new value.
	 */
	if (_DtCvINHERIT != _DtCvContainerJustifyOfSeg(con_seg))
	  {
	    layout->txt_justify  = _DtCvContainerJustifyOfSeg(con_seg);
	    if (_DtCvJUSTIFY_NUM == layout->txt_justify)
		layout->info.align_char = PeriodStr;
	    else if (_DtCvJUSTIFY_CHAR == layout->txt_justify)
	      {
		layout->info.align_char = _DtCvContainerJustifyCharOfSeg(con_seg);
		/*
		 * check to see if the character is 'valid'.
		 * if not, default out of JUSTIFY_CHAR
		 */
		if ( (NULL != layout->info.align_char) && ( '\0' != *layout->info.align_char)) 
		    layout->txt_justify = _DtCvJUSTIFY_LEFT;
	      }
	  }

	/*
	 * terminate the previous rendering list
	 */
	if (NULL != layout->lst_rendered)
	    layout->lst_rendered->next_disp = NULL;
	layout->lst_rendered = NULL;

	/*
	 * push the data point and reset margin/text info.
	 */
        PushDataPoint(layout, &basePt);
	SetMargins(layout);
	SetTextPosition(layout, True);
      }

    /*
     * determine the minimum Y that the child of this container should
     * occupy.  To do so, subtract off the bottom border pad and the
     * bottom margin.
     */
    if (0 < min_y)
      {
	myMinY = min_y - yPad;
	if (_DtCvWRAP_JOIN != _DtCvContainerFlowOfSeg(con_seg))
	  {
	    _DtCvUnit bPad = 0;

	    _DtCvAddSpace(_DtCvContainerBMarginOfSeg(con_seg), &bPad);
	    myMinY -= bPad;
	  }
      }

    /*
     * format the segment
     */
    saveYpos = layout->info.y_pos;

    GetCurrentDataPoint(layout, &curPt);

    /*
     * reset the max x variable
     */
    layout->info.cur_max_x = 0;
    ProcessSegmentList(canvas, layout, _DtCvContainerListOfSeg(con_seg),
					myMinY,
					&maxWidth, &maxXPos, NULL);
    /*
     * if this container forces a wrap join of the next item,
     * save the current line, but don't add space or null the
     * last rendered item.
     */
    CheckSaveInfo(canvas, layout, con_seg, 0);
    if (maxWidth < layout->info.cur_max_x - curPt.left + layout->right)
        maxWidth = layout->info.cur_max_x - curPt.left + layout->right;
    if (maxXPos < layout->info.cur_max_x)
        maxXPos = layout->info.cur_max_x;

    if (_DtCvWRAP_JOIN != _DtCvContainerFlowOfSeg(con_seg))
      {
        /*
         * Save any information in the buffer,
         * reset the margins and add the appropriate lines,
         * and check for going over boundaries.
         */
        _DtCvAddSpace(_DtCvContainerBMarginOfSeg(con_seg),
							&(layout->info.y_pos));

	/*
	 * terminate the previous rendering list
	 */
	if (NULL != layout->lst_rendered)
	    layout->lst_rendered->next_disp = NULL;
        layout->lst_rendered = NULL;
      }

    /*
     * remove this element's data points from the stack.
     */
    RemoveDataPoint(layout, &basePt);

    /*
     * include the bottom border (if needed) in the ending y position
     */
    layout->info.y_pos += yPad;

    /*
     * Set the ending counts for the lines and text in me.
     * This sets the ending counts for the lines contained in me,
     * NOT the lines in my border.
     */
    SetEndCounts(canvas, &(frmtInfo.cnt), 0);

    /*
     * does this object need to be adjust within its height?
     */
    if (0 < min_y && layout->info.y_pos < min_y)
      {
	AdjustObjectPosition(canvas, layout, TxtVertJustify(con_seg),
			frmtInfo.cnt.beg_txt, frmtInfo.cnt.beg_ln,
			frmtInfo.cnt.beg_brk,
			frmtInfo.cnt.end_txt, frmtInfo.cnt.end_ln,
			frmtInfo.cnt.end_brk,
			0, min_y - layout->info.y_pos - yPad, 0, 0, 0);
	layout->info.y_pos = min_y;
      }

    /*
     * Now draw the borders
     * If borders are drawn, cur_max_x & max_x_pos may get changed
     * if a right side border is drawn.
     */
    if (maxWidth < layout->max_width - curPt.left - curPt.right)
	maxWidth = layout->max_width - curPt.left - curPt.right;

    getLn = DrawBorders (canvas, layout, Border(con_seg),
				BrdData(con_seg), BrdWidth(con_seg),
				saveYpos, layout->info.y_pos - yPad,
				curPt.left,
				curPt.left + maxWidth);

    /*
     * check to see if we need to save the container counts away
     * because we might need to move the entire container as one
     * to honor the boundary. This will occur if the flag to honor
     * a boundary is set to _DtCvUSE_BOUNDARY_MOVE, this container has
     * border lines, the container's parent is not a table nor
     * is the container within another container that has a border.
     */
    if (_DtCvUSE_BOUNDARY_MOVE == canvas->constraint
			&& True == layout->brdr_flag
			&& False == saveBrdr && False == layout->table_flag)
      {
	GrpInfo *info = (GrpInfo *) malloc (sizeof(GrpInfo));

	/*
	 * warning - nothing done if malloc error.
	 */
	if (NULL != info)
	  {
	    /*
	     * initialize to the end information of the container.
	     */
	    info->cnt = frmtInfo.cnt;

	    /*
	     * take into account the borders for this container
	     */
	    SetEndCounts(canvas, &(info->cnt), getLn);

	    /*
	     * set the linked list information
	     */
	    info->next_info = layout->grp_lst;
	    layout->grp_lst = info;
	  }
      }

    /*
     * set the return values.
     */
    *ret_max_x = layout->info.cur_max_x;
    *ret_width = maxWidth;
    if (*ret_width < layout->max_width - curPt.left - curPt.right)
	*ret_width = layout->max_width - curPt.left - curPt.right;

    /*
     * Restore the previous information
     */
    layout->left      = saveLeft;
    layout->right     = saveRight;
    layout->first     = saveFirst;
    layout->stat_flag = saveStatic;
    layout->brdr_flag = saveBrdr;
    layout->info.leading = saveLead;

    /*
     * Besides checking for flow constraints, also (re)sets margins and
     * text information.
     */
    CheckFormat(layout, True);
    layout->txt_justify  = saveJustify;
    layout->info.align_char  = saveJustifyChar;

    /*
     * for tables and such, return how many lines were drawn around this
     * container.
     */
    if (ret_cnt)
	*ret_cnt = getLn;

    return;
}

/******************************************************************************
 * Function: ProcessSegmentList
 *
 * Process the segment list, laying it out according to left, right,
 * and first margins specified. Returns the max_width of the all
 * segments processed and the maximum x coordinate used.
 *****************************************************************************/
static void
ProcessSegmentList(
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvSegmentI	*cur_seg,
    _DtCvUnit		 min_y,
    _DtCvUnit		*ret_width,
    _DtCvUnit		*ret_max_x,
    int			**ret_vert)
{
    int		 junk;
    int		 saveTravCnt  = canvas->trav_cnt;
    int		 saveTxtCnt   = canvas->txt_cnt;
    int		 saveLineCnt  = canvas->line_cnt;
    int		 saveBrkCnt   = canvas->brk_cnt;
    _DtCvUnit	 tempX;
    _DtCvUnit	 tempLen;
    _DtCvUnit	 width;
    _DtCvUnit	 nWidth       = 0;
    _DtCvUnit	 leftMargin   = 0;
    _DtCvUnit	 rightMargin  = 0;
    _DtCvUnit	 topHeight    = 0;
    _DtCvUnit	 botHeight    = 0;
    _DtCvUnit	 maxWidth     = 0;
    _DtCvUnit	 saveYpos     = layout->info.y_pos;
    _DtCvSegmentI	*segStart     = cur_seg;
    LayFrmtInfo		*headInfo     = NULL;
    LayFrmtInfo		*lastHead     = NULL;
    LayFrmtInfo		*nxtHead;
    _DtCvLayoutInfo	 startInfo;

    _DtCvValue	 redo         = False;
    _DtCvValue	 joinCleared  = False;
    _DtCvValue	 flag;

    TopDims	 topBot;
    SideDims	 sideDims;
    CornerDims	 cornerDims;
    FlowDims	 flowDims;
    DataPoint	 basePt;
    DataPoint	 leftPt;
    DataPoint	 rightPt;

    /*
     * clear the controller arrays
     */
    InitDimArrays(&topBot, &sideDims, &cornerDims, &flowDims);

    /*
     * get the current left and right values or 'base'.
     */
    GetCurrentDataPoint(layout, &basePt);

    /*
     * ???
     */
    leftPt  = basePt;
    rightPt = basePt;
    leftPt.y_pos  = _CEFORMAT_ALL;
    rightPt.y_pos = _CEFORMAT_ALL;

    /*
     * process all the controller type containers in the segment list
     */
    while (NULL != cur_seg)
      {
	if (_DtCvIsSegContainer(cur_seg) && _DtCvIsSegController(cur_seg))
	  {
	    /*
	     * want to clear this once and only once. Then any join
	     * directives will survive, though if two or more controllers
	     * have the directive set, the 'last' one will win out.
	     */
	    if (False == joinCleared)
	      {
	        _DtCvSetJoinInfo(&(layout->info), False, -1);
		joinCleared = True;
	      }

	    /*
	     * process the 'controller'
	     */
	    nxtHead = ProcessController(canvas, layout, cur_seg);

	    /*
	     * update the dimension arrays so that the controller
	     * will get placed correctly.
	     */
	    UpdateDimensionArrays(cur_seg, nxtHead->width, nxtHead->height,
				&topBot, &sideDims, &cornerDims, &flowDims,
				&leftMargin, &rightMargin);

	    /*
	     * remember this controller.
	     */
	    if (NULL == headInfo)
		headInfo = nxtHead;
	    else
		lastHead->next_info = nxtHead;
	    lastHead = nxtHead;
	  }

	/*
	 * go to the next segment
	 */
	cur_seg = cur_seg->next_seg;
      }

    /*
     * Now reset the margins based on the controllers found
     */
    if (NULL != headInfo)
      {
	DetermineMaxDims(&topBot, &cornerDims, leftMargin, rightMargin,
			    &topHeight, &botHeight, &maxWidth);
	layout->info.y_pos += topHeight;
	layout->left	   += leftMargin;
	layout->right	   += rightMargin;
	DetermineFlowConstraints(layout, flowDims,
			    basePt.left, basePt.right,
			    layout->info.y_pos, &leftPt, &rightPt);

	/*
	 * get rid of the leftMargin and rightMargin values in maxWidth
	 * otherwise the use of layout->left & layout->right will double
	 * the value.
	 */
	maxWidth = maxWidth - leftMargin - rightMargin;
	if (layout->max_width < maxWidth + basePt.left + basePt.right +
			    layout->left + layout->right)
	    layout->max_width = maxWidth + basePt.left + basePt.right +
					    layout->left + layout->right;

	SetMargins(layout);
	SetTextPosition(layout, True);
	if (JoinSet(layout))
	  {
	    int  cnt;
	    int  joinLine = layout->info.join_line;
	    int  start    = canvas->txt_lst[joinLine].byte_index;
	    int  count    = canvas->txt_lst[joinLine].length;
	    _DtCvSegmentI *pSeg = canvas->txt_lst[joinLine].seg_ptr;
	    _DtCvUnit	  tmpWidth;

	    /*
	     * change the starting location of the following text.
	     * take into account the left margin that *hasn't*
	     * been added to the controlling container.
	     */
	    layout->info.text_x_pos  = canvas->txt_lst[joinLine].text_x
							+ layout->lmargin;
	    layout->info.cur_len = 0;

	    /*
	     * now calculate the width of this line.
	     */
	    while (pSeg != NULL && count > 0)
	      {
		_DtCvGetWidthOfSegment(canvas,pSeg,start,count,
						    &cnt, &tmpWidth, NULL);
		layout->info.text_x_pos += tmpWidth;
		count -= cnt;
		start  = 0;
		pSeg   = pSeg->next_disp;
	      }
	  }
      }

    /*
     * now format for non-controller containers and non-containers.
     * re-start at the beginning.
     */
    cur_seg   = segStart;

    /*
     * Save some information incase we have to redo the layout. I.e.
     * we overflow the sizing.
     */
    startInfo = layout->info;
    saveTravCnt = canvas->trav_cnt;
    saveTxtCnt   = canvas->txt_cnt;
    saveLineCnt  = canvas->line_cnt;
    saveBrkCnt   = canvas->brk_cnt;

    while (NULL != cur_seg)
      {
        width = layout->max_width - layout->info.text_x_pos
				- layout->rmargin - layout->info.cur_len;

	/*
	 * check to see if this item should start a line.
	 */
	CheckSetLineStart(layout, cur_seg);

	/*
	 * check to see if this item will cause a page break.
	 */
	CheckForPageBreak(canvas, cur_seg, layout->info.y_pos);

	switch (_DtCvPrimaryTypeOfSeg(cur_seg))
          {
	    case _DtCvCONTAINER:
		    if (!(_DtCvIsSegController(cur_seg)))
			ProcessContainer(canvas, layout, cur_seg, min_y,
						&junk, &junk, &junk);
		    break;

	    case _DtCvREGION:
		    /*
		     * flag that this segment needs a line number
		     */
		    cur_seg->internal_use = (void *) -1;

		    /*
		     * process the segment
		     */
		    if (_DtCvIsSegInLine(cur_seg))
		      {
			/*
			 * if a hypertext link, this will add it to
			 * the internal list.
			 */
			CheckAddToHyperList(canvas, cur_seg);

			/*
			 * get the traversal width
			 */
			nWidth = _DtCvGetTraversalWidth(canvas, cur_seg,
						    layout->info.lst_hyper);

			/*
			 * check to see if this region can end a line
			 */
			flag = _DtCvCheckLineSyntax(canvas,cur_seg,0,0,False);

			/*
			 * if this can't end a line, get the length up to
			 * the next segment that can and base whether to
			 * save pased on that.
			 */
			tempLen = 0;
			if (False == flag)
			  {
			    tempLen = _DtCvGetNextWidth(canvas,
						_DtCvSTRING,
						layout->info.lst_hyper,
						cur_seg->next_seg,
						0, cur_seg, NULL, NULL, NULL);
			    /*
			     * if the next width is zero, reset the flag.
			     */
			    if (tempLen <= 0)
				flag = True;
			  }
			tempLen += nWidth;

			/*
			 * if not joining, but my length goes over the
			 * working width, save out the current buffered
			 * information
			 */
			if (NotJoining(layout) &&
			    _DtCvWidthOfRegionSeg(cur_seg) + tempLen > width)
			    CheckSaveInfo (canvas, layout, cur_seg, 0);

			/*
			 * up counts on the buffered information.
			 */
			layout->info.line_bytes += 1;
			layout->info.cur_len    += 
				(_DtCvWidthOfRegionSeg(cur_seg) + nWidth);

			/*
			 * does the next segment need to join with
			 * this one? If so, set the information
			 */
			_DtCvSetJoinInfo(&(layout->info), (flag ? 0 : 1), -1);
		        if (_DtCvIsSegNewLine(cur_seg))
			    SaveInfo(canvas, layout, cur_seg, 0);
		      }
		    else
		      {
			/*
			 * clear out the join information
			 * standalone figures can't join with others.
			 */
			_DtCvSetJoinInfo(&(layout->info), False, -1);

			/*
			 * figures are standalone. Save any
			 * information in the buffer away.
			 */
			CheckSaveInfo (canvas, layout, cur_seg, 0);

			/*
			 * check to see if this segment is
			 * a hypertext. If so, add it to the
			 * list if it hasn't been added yet.
			 */
			CheckAddToHyperList(canvas, cur_seg);

			/*
			 * get the traversal width, if any.
			 */
			nWidth = _DtCvGetTraversalWidth(canvas, cur_seg,
						    layout->info.lst_hyper);

			/*
			 * now save the standalone figure
			 */
			layout->info.line_bytes += 1;
			layout->info.cur_len    += 
				_DtCvWidthOfRegionSeg(cur_seg) + nWidth;
			SaveInfo(canvas, layout, cur_seg->next_seg, 0);

			/*
			 * check for wrapping overflow
			 */
			CheckFormat(layout, False);
		      }

		    /*
		     * indicate this segment as the last item rendered
		     */
		    if (NULL != layout->lst_rendered)
		        layout->lst_rendered->next_disp = cur_seg;
		    layout->lst_rendered = cur_seg;
		    break;

	    case _DtCvLINE:
		    /*
		     * lines are standalone. Save any
		     * information in the buffer away.
		     */
		    CheckSaveInfo (canvas, layout, cur_seg, 0);

		    /*
		     * if the line_width is zero, make it 1 so
		     * that it really does take some space.
		     */
		    nWidth = _DtCvWidthOfLineSeg(cur_seg);
		    if (0 == nWidth)
			nWidth = 1;

		    /*
		     * start with it going all the way across the window.
		     */
		    width = layout->max_width;
		    tempX = 0;

		    /*
		     * or does it only extend across the container?
		     */
		    if (_DtCvIsSegBlockLine(cur_seg))
		      {
			tempX = layout->lmargin;
			width = layout->max_width - tempX - layout->rmargin;
		      }

		    SaveLine (canvas, layout, _DtCvLINE_HORZ,
				_DtCvDataOfLineSeg(cur_seg), nWidth, tempX,
				layout->info.y_pos, width);

		    layout->info.y_pos += nWidth;
		    break;

	    case _DtCvMARKER:
		    /*
		     * check to see if marker is the target id
		     */
		    CheckId(layout, _DtCvIdOfMarkerSeg(cur_seg));
		    break;

	    case _DtCvNOOP:
		    if (_DtCvIsSegNewLine(cur_seg))
			SaveInfo(canvas, layout, cur_seg, 0);
		    break;

	    case _DtCvSTRING:
		    /*
		     * flag that this segment needs a line number
		     */
		    cur_seg->internal_use = (void *) -1;

		    /*
		     * process the string
		     */
		    ProcessStringSegment(canvas, layout, cur_seg);

		    /*
		     * check for wrapping overflow.
		     */
		    if (_DtCvIsSegNewLine(cur_seg))
			CheckFormat(layout, False);

		    /*
		     * indicate this segment as the last item rendered
		     */
		    if (NULL != layout->lst_rendered)
		        layout->lst_rendered->next_disp = cur_seg;
		    layout->lst_rendered = cur_seg;
		    break;

	    case _DtCvTABLE:
		    ProcessTable(canvas, layout, cur_seg, min_y);
	    default:
		    break;
	  }

	/*
	 * get the next segment
	 */
	cur_seg = cur_seg->next_seg;

	/*
	 * check the flowing text points
	 */
	if (leftPt.y_pos > 0 &&
		leftPt.x_units > layout->max_width - leftPt.left - leftPt.right)
	  {
	    layout->max_width = leftPt.x_units + leftPt.left + leftPt.right;
	    redo = True;
	  }
	if (rightPt.y_pos > 0 &&
		rightPt.x_units > layout->max_width-rightPt.left-rightPt.right)
	  {
	    layout->max_width = rightPt.x_units + rightPt.left + rightPt.right;
	    redo = True;
	  }

	/*
	 * have we violated the available space?
	 * if so, we'll have to reformat.
	 */
	if (redo == True)
	  {
	    redo             = False;
	    cur_seg          = segStart;
	    canvas->trav_cnt = saveTravCnt;
	    canvas->txt_cnt  = saveTxtCnt;
	    canvas->line_cnt = saveLineCnt;
	    canvas->brk_cnt  = saveBrkCnt;
	    layout->info     = startInfo;

	    if (rightPt.y_pos > 0)
	      {
	        RemoveDataPoint(layout, &rightPt); /* make sure its gone */
	        InsertDataPoint(layout, &rightPt);
	      }
	    if (leftPt.y_pos > 0)
	      {
	        RemoveDataPoint(layout, &leftPt); /* make sure its gone */
	        InsertDataPoint(layout, &leftPt);
	      }

	    /*
	     * Now reset the margins based on the controllers found
	     */
	    if (NULL != headInfo)
	      {
		layout->left   += leftMargin;
		layout->right  += rightMargin;
		SetMargins(layout);
		SetTextPosition(layout, True);
		if (JoinSet(layout))
		  {
		    int  cnt;
		    int  joinLine = layout->info.join_line;
		    int  start    = canvas->txt_lst[joinLine].byte_index;
		    int  count    = canvas->txt_lst[joinLine].length;
		    _DtCvSegmentI *pSeg = canvas->txt_lst[joinLine].seg_ptr;
		    _DtCvUnit	  tmpWidth;
	
		    /*
		     * change the starting location of the following text.
		     * take into account the left margin that *hasn't*
		     * been added to the controlling container.
		     */
		    layout->info.text_x_pos  = canvas->txt_lst[joinLine].text_x
							+ layout->lmargin;
		    layout->info.cur_len = 0;
	
		    /*
		     * now calculate the width of this line.
		     */
		    while (pSeg != NULL && count > 0)
		      {
			_DtCvGetWidthOfSegment(canvas,pSeg,start,count,
							&cnt, &tmpWidth, NULL);
			layout->info.text_x_pos += tmpWidth;
			count -= cnt;
			start  = 0;
			pSeg   = pSeg->next_disp;
		      }
		  }
	      }
	  }
      }

    RemoveDataPoint(layout, &leftPt);
    RemoveDataPoint(layout, &rightPt);

    /*
     * if there were heads, now place them correctly.
     */
    if (NULL != headInfo)
      {
	_DtCvUnit	blockHeight;
	_DtCvUnit	blockWidth;

	/*
	 * make sure all of the information in the body is saved out
	 */
	CheckSaveInfo (canvas, layout, NULL, 0);

	/*
	 * now calculate the non-controllers overall height.
	 */
	blockHeight = layout->info.y_pos - saveYpos - topHeight;

	/*
	 * now figure the head positions.
	 */
	DetermineHeadPositioning(&topBot, &sideDims, &cornerDims, &flowDims,
				saveYpos, topHeight,
				blockHeight, &blockHeight);
	/*
	 * if the maximum available space was exceeded by the text
	 * calculate a new max width
	 */
	if (layout->max_width < layout->info.cur_max_x + layout->right)
	    layout->max_width = layout->info.cur_max_x + layout->right;

	blockWidth = layout->max_width - basePt.left - basePt.right
						- layout->left - layout->right;

	nxtHead = headInfo;
	cur_seg = segStart;
	while (cur_seg != NULL)
	  {
	    if (_DtCvIsSegContainer(cur_seg) && _DtCvIsSegController(cur_seg))
	      {
	        AdjustHeadPosition(canvas, layout, cur_seg,
			&topBot, &sideDims, &cornerDims, &flowDims, nxtHead,
			0, basePt.left + layout->left - leftMargin, blockWidth,
			leftMargin, rightMargin);
	        /*
	         * go to the next head element
		 */
	        nxtHead = nxtHead->next_info;

	        /*
	         * free the information.
	         */
	        free(headInfo);
	        headInfo = nxtHead;
	      }

	    /*
	     * got to the next segment
	     */
	    cur_seg = cur_seg->next_seg;
	  }

	if (layout->info.y_pos < saveYpos + topHeight + blockHeight)
	    layout->info.y_pos = saveYpos + topHeight + blockHeight;

	layout->info.y_pos += botHeight;
      }

    /*
     * set the return values
     */
    *ret_width = layout->info.cur_max_x - basePt.left + layout->right;
    *ret_max_x = layout->info.cur_max_x;

    return;
}

/*****************************************************************************
 * Function:    static _DtCvUnit MaxOfGroup (
 *
 * Purpose: Determine the maximum of a group.
 *****************************************************************************/
static void
MaxOfGroup (
    GrpInfo		*group,
    _DtCvDspLine	*text,
    _DtCvLineSeg	*lines,
    _DtCvUnit		 max_x,
    _DtCvUnit		 max_y)
{
    int		 i;

    /*
     * initialize
     */
    group->min_x = max_x;
    group->max_x = 0;
    group->top_y = max_y;
    group->bot_y = 0;

    /*
     * find the maximum of the group
     */
    for (i = group->cnt.beg_txt; i < group->cnt.end_txt; i++)
      {
	/*
	 * check for min's and max's
	 */
	if (group->min_x > text[i].text_x)
	    group->min_x = text[i].text_x;

	if (group->max_x < text[i].max_x)
	    group->max_x = text[i].max_x;

	if (group->top_y > text[i].baseline - text[i].ascent)
	    group->top_y = text[i].baseline - text[i].ascent;

	if (group->bot_y < text[i].baseline + text[i].descent)
	    group->bot_y = text[i].baseline + text[i].descent;

	/*
	 * indicate that this line has been processed already
	 */
	_DtCvSetProcessed(text[i]);
      }

    for (i = group->cnt.beg_ln; i < group->cnt.end_ln; i++)
      {
	/*
	 * check for min's and max's
	 */
	if (group->min_x > lines[i].pos_x)
	    group->min_x = lines[i].pos_x;

	if (group->max_x < lines[i].max_x)
	    group->max_x = lines[i].max_x;

	if (group->top_y > lines[i].pos_y)
	    group->top_y = lines[i].pos_y;

	if (group->bot_y < lines[i].max_y)
	    group->bot_y = lines[i].max_y;

	/*
	 * indicate that this line has been processed already
	 */
	_DtCvSetProcessed(lines[i]);
      }
}

/*****************************************************************************
 * Function:    static _DtCvUnit TestSpacing (
 *
 * Parameters:
 *
 * Returns:	True	if the object is before (x wise) the test object.
 *		False	if the object is not before (x wise) the text object.
 *
 * Purpose:
 * 
 *****************************************************************************/
static _DtCvStatus
TestSpacing (
    _DtCvUnit		 tst_top,
    _DtCvUnit		 tst_bot,
    _DtCvUnit		 tst_min,
    _DtCvUnit		 obj_top,
    _DtCvUnit		 obj_bot,
    _DtCvUnit		 obj_max,
    _DtCvUnit		 needed,
    _DtCvUnit		 min_space,
    _DtCvUnit		*ret_amount)
{
    _DtCvStatus  result = False;

    /*
     * check to see if the object is to the left of the test object
     * to move and that it 'infringes' on the vertical
     * space of the test object.
     *
     * I.e.     ----obj_top------
     *          |               |   ----tst_top----
     *          ----obj_bot------   |             |
     *                              ----tst_bot----
     *
     * I.e.                         ----tst_top----
     *          ----obj_top-------  |             |
     *          |                |  ----tst_bot----
     *          ----obj_bot-------
     *
     * I.e.     ----obj_top------
     *          |               |   ----tst_top----
     *          |               |   |             |
     *          |               |   ----tst_bot----
     *          ----obj_bot------
     *
     * I.e.                         ----tst_top----
     *          ----obj_top-------  |             |
     *          |                |  |             |
     *          ----obj_bot-------  |             |
     *                              ----tst_bot----
     */
    if (obj_max < tst_min
	&& True == _DtCvCheckInfringement(tst_top, tst_bot, obj_top, obj_bot)
	&& needed > tst_min - obj_max)
      {
	/*
	 * okay, this infringes on the object's space.
	 * truncate the amount of room there is to move the object
	 */
	result = True;
	needed = tst_min - obj_max;

	/*
	 * is the space between these two objects already squeezed
	 * below the minimum allowed?
	 */
	if (needed < min_space)
	    needed = 0;
	else /* leave the minimum space between the objects */
	    needed -= min_space;
      }

    *ret_amount = needed;
    return result;
}

/*****************************************************************************
 * Function:    static void MoveLeft (
 *
 * Parameters:
 *
 * Returns:
 *
 * Purpose:	Moves the object's rules/lines and text lines to the left.
 * 
 *****************************************************************************/
static void
MoveLeft (
    _DtCanvasStruct	*canvas,
    int			 beg_txt,
    int			 end_txt,
    int			 beg_ln,
    int			 end_ln,
    _DtCvUnit		 space)
{
    int		i;

    /*
     * bail now if nothing to do.
     */
    if (1 > space)
	return;

    /*
     * move each text/region line.
     */
    for (i = beg_txt; i < end_txt; i++)
      {
	canvas->txt_lst[i].text_x -= space;
	canvas->txt_lst[i].max_x  -= space;
      }

    /*
     * move each line/rule.
     */
    for (i = beg_ln; i < end_ln; i++)
      {
	canvas->line_lst[i].pos_x -= space;
	canvas->line_lst[i].max_x -= space;
      }
}

/*****************************************************************************
 * Function:    static _DtCvUnit CheckSpacing (
 *
 * Purpose:	Check the spacing before an object and move any objects
 *		before it to the left to make room.
 *
 *****************************************************************************/
static _DtCvUnit
CheckSpacing (
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    GrpInfo		*tst_grp,
    int			 txt_idx,
    int			 line_idx,
    _DtCvUnit		 top_y,
    _DtCvUnit		 bot_y,
    _DtCvUnit		 min_x,
    _DtCvUnit		 needed)
{
    int		 i;
    _DtCvUnit	 space;
    _DtCvUnit	 maxSpace;
    _DtCvUnit	 topY;
    _DtCvUnit	 botY;
    GrpInfo     *nxtGrp   = layout->grp_lst;

    /*
     * truncate if the amount needed is more than available.
     */
    if (min_x < needed)
	needed = min_x;

    maxSpace = needed;

    /*
     * see what group is before this group and how much space there is
     */
    while (NULL != nxtGrp)
      {
	/*
	 * as long as I'm not comparing against myself, try it.
	 */
	if (nxtGrp != tst_grp)
	  {
	    /*
	     * is this group before(x wise) the test group, infringing
	     * upon the test group's top and bottom positioning (y)
	     * and is the amount of space (x wise) between them smaller
	     * than the current smallest space found?
	     */
	    if (True == TestSpacing (top_y, bot_y, min_x,
					nxtGrp->top_y, nxtGrp->bot_y,
					nxtGrp->max_x, needed,
					canvas->metrics.horiz_pad_hint,
					&space))
	      {
		space += MoveGroup(canvas, layout, nxtGrp, needed - space);
		if (maxSpace > space)
		    maxSpace = space;
	      }
	  }

	/*
	 * check the next group
	 */
	nxtGrp = nxtGrp->next_info;
      }

    /*
     * look at each of the text lines;
     */
    for (i = 0; i < canvas->txt_cnt; i++)
      {
	/*
	 * Only look at those lines not already processed.
	 */
	if (i != txt_idx && _DtCvIsNotProcessed(canvas->txt_lst[i]))
	  {
	    topY = canvas->txt_lst[i].baseline - canvas->txt_lst[i].ascent;
	    botY = canvas->txt_lst[i].baseline - canvas->txt_lst[i].descent;
	    if (True == TestSpacing(top_y, bot_y, min_x, topY, botY,
				canvas->txt_lst[i].max_x, needed,
				canvas->metrics.horiz_pad_hint, &space))
	      {
		space += MoveText(canvas, layout, i, topY, botY, needed-space);
		if (maxSpace > space)
		    maxSpace = space;
	      }
	  }
      }

    /*
     * look at each of the rules/lines;
     */
    for (i = 0; i < canvas->line_cnt; i++)
      {
	/*
	 * Only look at those lines not already processed.
	 */
	if (i != line_idx && _DtCvIsNotProcessed(canvas->line_lst[i]))
	  {
	    /*
	     * calculate the top and bottom of the line
	     */
	    topY = canvas->line_lst[i].pos_y;
	    botY = canvas->line_lst[i].max_y;

	    if (True == TestSpacing(top_y, bot_y, min_x, topY, botY,
				canvas->line_lst[i].max_x, needed,
				canvas->metrics.horiz_pad_hint, &space))
	      {
		space += MoveLines(canvas, layout, i, topY, botY, needed-space);
		if (maxSpace > space)
		    maxSpace = space;
	      }
	  }
      }

    return maxSpace;
}

/*****************************************************************************
 * Function:    static _DtCvUnit MoveGroup (_DtCanvasStruct canvas);
 *
 * Purpose:	To move groupings (container, tables, etc.) as a group to
 *		honor boundaries.
 * 
 *****************************************************************************/
static _DtCvUnit
MoveGroup (
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    GrpInfo		*tst_grp,
    _DtCvUnit		 needed)
{
    _DtCvUnit	 space;

    /*
     * find out what's in front of it. And how much 'extra' room
     * there is.
     */
    space = CheckSpacing(canvas, layout, tst_grp, -1, -1,
				tst_grp->top_y, tst_grp->bot_y, tst_grp->min_x,
				needed);
    /*
     * now move the group
     */
    MoveLeft(canvas, tst_grp->cnt.beg_txt, tst_grp->cnt.end_txt,
			tst_grp->cnt.beg_ln, tst_grp->cnt.end_ln, space);

    tst_grp->max_x -= space;
    tst_grp->min_x -= space;

    return space;
}

/*****************************************************************************
 * Function:    static _DtCvUnit MoveText (_DtCanvasStruct canvas);
 *
 * Purpose: To move text lines to honor boundaries.
 * 
 *****************************************************************************/
static _DtCvUnit
MoveText (
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    int			 idx,
    _DtCvUnit		 top_y,
    _DtCvUnit		 bot_y,
    _DtCvUnit		 needed)
{
    _DtCvUnit	 space;

    /*
     * find out what's in front of it. And how much 'extra' room
     * there is.
     */
    space = CheckSpacing(canvas, layout, NULL, idx, -1,
				top_y, bot_y,
				canvas->txt_lst[idx].text_x,
				needed);
    /*
     * now move the group
     */
    MoveLeft(canvas, idx, idx, -1, -1, space);

    return space;
}

/*****************************************************************************
 * Function:    static _DtCvUnit MoveLines (_DtCanvasStruct canvas);
 *
 * Purpose: To move groupings (container, tables, etc.) as a group to
 *	    honor boundaries.
 * 
 *****************************************************************************/
static _DtCvUnit
MoveLines (
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    int			 idx,
    _DtCvUnit		 top_y,
    _DtCvUnit		 bot_y,
    _DtCvUnit		 needed)
{
    _DtCvUnit	 space;

    /*
     * find out what's in front of it. And how much 'extra' room
     * there is.
     */
    space = CheckSpacing(canvas, layout, NULL, -1, idx,
				top_y, bot_y,
				canvas->line_lst[idx].pos_x,
				needed);

    /*
     * now move the group
     */
    MoveLeft(canvas, -1, -1, idx, idx, space);

    return space;
}

/*****************************************************************************
 * Function:    static void CheckMoveInfo (_DtCanvasStruct canvas);
 *
 * Purpose:	To move each of the groupings, rules and text lines to
 *		honor boundaries.
 * 
 *****************************************************************************/
static void
CheckMoveInfo (
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout)
{
    int		 i;
    _DtCvUnit	 topY;
    _DtCvUnit	 botY;
    _DtCvUnit	 maxWidth = canvas->metrics.width;
    GrpInfo	*nxtGrp;

    /*
     * fill in the max x of each group
     */
    for (nxtGrp = layout->grp_lst; NULL != nxtGrp; nxtGrp = nxtGrp->next_info)
      {
	/*
	 * find the maximum of the group
	 */
	MaxOfGroup(nxtGrp, canvas->txt_lst, canvas->line_lst,
				layout->info.max_x_pos, layout->info.y_pos);
      }

    /*
     * now check each group for exceeding the boundary.
     */
    for (nxtGrp = layout->grp_lst; NULL != nxtGrp; nxtGrp = nxtGrp->next_info)
      {
	/*
	 * does this group exceed the boundary?
	 */
	if (maxWidth < nxtGrp->max_x)
	     (void) MoveGroup(canvas, layout, nxtGrp, nxtGrp->max_x - maxWidth);
      }

    /*
     * look at each of the text lines;
     */
    for (i = 0; i < canvas->txt_cnt; i++)
      {
	/*
	 * Only look at those lines not already processed.
	 */
	if (_DtCvIsNotProcessed(canvas->txt_lst[i]) &&
					maxWidth < canvas->txt_lst[i].max_x)
	  {
	    topY = canvas->txt_lst[i].baseline - canvas->txt_lst[i].ascent;
	    botY = canvas->txt_lst[i].baseline - canvas->txt_lst[i].descent;
	    (void) MoveText(canvas, layout, i, topY, botY,
					canvas->txt_lst[i].max_x - maxWidth);
	  }
      }

    /*
     * look at each of the rules/lines;
     */
    for (i = 0; i < canvas->line_cnt; i++)
      {
	/*
	 * Only look at those lines not already processed.
	 */
	if (_DtCvIsNotProcessed(canvas->line_lst[i]) &&
					maxWidth < canvas->line_lst[i].max_x)
	  {
	    /*
	     * calculate the top and bottom of the line
	     */
	    topY = canvas->line_lst[i].pos_y;
	    botY = canvas->line_lst[i].max_y;

	    (void) MoveLines(canvas, layout, i, topY, botY,
					canvas->line_lst[i].max_x - maxWidth);
	  }
      }
}

/*****************************************************************************
 * Function:    static void CompareUnits ()
 *
 * Parameters:
 *
 * Returns:
 *
 * Purpose:
 * 
 *****************************************************************************/
static int
CompareUnits (
    const void  *a,
    const void  *b)
{
    _DtCvUnit *aPtr = (_DtCvUnit *) a;
    _DtCvUnit *bPtr = (_DtCvUnit *) b;

    if (*aPtr <  *bPtr) return -1;
    if (*aPtr == *bPtr) return 0;

    return 1;
}

/*****************************************************************************
 * Function:    static void CompareSearchs ()
 *
 * Parameters:
 *
 * Returns:
 *
 * Purpose:
 * 
 *****************************************************************************/
static int
CompareSearchs (
    const void  *a,
    const void  *b)
{
    _DtCvSearchData *searchA = (_DtCvSearchData *) a;
    _DtCvSearchData *searchB = (_DtCvSearchData *) b;
    _DtCvDspLine    *lineA   = &(searchA->lst[searchA->idx]);
    _DtCvDspLine    *lineB   = &(searchB->lst[searchB->idx]);
    _DtCvUnit        topA    = lineA->baseline - lineA->ascent;
    _DtCvUnit        topB    = lineB->baseline - lineB->ascent;
    _DtCvUnit        heightA = lineA->ascent   + lineA->descent;
    _DtCvUnit        heightB = lineB->ascent   + lineB->descent;
    _DtCvUnit        centA   =  topA + (heightA >> 1);
    _DtCvUnit        centB   =  topB + (heightB >> 1);

    if (lineA->baseline + lineA->descent < centB && centA < topB)
	return -1;

    if (lineB->baseline + lineB->descent < centA && centB < topA)
	return 1;

    if (lineA->text_x != lineB->text_x)
	return ((lineA->text_x < lineB->text_x) ? -1 : 1);

    if (topA != topB)
	return ((topA < topB) ? -1 : 1);

    if (heightA != heightB)
	return ((heightA < heightB) ? -1 : 1);

    if (lineA->max_x != lineB->max_x)
	return ((lineA->max_x < lineB->max_x) ? -1 : 1);

    return 0;
}

/*****************************************************************************
 * Function:    static Status LayoutCanvasInfo (_DtCvHandle canvas);
 *
 * Parameters:
 *
 * Returns:
 *
 * Purpose:
 * 
 *****************************************************************************/
static _DtCvStatus
LayoutCanvasInfo (
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    _DtCvUnit		 divisor,
    char		*target_id)
{
    int                  i = 0;
    DataPoint		 basePt;
    _DtCvUnit		 maxWidth = 0;
    _DtCvUnit		 maxXPos  = 0;
    _DtCvStatus		 result   = _DtCvSTATUS_OK;

    *layout           = DefInfo;
    layout->divisor   = divisor;
    layout->max_width = canvas->metrics.width;
    layout->left      = canvas->metrics.side_margin;
    layout->right     = canvas->metrics.side_margin;
    layout->target_id = target_id;

    _DtCvInitLayoutInfo(canvas, &(layout->info));

    basePt = DefDataPt;
    PushDataPoint(layout, &basePt);
    SetMargins (layout);
    SetTextPosition (layout, True);

    ProcessSegmentList(canvas, layout, canvas->element_lst, -1,
						&maxWidth, &maxXPos, NULL);

    RemoveDataPoint(layout, &basePt);

    /*
     * fill in the max_x of each line of text/regions.
     */
    for (i = 0; i < canvas->txt_cnt; i++)
        canvas->txt_lst[i].max_x = MaxXOfLine(canvas, &(canvas->txt_lst[i]));

    /*
     * calculate the actual right hand side boundary.
     */
    layout->info.max_x_pos += canvas->metrics.side_margin;

    /*
     * the max_x_pos so far has indicated where the *next* character,
     * line or region will be *started*. Therefore back up one to
     * indicate the true last position used.
     */
    layout->info.max_x_pos--;

    return result;

}  /* End LayoutCanvasInfo */

/*****************************************************************************
 * Function:    static Status LayoutCanvas (_DtCvHandle canvas);
 *
 * Parameters:
 *
 * Returns:
 *
 * Purpose:
 * 
 *****************************************************************************/
static _DtCvStatus
LayoutCanvas (
    _DtCanvasStruct	*canvas,
    LayoutInfo		*layout,
    char		*target_id)
{
    _DtCvUnit		 divisor = 1;
    _DtCvValue		 redo;
    _DtCvStatus		 result;

    int i, search_cnt = canvas->search_cnt;
    
    do {
	redo = False;
        result = LayoutCanvasInfo(canvas, layout, divisor, target_id);

        /*
         * Are we suppose to honor the boundary?
	 * If so, do any lines go over the boundary?
	 * Is there any margins that can be decremented?
         */
	if (_DtCvSTATUS_OK == result
		&& (_DtCvUSE_BOUNDARY == canvas->constraint ||
			_DtCvUSE_BOUNDARY_MOVE == canvas->constraint)
		&& layout->info.max_x_pos >= canvas->metrics.width)
          {
	    if (_DtCvUSE_BOUNDARY_MOVE == canvas->constraint)
	      {
		int i;

		/*
		 * clear the processed flag from all the text/region lines.
		 */
		for (i = 0; i < canvas->txt_cnt; i++)
		   _DtCvClearProcessed(canvas->txt_lst[i]);

		/*
		 * clear the processed flag from all the line/rules.
		 */
		for (i = 0; i < canvas->line_cnt; i++)
		   _DtCvClearProcessed(canvas->line_lst[i]);

		CheckMoveInfo(canvas, layout);

		/*
		 * recalculate the new max x
		 */
		layout->info.max_x_pos = 0;
		for (i = 0; i < canvas->txt_cnt; i++)
		    if (layout->info.max_x_pos < canvas->txt_lst[i].max_x)
		        layout->info.max_x_pos = canvas->txt_lst[i].max_x;

		for (i = 0; i < canvas->line_cnt; i++)
		    if (layout->info.max_x_pos < canvas->line_lst[i].max_x)
		        layout->info.max_x_pos = canvas->line_lst[i].max_x;

		layout->info.max_x_pos--;
	      }
	    else if (True == layout->margin_non_zero)
	      {
	        redo = True;
	        divisor *= 2;
	        canvas->txt_cnt  = 0;
	        canvas->line_cnt = 0;
	        canvas->trav_cnt = 0;
              }
          }

      } while (True == redo);

    /*
     * clean up table information
     */
    if (NULL != layout->grp_lst)
	free(layout->grp_lst);

    /*
     * subtract one from the y position to indicate the *last*
     * pixel/column/etc that will be rendered.
     */
    canvas->max_y = layout->info.y_pos - 1;
    canvas->max_x = layout->info.max_x_pos;

    for (i = search_cnt; i < canvas->search_cnt; i++)
      {
	canvas->searchs[i - search_cnt]     = canvas->searchs[i];
	canvas->searchs[i - search_cnt].lst = canvas->txt_lst;
      }

    canvas->search_cnt -= search_cnt;

    /*
     * are there any search hits?
     */
    if (0 != canvas->search_cnt)
	qsort (canvas->searchs, canvas->search_cnt, sizeof(_DtCvSearchData),
							CompareSearchs);
    /*
     * sort the page break list.
     */
    if (0 != canvas->brk_cnt)
	qsort (canvas->pg_breaks, canvas->brk_cnt, sizeof(_DtCvUnit),
								CompareUnits);

    return result;

}  /* End LayoutCanvas */

/*****************************************************************************
 * Function:    static void SortTraversal (_DtCvHandle canvas);
 *
 * Parameters:
 *
 * Returns:
 *
 * Purpose:
 * 
 *****************************************************************************/
static void
SortTraversal (_DtCanvasStruct *canvas)
{
    int i;

    /*
     * sort the links correctly. First, establish the x,y,width,height
     * of each link.
     */
    for (i = 0; i < canvas->trav_cnt; i++)
      {
	if (_DtCvTraversalLink == canvas->trav_lst[i].type)
	    GetLinkInfo(canvas, i, &(canvas->trav_lst[i].x_pos),
					&(canvas->trav_lst[i].y_pos),
					&(canvas->trav_lst[i].width),
					&(canvas->trav_lst[i].height));
	else
	    _DtCvCalcMarkPos(canvas, canvas->trav_lst[i].idx,
					&(canvas->trav_lst[i].x_pos),
					&(canvas->trav_lst[i].y_pos),
					&(canvas->trav_lst[i].width),
					&(canvas->trav_lst[i].height));
      }

    _DtCvSortTraversalList(canvas, _DtCvFALSE);
}

/*****************************************************************************
 * Function:    static void ProcessMarks (_DtCanvasStruct *canvas);
 *
 * Parameters:
 *
 * Returns:
 *
 * Purpose:
 * 
 *****************************************************************************/
static _DtCvStatus
ProcessMarks (
    _DtCanvasStruct	 *canvas,
    _DtCvPointInfo	**mark_lst)
{
    int			markIdx;
    int			result = _DtCvSTATUS_OK;
    _DtCvSelectData	beg;
    _DtCvSelectData	end;
    _DtCvSegmentI	*firstSeg;

    while (NULL != mark_lst && NULL != *mark_lst)
      {
	/*
	 * convert the segments to begin and end points
	 */
	if (_DtCvSTATUS_BAD == _DtCvCvtSegsToPts(canvas, (*mark_lst)->segs,
					&beg, &end, NULL, NULL, &firstSeg))
	    /*
	     * just set a return value since this indicates bad data
	     * and not system failure.
	     */
	    result = _DtCvSTATUS_BAD;

	/*
	 * now add it to the mark list
	 */
	else
	  {
	    markIdx =  _DtCvAddToMarkList(canvas, (*mark_lst)->client_data,
						_DtCvFALSE, &beg, &end);
	    /*
	     * now put the mark in the traversal list, but don't sort
	     * or fill out position and dimension information.
	     * SortTraversal() will do that.
	     *
	     * bail here if system failure indicated.
	     */
	    if (-1 == markIdx || 0 != _DtCvSetTravEntryInfo(canvas,
						_DtCvGetNextTravEntry(canvas),
						_DtCvTraversalMark, firstSeg,
						markIdx, _DtCvTRUE))
	        return _DtCvSTATUS_BAD;
	  }

	mark_lst++;
      }

    return result;
}

/*****************************************************************************
 *              Public Functions
 *****************************************************************************/
/*****************************************************************************
 * Function:    void _DtCanvasResize (_DtCvHandle canvas);
 *
 * Parameters:
 *
 * Returns:
 *
 * Purpose:
 * 
 *****************************************************************************/
_DtCvStatus
_DtCanvasResize (
    _DtCvHandle		 canvas_handle,
    _DtCvValue		 force,
    _DtCvUnit		*ret_width,
    _DtCvUnit		*ret_height )
{
    int			 i;
    _DtCvStatus		 selectStatus;
    _DtCvStatus		 retStatus = _DtCvSTATUS_NONE;
    _DtCanvasStruct	*canvas    = (_DtCanvasStruct *) canvas_handle;
    _DtCvUnit		 oldWidth  = canvas->metrics.width;
    LayoutInfo		 layOut;
    _DtCvPointInfo       selPt;
    _DtCvPointInfo	**markInfo;

    selPt.client_data = NULL;
    selPt.segs        = NULL;

    /*
     * check to see if the width has changed - if not,
     * don't do anything (but re-initialize the metrics
     * to get the new height).
     */
    (*(canvas->virt_functions.get_metrics))(canvas->client_data,
				_DtCvCANVAS_TYPE, &(canvas->metrics));

    if (canvas->metrics.width != oldWidth || _DtCvTRUE == force)
      {
        /*
         * remember the current selection.
         */
        selectStatus = _DtCanvasGetSelectionPoints(canvas, &(selPt.segs),
								NULL, NULL);
        /*
         * remember the marks
         */
        if (_DtCvSTATUS_BAD == _DtCvGetMarkSegs(canvas, &markInfo))
	    return _DtCvSTATUS_BAD;

        /*
         * Re-Layout the information.
         * First step - invalidate some counters.
         */
        canvas->trav_cnt = 0;	/* zero this only because we re-process */
				/* do not zero cur_hyper or we'll loose */
				/* where we are in the TOC              */
        canvas->txt_cnt  = 0;
        canvas->line_cnt = 0;
        canvas->mark_cnt = 0;
        canvas->brk_cnt  = 0;

        /*
         * Layout the information if there is anything to do
         */
        if (_DtCvSTATUS_BAD == LayoutCanvas (canvas, &layOut, NULL))
	    return _DtCvSTATUS_BAD;
    
        /*
         * restore the current selection.
         */
        if (_DtCvSTATUS_OK == selectStatus)
          {
	    _DtCanvasActivatePts(canvas,_DtCvACTIVATE_SELECTION, &selPt,
								NULL,NULL);
	    _DtCvFreeArray((void **) selPt.segs);
          }

        /*
         * now place the marks in the mark and traversal lists
         */
        ProcessMarks(canvas, markInfo);
        if (NULL != markInfo)
	  {
	    for (i = 0; NULL != markInfo[i]; i++)
		_DtCvFreeArray((void **) (markInfo[i]->segs));
	    _DtCvFreeArray((void **) markInfo);
	  }

        /*
         * sort the traversal list.
         */
        SortTraversal(canvas);
	retStatus = _DtCvSTATUS_OK;
      }

    /*
     * return the maximum height and width used
     */
    if (ret_width != NULL)
        *ret_width  = canvas->max_x;
    if (ret_height != NULL)
        *ret_height = canvas->max_y;

    return retStatus;

}  /* End _DtCanvasResize */

/*****************************************************************************
 * Function:    void _DtCanvasSetTopic (_DtCvHandle canvas);
 *
 * Parameters:
 *
 * Returns:
 *
 * Purpose:
 * 
 *****************************************************************************/
_DtCvStatus
_DtCanvasSetTopic (
    _DtCvHandle		 canvas_handle,
    _DtCvTopicPtr	 topic,
    _DtCvValue		 honor_size,
    _DtCvUnit		*ret_width,
    _DtCvUnit		*ret_height,
    _DtCvUnit		*ret_y )
{
    _DtCvStatus		 result = _DtCvSTATUS_OK;
    _DtCanvasStruct	*canvas = (_DtCanvasStruct *) canvas_handle;
    LayoutInfo		layOut;

    /*
     * clean the canvas
     */
    _DtCanvasClean (canvas_handle);

    /*
     * attach to the canvas
     */
    canvas->element_lst = topic->seg_list;

    /*
     * Attach the link information
     */
    canvas->link_data = topic->link_data;

    /*
     * init the internal use pointer in all containers to NULL
     */
    _DtCvClearInternalUse(canvas->element_lst, _DtCvFALSE);

    /*
     * Layout the information if there is anything to do
     */
    canvas->constraint = honor_size;
    if (_DtCvSTATUS_BAD == LayoutCanvas (canvas, &layOut, topic->id_str))
	return _DtCvSTATUS_BAD;
    
    /*
     * add the marks to the mark and traversal lists
     */
    ProcessMarks(canvas, topic->mark_list);

    /*
     * sort the traversal list.
     */
    SortTraversal(canvas);

    /*
     * return the maximum height and width used
     * And the location of the id.
     */
    if (ret_width != NULL)
        *ret_width = canvas->max_x;
    if (ret_height != NULL)
        *ret_height = canvas->max_y;
    if (ret_y != NULL)
      {
	if (NULL != layOut.target_id && True != layOut.id_found)
	    result = _DtCvSTATUS_ID_BAD;
        *ret_y = layOut.id_Ypos;
      }

    return result;

}  /* End _DtCanvasSetTopic */
