/*
 * 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: LayoutUtil.c /main/26 1996/11/06 12:25:09 cde-hp $ */
/************************************<+>*************************************
 ****************************************************************************
 **
 **   File:       LayoutUtil.c
 **
 **   Project:    Cde DtHelp
 **
 **   Description:
 **
 **  (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 <string.h>
#include <limits.h>

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

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

/******************************************************************************
 *
 * Private Defines
 *
 *****************************************************************************/
#define	GROW_SIZE	10
#define	CheckFormat(x) \
	(((x)->format_y == -1 || (x)->format_y > (x)->y_pos) ? False : True)

/******************************************************************************
 *
 * Private Variables
 *
 *****************************************************************************/
static  char *OneByteCantBeginList = "])}`\"\'.,;?:!";
static  char *OneByteCantEndList   = "[({`\"";

static	_DtCvLayoutInfo	DefLayInfo =
    {
	NULL, 		/* _DtCvSegmentI *line_seg;		*/
	 0,		/* unsigned int	 line_start;		*/
	 0,		/* unsigned int	 line_bytes;		*/
	 0,		/* _DtCvUnit	 cur_len;		*/
	 0,		/* _DtCvUnit	 max_x_pos;		*/
	 0,		/* _DtCvUnit	 cur_max_x;		*/
	 0,		/* _DtCvUnit	 y_pos;			*/
	 0,		/* _DtCvUnit	 text_x_pos;		*/
	 0,		/* _DtCvUnit	 leading;		*/
	-1,		/* int		 lst_hyper;		*/
	_CEFORMAT_ALL,	/* int		 format_y;		*/
	-1,		/* int		 join_line;		*/
	FALSE,		/* _DtCvValue	 lst_vis;		*/
	FALSE,		/* _DtCvValue	 join;			*/
	FALSE,		/* _DtCvValue	 align_flag;		*/
	NULL,		/* const char	*align_char;		*/
	-1,		/* _DtCvUnit	 align_pos;		*/
	0,		/* int		 delayed_search_saves	*/
    };

static const _DtCvSelectData DefSelectData = { -1, -1, -1, -1 };
static const _DtCvTraversalInfo DefTravData =
  {
    _DtCvFALSE		/* active   */,
    _DtCvTraversalNone	/* type     */,
    -1			/* idx      */,
    0			/* x_pos    */,
    0			/* y_pos    */,
    0			/* width    */,
    0			/* height   */,
    NULL		/* *seg_ptr */
  };

/******************************************************************************
 *
 * Private Functions
 *
 *****************************************************************************/
/******************************************************************************
 * Function:	IsTrueMultiByte
 *
 * Returns:	True	if the character is a multibyte character
 *		False	if the character is a single byte character.
 *****************************************************************************/
static _DtCvValue
IsTrueMultiByte (wchar_t wc_char)
{
    char buf[MB_LEN_MAX];

    /*
     * check to see if this is a one byte character
     * There might not be a multibyte list for this locale.
     * Can't break on single byte characters.
     */
    if (1 != wctomb(buf, wc_char))
	return True;

    return False;
}

/******************************************************************************
 * Function:	CheckList
 *
 * Returns:	True	if the character matches one of the characters in
 *			the MultiCantEndList.
 *		False	if the character does not match an item in
 *			the MultiCantEndList.
 *****************************************************************************/
static _DtCvValue
CheckList (
    wchar_t		 wc_char,
    const wchar_t	*list)
{
    /*
     * check the multibyte list for the character
     */
    if (list != NULL)
      {
        while ('\0' != *list)
	  {
	    /*
	     * it matches, return true
	     */
	    if (*list == wc_char)
	        return True;
	    list++;
          }
      }

    return False;
}

/*****************************************************************************
 * Function:    static int CompareTraversalPos (_DtCvHandle canvas);
 *
 * Parameters:
 *
 * Returns:
 *
 * Purpose:
 *
 *****************************************************************************/
static int
CompareTraversalPos (
    const void  *a,
    const void  *b)
{
    _DtCvTraversalInfo *linkA = (_DtCvTraversalInfo *) a;
    _DtCvTraversalInfo *linkB = (_DtCvTraversalInfo *) b;
    _DtCvUnit           centA = linkA->y_pos + (linkA->height >> 1);
    _DtCvUnit           centB = linkB->y_pos + (linkB->height >> 1);

    if (linkA->y_pos + linkA->height < centB && centA < linkB->y_pos)
        return -1;

    if (linkB->y_pos + linkB->height < centA && centB < linkA->y_pos)
        return 1;

    if (linkA->x_pos != linkB->x_pos)
        return ((linkA->x_pos < linkB->x_pos) ? -1 : 1);

    if (linkA->y_pos != linkB->y_pos)
        return ((linkA->y_pos < linkB->y_pos) ? -1 : 1);

    if (linkA->height != linkB->height)
        return ((linkA->height < linkB->height) ? -1 : 1);

    if (linkA->width != linkB->width)
        return ((linkA->width < linkB->width) ? -1 : 1);

    return 0;
}

/******************************************************************************
 *
 * Private Layout Utility Functions
 *
 *****************************************************************************/
/******************************************************************************
 * Function:    void _DtCvInitLayoutInfo ()
 *
 * Parameters:
 *
 * Returns:     Nothing.
 *
 *****************************************************************************/
void
_DtCvInitLayoutInfo (
    _DtCanvasStruct      *canvas,
    _DtCvLayoutInfo	*layout)
{
   *layout = DefLayInfo;

   layout->y_pos = canvas->metrics.top_margin;
}

/******************************************************************************
 * Function:    int _DtCvGetTraversalWidth ()
 *
 * Parameters:
 *
 * Returns:     The total amount of space to add before and after the
 *		segment to take into account traversal/link metrics on
 *		this segment including any necessary to 'close' out the
 *		link on the previous segment.
 *
 *****************************************************************************/
int
_DtCvGetTraversalWidth (
    _DtCanvasStruct      *canvas,
    _DtCvSegmentI        *p_seg,
    int			 lst_hyper)
{
    int  value = 0;
    int  lnkBefore = 0;
    int  lnkAfter  = 0;

    /*
     * does this segment have a different link than the previous one?
     */
    if (lst_hyper != p_seg->link_idx)
      {
	/*
	 * is the link visible?
	 */
        if (_DtCvIsSegVisibleLink(p_seg))
	  {
	    /*
	     * get the visible link metrics
	     */
	    lnkBefore = canvas->link_info.space_before;
	    lnkAfter  = canvas->link_info.space_after;
	  }
        if (_DtCvIsSegALink(p_seg))
          {
	    /*
	     * if the last 'link' was really a link, close it out by
	     * leaving room for the traversal and link end indicators
	     */
            if (lst_hyper != -1)
                value += (canvas->traversal_info.space_after + lnkAfter);

	    /*
	     * leave space for the traversal/link begin and end
	     * indicators for this segment.
	     */
            value += (canvas->traversal_info.space_before
				+ canvas->traversal_info.space_after
				+ lnkBefore
				+ lnkAfter);
          }
      }

    return value;
}

/******************************************************************************
 * Function: _DtCvAddLines
 *
 * makes sure the last x number of lines are blank.
 *****************************************************************************/
void
_DtCvAddSpace (
    _DtCvUnit		 number,
    _DtCvUnit		*ret_y)
{
    /*
     * anything to do?
     */
    if (0 >= number)
        return;

    /*
     * adjust the global Y position to allow the extra room
     */
    *ret_y = *ret_y + number;
}

/******************************************************************************
 * Function:	CheckOneByteCantEndList
 *
 * Returns:	True	if the character matches one of the characters in
 *			the OneByteCantEndList.
 *		False	if the character does not match an item in
 *			the OneByteCantEndList.
 *****************************************************************************/
_DtCvValue
_DtCvCheckOneByteCantEndList (
    char	 c,
    char	*cant_end_list)
{
    int  i;

    for (i = 0; cant_end_list[i]; i++)
	if (cant_end_list[i] == c)
	    return True;

    return False;
}

/******************************************************************************
 * Function:	CheckOneByteCantBeginList
 *
 * Returns:	True	if the character matches one of the characters in
 *			the OneByteCantBeginList.
 *		False	if the character does not match an item in
 *			the OneByteCantBeginList.
 *****************************************************************************/
_DtCvValue
_DtCvCheckOneByteCantBeginList (
    char	 c,
    char	*cant_begin_list)
{
    int  i;

    for (i = 0; cant_begin_list[i]; i++)
	if (cant_begin_list[i] == c)
	    return True;

    return False;
}

/******************************************************************************
 * Function:	CheckLineSyntax
 *
 * Returns:	True	if the segment can end a line.
 *		False	if the segment can not end a line.
 *
 * Purpose:	Checks the line syntax. Will not allow a segment to end
 *		a line if:
 *			the segment does not end with a hypen.
 *			the segment does not end with a space and the
 *				next segment does not begin with a space.
 *			the segment ends with a two byte characters that
 *				can not end a line.
 *			The next segment starts with a two byte character
 *				that can not begin a line.
 *			the segment ends with an one-byte open type and
 *				the next segment starts with a
 *				two byte character.
 *			the segment ends with a two byte character and
 *				the next segment starts with a one-byte
 *				close type.
 *			the next segment is a non-breaking string or region.
 *
 *****************************************************************************/
_DtCvValue
_DtCvCheckLineSyntax (
    _DtCanvasStruct	 *canvas,
    _DtCvSegmentI	 *pSeg,
    int			  start,
    int			  str_len,
    _DtCvValue		  skip_hypen_ck)
{
    int     myStrLen = 0;
    int     wcFlag = 0;
    void    *pChar = NULL;
    wchar_t  nextChar;
    wchar_t  lastChar = 0;
    _DtCvValue lstCharMb = False;
    _DtCvValue nxtCharMb = False;

    /*
     * while this is a marker or a noop without a end-of-line, go to the
     * next segment.
     */
    while (NULL != pSeg && (_DtCvIsSegMarker(pSeg) ||
				(_DtCvIsSegNoop(pSeg) && !_DtCvIsSegNewLine(pSeg))))
	pSeg = pSeg->next_seg;

    /*
     * if this segment is null or not a string or region, stop the
     * test right now.
     */
    if (pSeg == NULL || !(_DtCvIsSegString(pSeg) || _DtCvIsSegRegion(pSeg)))
        return True;

    /*
     * Get the string segment stats
     */
    if (_DtCvIsSegString(pSeg))
      {
        wcFlag   = _DtCvIsSegWideChar(pSeg);
        pChar    = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg), wcFlag, start);
        myStrLen = _DtCvStrLen (pChar, wcFlag);
      }

    /*
     * if this is a region or a string segment (at the end of its string)
     * and it has a newline on it, then it can end a line.
     */
    if ((_DtCvIsSegRegion(pSeg) ||
			(_DtCvIsSegString(pSeg) && myStrLen == str_len))
		&& (_DtCvIsSegNewLine (pSeg) || pSeg->next_seg == NULL))
        return True;

    /*
     * if this is a region, then check it's breaking flag.
     */
    if (_DtCvIsSegRegion(pSeg))
      {
	if (_DtCvIsSegNonBreakingChar(pSeg))
	    return False;
	return True;
      }

    /*
     * so, to get this far, this is a string segment.
     *
     * Problems with indexing?
     */
    if (str_len <= 0)
	return True;

    /*
     * do we need to check the last character in the string?
     * If skip_hypen_ck is true, it means that 'lastChar' would be a hypen.
     */
    if (False == skip_hypen_ck)
      {
        /*
         * this region is a string, get its string information.
         */
        lastChar = _DtCvChar(pChar, wcFlag, str_len - 1);

        /*
         * check to make sure the last character is a valid last
         * character.
         */
        if (' ' == lastChar || '-' == lastChar)
	    return True;

        /*
	 * If this string is a multi-byte, check the list of multi-bytes
	 * that can't end a line.  If one is found it can't end a line.
         */
        if ((wcFlag || canvas->mb_length > 1) &&
		CheckList(lastChar, canvas->locale.cant_end_chars) == True)
	    return False;

	/*
	 * so at the end of these tests, the last character is
	 *    -) not a blank.
	 *    -) not a hypen.
	 *    -) either a single byte character or multibyte character
	 *       that can end the line (including a single byte in
	 *       wide char form).
	 *
	 * set the flag for the type of character lastChar is.
	 * if skip_hypen_ck was True, then lstCharMb remains False
	 * which is logical since it means that the caller has already
	 * processed a hypen (a single byte character).
	 */
	lstCharMb = IsTrueMultiByte(lastChar);
      }

    /*
     * Check for more characters in the string and
     * check its next character for breakable space.
     */
    if (myStrLen > str_len)
      {
	/*
	 * go to the next character.
	 */
	nextChar = _DtCvChar(pChar, wcFlag, str_len);

	/*
	 * Is it a valid break point?
	 */
	if (' ' == nextChar)
	    return True;

	/*
	 * set the multibyte flag for the next character
	 */
	nxtCharMb = IsTrueMultiByte(nextChar);

	/*
	 * If this is wide char string, check the list of multi-byte
	 * that can't begin a line.
	 * 
	 * But only if the last character wasn't a hypen!  Otherwise
	 * it's a character after a hypen and should not be broken on.
	 * 
	 * if this character is in the 'cannot begin line' list, then it
	 * can't be broken on (a return value of true).  (A wide char
	 * encoding of a single byte character should come back as False
	 * as long as the character is not in the list.)
	 *
	 * Have to double check to make sure it is a multibyte
	 * character (want it to go through the CheckMulti list just in
	 * case it's specified in there, before eliminating it).
	 */
	if (False == skip_hypen_ck && (wcFlag || canvas->mb_length > 1)
		&& CheckList(nextChar,canvas->locale.cant_begin_chars) == False
		&& True == nxtCharMb)
	    return True;

	/*
	 * either the character is after a hypen (starting a line) OR it
	 * is a multibyte character in the 'cannot begin a line' list OR
	 * it is a single byte character.  Therefore, this is a
	 * nonbreakable character.
	 */
	return False;
      }

    /*
     * We were at the last character of the string.
     * go to the next segment and see if it can start a new line.
     */
    do
      {
        pSeg = pSeg->next_seg;
      } while (pSeg != NULL && (_DtCvIsSegMarker(pSeg) ||
			(_DtCvIsSegNoop (pSeg) && !(_DtCvIsSegNewLine(pSeg)))));
    /*
     * If there isn't another valid segment, then the original segment
     * can end the line.
     */
    if (pSeg == NULL || !(_DtCvIsSegString(pSeg) || _DtCvIsSegRegion(pSeg)))
        return True;

    /*
     * if the last if fell through, then pSeg is a string or region.
     * check to see if you can break on it.
     */
    if (_DtCvIsSegNonBreakingChar(pSeg))
        return False;

    /*
     * if the last if fell through, then this is a breaking string or
     * region. Therefore, if a region, you can break on it.
     */
    if (_DtCvIsSegRegion(pSeg))
        return True;

    /*
     * To get this far, the next segment must be a string.  Check the
     * first character of the string to see if it can start a new line.
     */
    nextChar = _DtCvChar(_DtCvStringOfStringSeg(pSeg),
						_DtCvIsSegWideChar(pSeg), 0);
    if (' ' == nextChar)
            return True;

    /*
     * If the previous character was a single byte character (or a hypen),
     * it couldn't end a line. If this is a single byte string, then
     * this string can't start a line......
     */
    if (_DtCvIsSegRegChar(pSeg) &&
				(True == skip_hypen_ck || False == lstCharMb))
        return False;

    /*
     * If this is multi-byte, check the list of multi-byte
     * that can't begin a line.
     */
    if (_DtCvIsSegWideChar(pSeg) || canvas->mb_length > 1)
      {
	/*
	 * plus checking the 'can not begin a line' list, check
	 * if the previous character was a hypen, then this can't be
	 * broken on either.
	 */
        if (True == skip_hypen_ck ||
		CheckList(nextChar, canvas->locale.cant_begin_chars) == True)
            return False;

        /*
         * if the previous character was a multi-byte and this
	 * character is a multibyte, then it is a valid break.
         */
	nxtCharMb = IsTrueMultiByte(nextChar);
        if (True == lstCharMb && True == nxtCharMb)
            return True;
      }

    /*
     * if the last character was a single byte character, then there
     * is still more to check - 1 byte punctuation around multi-byte.
     */
    if (False == lstCharMb &&
	_DtCvCheckOneByteCantEndList((char)lastChar,OneByteCantEndList) == True)
        return False;
    
    /*
     * or was the last character a multibyte and is followed by single byte
     * punctuation?
     */
    if (True == lstCharMb && False == nxtCharMb &&
	_DtCvCheckOneByteCantBeginList((char)nextChar, OneByteCantBeginList)
									== True)
        return False;

    return True;
}

/******************************************************************************
 * Function:	_DtCvGetNextWidth
 *
 * Purpose:	Determines the width of the next legal segment.
 *
 * Returns:	The width of the next legal segment.
 *
 *****************************************************************************/
int
_DtCvGetNextWidth (
    _DtCanvasStruct	 *canvas,
    int			  old_type,
    int			  lst_hyper,
    _DtCvSegmentI	 *pSeg,
    int			  start,
    _DtCvSegmentI	 *prev_seg,
    _DtCvSegmentI	**nextSeg,
    int			 *nextStart,
    int			 *widthCount)
{
    int      result;
    int      len = 0;
    int      mbl;
    int      tLen;
    int      wcFlag;
    int      curWidth;
    int      myLength;
    int      nextLen = 0;
    void    *pChar;
    char    *tChar;
    _DtCvValue  good_len;

    /*
     * pass over noops that don't have newlines and markers
     */
    while (pSeg != NULL && (_DtCvIsSegMarker(pSeg) ||
			(_DtCvIsSegNoop (pSeg) && !(_DtCvIsSegNewLine(pSeg)))))
      {
	pSeg = pSeg->next_seg;
	start = 0;
      }

    if (nextSeg != NULL)
        *nextSeg = pSeg;
    if (nextStart != NULL)
        *nextStart = start;

    /*
     * if the next segment is null or anything else but a string or region;
     * return that there is no more after this segment.
     */
    if (pSeg == NULL || !(_DtCvIsSegString(pSeg) || _DtCvIsSegRegion(pSeg)))
	return 0;

    /*
     * this segment is a region or string
     * check for region...anything left is a string.
     */
    if (_DtCvIsSegRegion(pSeg))
      {
	/*
	 * can I break on this region
	 */
	if (_DtCvIsSegNonBreakingChar(pSeg))
	  {
	    /*
	     * no...set the lengths and continue
	     */
	    len      = 1;
	    curWidth = _DtCvWidthOfRegionSeg(pSeg);
	  }
	else
	    return 0;
      }
    /*
     * is this a non breaking string?
     */
    else if (_DtCvIsSegNonBreakingChar(pSeg))
      {
	pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg),
					_DtCvIsSegWideChar(pSeg), start);
	len   = _DtCvStrLen (pChar, _DtCvIsSegWideChar(pSeg));

	curWidth = _DtCvGetStringWidth (canvas, pSeg, pChar, len)
			+ _DtCvGetTraversalWidth(canvas, pSeg, lst_hyper);
      }
    /*
     * so this is a string with possible breaks in it.
     */
    else
      {
	/*
	 * get the string stats
	 */
	wcFlag   = _DtCvIsSegWideChar (pSeg);
	pChar    = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg), wcFlag, start);
	myLength = _DtCvStrLen (pChar, wcFlag);
    
	    /*
	 * if a single byte string, zoom through it looking for
	 * specific breaking characters.
	     */
	if (0 == wcFlag && canvas->mb_length == 1)
	  {
	    tChar = pChar;
	    len = 0;
	    do
	      {
		/*
		 * checking for a hypen or space
		 */
		good_len = True;
		result = _DtCvStrcspn ((void *) tChar, " -", 0, &tLen);
		len += tLen;
    
		/*
		 * check for '-'. Some of the possible combinations are:
		 *    -text
		 *    - text
		 *    -/text/
		 *    text/-text/
		 *    text-text
		 *    text text
		 *
		 * if it is the first character to check and there is no
		 * previous segment, then it is starting a line and can
		 * not be broken on.
		 *
		 * _DtCvStrcpn return 0 if one of the characters in the
		 * test string was found.
		 */
		if (0 == result && '-' == tChar[tLen] && 0 == len &&
		    NULL == prev_seg &&
		    _DtCvCheckLineSyntax(canvas,pSeg,start,1,True) == False)
		  {
		    len++;
		    tLen++;
		    tChar += tLen;
		    good_len = False;
		  }
	      } while (!good_len);
    
	    /*
	     * found either a space or a hypen or null byte.
	     * If we found a hypen, include it.
	     */
	    if ('-' == *tChar)
		len++;
    
	    curWidth = _DtCvGetStringWidth (canvas, pSeg, pChar, len)
			+ _DtCvGetTraversalWidth(canvas, pSeg, lst_hyper);
    
	    /*
	     * Did we find a space or hypen?
	     * If not, can this segment stand alone?
	     */
	    if (result == 0 ||
		    _DtCvCheckLineSyntax(canvas,pSeg,start,len,False) == True)
	      {
		if (nextSeg != NULL)
		    *nextSeg   = pSeg;
		if (nextStart != NULL)
		    *nextStart = start + len;
		if (widthCount != NULL)
		    *widthCount = len;
		 return curWidth;
	      }
	  }
	/*
	 * multibyte (wide char string), look for a break the hard way.
	 */
	else
	  {
	    len = 0;
	    while (len < myLength)
	      {
		if (wcFlag) len++;
		else
		  {
		    mbl = mblen(pChar + len, MB_CUR_MAX);

		    if (mbl == -1)
		      {
			++len;
			continue;
		      }
		    else if (!mbl) break;
		    else len += mbl;
		  }

		if (_DtCvCheckLineSyntax(canvas,pSeg,start,len,False) == True)
		  {
		    pChar    = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg),
					_DtCvIsSegWideChar(pSeg), start);
		    curWidth = _DtCvGetStringWidth(canvas,pSeg,pChar,len)
			    + _DtCvGetTraversalWidth(canvas,pSeg,lst_hyper);
    
		    if (nextSeg != NULL)
		        *nextSeg    = pSeg;
		    if (nextStart != NULL)
		        *nextStart  = start + len;
		    if (widthCount != NULL)
		        *widthCount = len;
		    return curWidth;
		  }
	      }
    
	    /*
	     * Didn't find a smaller segment that satisfied the requirements.
	     * Determine the length of the current segment.
	     */
	    curWidth = _DtCvGetStringWidth (canvas, pSeg, pChar, len)
				    + _DtCvGetTraversalWidth(canvas, pSeg,
							lst_hyper);
	  }
      }

    /*
     * sigh...need to go further...this segment can't end a line
     * either.
     */
    prev_seg = pSeg;
    pSeg = pSeg->next_seg;
    if (pSeg != NULL)
      {
        start = 0;
        curWidth += _DtCvGetNextWidth (canvas, 
			_DtCvPrimaryTypeOfSeg (prev_seg), lst_hyper,
				pSeg, start, prev_seg,
				nextSeg, nextStart, &nextLen);

      }

    if (widthCount != NULL)
        *widthCount = len + nextLen;
    return (curWidth);
}

/******************************************************************************
 * Function: _DtCvSaveInfo
 *
 * Initializes a line table element to the segment it should display.
 *****************************************************************************/
void
_DtCvSaveInfo (
    _DtCanvasStruct	*canvas,
    _DtCvLayoutInfo	*layout,
    _DtCvUnit		 max_width,
    _DtCvUnit		 r_margin,
    _DtCvFrmtOption	 txt_justify)
{
    /*****************************************************************
     * The ascent for a line is described as the number of units
     * above the baseline.
     *
     * The descent for a line is described as the number of units
     * below the baseline.
     * 
     * Neither the ascent or decent value includes the baseline
     ****************************************************************/
    int    len;
    int    start      = layout->line_start;
    int    count      = layout->line_bytes;
    long   txtCnt     = canvas->txt_cnt;
    _DtCvUnit   maxAscent  = 0;
    _DtCvUnit   maxDescent = 0;
    _DtCvUnit   maxRegion  = 0;
    _DtCvUnit   superY     = 0;
    _DtCvUnit   subY       = 0;
    _DtCvUnit   fontAscent;
    _DtCvUnit   fontDescent;
    _DtCvValue fndLnk = False;
    _DtCvValue visLnk = False;

    void  *pChar;

    _DtCvSegmentI       *pSeg = layout->line_seg;

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

    while (pSeg != NULL && count > 0)
      {
	/*
	 * set which line will this segment sit on, iff this is the
	 * first access to the segment.
	 */
	if ((void *) -1 == pSeg->internal_use)
	    pSeg->internal_use = (void *) txtCnt;
	
	/*
	 * now get the segment's sizing so we can determine
	 * the height and depth of the line.
	 */
	len = 1;
        fontAscent  = 0;
        fontDescent = 0;

	if (_DtCvIsSegVisibleLink(pSeg))
	    visLnk = True;

	if (_DtCvIsSegALink(pSeg))
	    fndLnk = True;

	/*
	 * get the ascent and descent of the segment along with a length
	 */
	if (_DtCvIsSegString(pSeg))
	  {
	    _DtCvFontMetrics(canvas,_DtCvFontOfStringSeg(pSeg),
			&fontAscent, &fontDescent, NULL, NULL, NULL);

	    pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg),
					_DtCvIsSegWideChar(pSeg), start);
	    len   = _DtCvStrLen (pChar, _DtCvIsSegWideChar(pSeg));

	    if (len > count)
		len = count;
	  }
	else if (_DtCvIsSegRegion(pSeg))
	  {
	    if (-1 == _DtCvAscentOfRegionSeg(pSeg))
	      {
		if (maxRegion < _DtCvHeightOfRegionSeg(pSeg))
		    maxRegion = _DtCvHeightOfRegionSeg(pSeg);
	      }
	    else
	      {
		fontAscent  = _DtCvAscentOfRegionSeg(pSeg);
		fontDescent = _DtCvHeightOfRegionSeg(pSeg) - fontAscent;
	      }
	  }

	/*
	 * adjust the ascent and descent values by their subscript
	 * or superscript adjustments.
	 */
	if (_DtCvIsSegSuperScript(pSeg))
	  {
	    fontAscent  += superY;
	    fontDescent -= superY;

	    if (_DtCvIsSegRegion(pSeg) && -1 == _DtCvAscentOfRegionSeg(pSeg)
		&& maxRegion < _DtCvHeightOfRegionSeg(pSeg) + superY)
		maxRegion = _DtCvHeightOfRegionSeg(pSeg) + superY;
	  }
	else if (_DtCvIsSegSubScript(pSeg))
	  {
	    fontAscent  -= subY;
	    fontDescent += subY;
	    if (_DtCvIsSegRegion(pSeg) && -1 == _DtCvAscentOfRegionSeg(pSeg)
		&& maxRegion < _DtCvHeightOfRegionSeg(pSeg) + subY)
		maxRegion = _DtCvHeightOfRegionSeg(pSeg) + subY;
	  }
	else /* not a subscript or superscript */
	  {
	    /*
	     * set up the super and sub script offsets for following
	     * segments.
	     */
	    if (_DtCvIsSegString (pSeg))
	        _DtCvFontMetrics(canvas,_DtCvFontOfStringSeg(pSeg),
					NULL, NULL, NULL, &superY, &subY);
	    else if (_DtCvIsSegRegion(pSeg))
	      {
		superY = _DtCvHeightOfRegionSeg(pSeg) * 4 / 10;
		subY   = superY;
	      }
	  }

	/*
	 * now determine the maximums for ascent and descent.
	 */
	if (fontAscent > maxAscent)
	    maxAscent = fontAscent;
	if (fontDescent > maxDescent)
	    maxDescent = fontDescent;

	/*
	 * decrement the count
	 */
	count -= len;

	/*
	 * If this segment terminates the paragraph
	 * force the end of the loop.
	 */
        pSeg  = pSeg->next_disp;
	start = 0;
      }

    if (txt_justify == _DtCvJUSTIFY_RIGHT || _DtCvJUSTIFY_CENTER == txt_justify)
      {
	/*
	 * justify the line.
	 */
	_DtCvUnit   workWidth = max_width - layout->text_x_pos -
						r_margin - layout->cur_len;
	if (txt_justify == _DtCvJUSTIFY_CENTER)
	    workWidth = workWidth / 2;

	if (workWidth < 0)
	    workWidth = 0;

	layout->text_x_pos += workWidth;
      }

    /*
     * adjust for any special characters found
     */
    if (maxRegion > maxAscent + maxDescent + 1)
	maxAscent = maxRegion - maxDescent - 1;

    /*
     * check to see if the max values have even been touched.
     */
    if (layout->line_bytes == 0 && maxAscent == 0 && maxDescent == 0)
	maxAscent = canvas->metrics.line_height;

    /*
     * adjust ascent and descent by the traversal and link info
     */
    maxDescent += layout->leading;
    if (fndLnk)
      {
	maxAscent  += canvas->traversal_info.space_above;
        maxDescent += canvas->traversal_info.space_below;
	if (visLnk)
	  {
	    maxAscent  += canvas->link_info.space_above;
            maxDescent += canvas->link_info.space_below;
	  }
      }

    /*
     * save the line information, if there is a string here.
     */
    if (layout->line_bytes > 0)
      {
        canvas->txt_lst[txtCnt].processed  = _DtCvFALSE;
        canvas->txt_lst[txtCnt].text_x     = layout->text_x_pos;
        canvas->txt_lst[txtCnt].max_x      = layout->text_x_pos;
        canvas->txt_lst[txtCnt].baseline   = layout->y_pos + maxAscent;
        canvas->txt_lst[txtCnt].descent    = maxDescent;
        canvas->txt_lst[txtCnt].ascent     = maxAscent;
        canvas->txt_lst[txtCnt].byte_index = layout->line_start;
        canvas->txt_lst[txtCnt].length     = layout->line_bytes;
        canvas->txt_lst[txtCnt].seg_ptr    = layout->line_seg;

        canvas->txt_cnt++;
      }
    /*
     * blank line is one half the normal size line
     */
    else
      {
	maxAscent  = (maxAscent + maxDescent) / 2;
	maxDescent = 0;
      }

    if (layout->text_x_pos + layout->cur_len > layout->cur_max_x)
	layout->cur_max_x = layout->text_x_pos + layout->cur_len;

    if (layout->text_x_pos + layout->cur_len > layout->max_x_pos)
	layout->max_x_pos = layout->text_x_pos + layout->cur_len;

    /*
     * zero the string info
     */
    layout->line_bytes = 0;
    layout->cur_len    = 0;
    layout->lst_hyper  = -1;
    layout->lst_vis    = False;

    _DtCvSetJoinInfo(layout, False, -1);

    /*
     * adjust where the next line is positioned.
     */
    layout->y_pos = layout->y_pos + maxAscent + maxDescent + 1;
}

/******************************************************************************
 * Function: _DtCvCheckAddHyperToTravList
 *
 *****************************************************************************/
void
_DtCvCheckAddHyperToTravList (
    _DtCanvasStruct	*canvas,
    _DtCvSegmentI	*p_seg,
    _DtCvValue		 flag,
    _DtCvValue		*lst_vis,
    int			*lst_hyper,
    _DtCvUnit		*cur_len)
{
    int   nxtHyper;
    int   prevIdx;
    _DtCvValue	junk;
    _DtCvUnit	retLen = *cur_len;

    if (_DtCvIsSegALink (p_seg))
      {
	nxtHyper = _DtCvGetNextTravEntry(canvas);
	if (-1 == nxtHyper)
/*
 * NOTE....should this routine return a value?
 * If (re)alloc error occurs, this simply ignores the problem.
 */
		return;

	prevIdx = nxtHyper - 1;
	if (prevIdx < 0
	     || _DtCvTraversalLink != canvas->trav_lst[prevIdx].type
	     || p_seg->link_idx != canvas->trav_lst[prevIdx].seg_ptr->link_idx)
	  {
	    /*
	     * save this hypertext link in the traversal list
	     */
	    _DtCvSetTravEntryInfo (canvas, nxtHyper, _DtCvTraversalLink, p_seg,
					canvas->txt_cnt, _DtCvTRUE);
	  }
      }

    /*
     * take into account the link metrics.
     */
    junk = _DtCvIsSegVisibleLink(p_seg);
    *lst_vis = _DtCvModifyXpos(canvas->link_info, p_seg, junk,
			*lst_vis, *lst_hyper,
			&retLen);
    /*
     * take into account the traversal metrics
     */
    junk = _DtCvIsSegALink(p_seg);
    (void) _DtCvModifyXpos(canvas->traversal_info, p_seg, junk,
			((_DtCvValue) True), *lst_hyper,
			&retLen);

    *lst_hyper = p_seg->link_idx;

    if (_DtCvTRUE == flag)
	*cur_len = retLen;
}

/******************************************************************************
 * Function: ProcessStringSegment
 *
 * chops a string segment up until its completely used.
 *
 * Returns:
 *	0	if the entire string segment was processed.
 *	1	if the required number of lines were processed.
 *****************************************************************************/
int
_DtCvProcessStringSegment(
    _DtCanvasStruct	*canvas,
    _DtCvLayoutInfo	*lay_info,
    _DtCvUnit		 max_width,
    _DtCvUnit		 l_margin,
    _DtCvUnit		 r_margin,
    _DtCvSegmentI	*cur_seg,
    unsigned int	*cur_start,
    _DtCvFrmtOption	 txt_justify,
    _DtCvValue		 stat_flag)
{
    _DtCvUnit   workWidth;
    _DtCvUnit   stringLen;
    _DtCvUnit   textWidth;
    _DtCvUnit   nWidth;
    _DtCvUnit   spaceSize = 0;
    int	   oldType;
    int	   retStart;
    int	   retCount;
    wchar_t	 *wcp;
    void	 *pChar;
    char	 *strPtr;
    _DtCvValue    done    = False;
    _DtCvSegmentI *retSeg;

    if (NULL != _DtCvStringOfStringSeg(cur_seg))
      {
	if (lay_info->cur_len == 0)
	  {
	    lay_info->line_seg   = cur_seg;
	    lay_info->line_start = *cur_start;
	  }

	if (*cur_start == 0 && (cur_seg->type & _DtCvSEARCH_FLAG))
	    lay_info->delayed_search_saves++;

	oldType = _DtCvPrimaryTypeOfSeg (cur_seg);

	/*
	 * is alignment in effect?
	 */
	if (TRUE == lay_info->align_flag)
	  {
	    pChar     = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
				_DtCvIsSegWideChar(cur_seg), *cur_start);
	    nWidth = _DtCvStrcspn (pChar, lay_info->align_char,
					_DtCvIsSegWideChar(cur_seg),
					&stringLen);
	    if (-1 == nWidth)
		return -1;

	    /*
	     * we got a valid length back, calculate the length
	     */
	    textWidth = 0;
	    if (0 != stringLen)
	        textWidth = _DtCvGetStringWidth(canvas,cur_seg,pChar,stringLen);

	    /*
	     * check to see if this a hypertext that needs
	     * to be remembered.
	     */
	    _DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvTRUE,
					&(lay_info->lst_vis),
					&(lay_info->lst_hyper),
					&(lay_info->cur_len));

	    /*
	     * update the length and position information
	     * to skip past the characters before the alignment character.
	     */
	    lay_info->line_bytes += stringLen;
	    lay_info->cur_len    += (textWidth
					+ _DtCvGetTraversalWidth(canvas,
						cur_seg, lay_info->lst_hyper));

	    *cur_start += stringLen;

	    /*
	     * if we didn't find the character, check to see if this forces
	     * a newline - honor it if it does. We'll check the next
	     * string segment for the alignment character.
	     */
	    if (1 == nWidth && _DtCvIsSegNewLine (cur_seg)
						&& lay_info->line_bytes)
	      {
		_DtCvSaveInfo (canvas,lay_info,max_width,r_margin,txt_justify);

		while (lay_info->delayed_search_saves > 0) {
		    _DtCvSetSearchEntryInfo(canvas, canvas->txt_cnt - 1);
		    lay_info->delayed_search_saves--;
		}
		return 0;
	      }

	    /*
	     * so we found the character, now get it's width.
	     */
	    pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
				_DtCvIsSegWideChar(cur_seg), *cur_start);
	    textWidth = _DtCvGetStringWidth(canvas, cur_seg, pChar, 1)
			+ _DtCvGetTraversalWidth(canvas, cur_seg,
					lay_info->lst_hyper);
	    /*
	     * is this the second or more align position?
	     * if so, need to shift the character to align with others.
	     */
	    if (lay_info->align_pos >
		      lay_info->text_x_pos + lay_info->cur_len + textWidth / 2)
		lay_info->text_x_pos = lay_info->align_pos - lay_info->cur_len
					- textWidth / 2;
	    /*
	     * otherwise, does this exceed the previous alignments?
	     * if so, the table processing should catch that we've
	     * changed the alignment position and re-format the others.
	     */
	    else if (lay_info->align_pos <
		      lay_info->text_x_pos + lay_info->cur_len + textWidth / 2)
		lay_info->align_pos =
		      lay_info->text_x_pos + lay_info->cur_len + textWidth / 2;

	    /*
	     * indicate that the character has been found.
	     */
	    lay_info->align_flag = False;

	    /*
	     * check to see if this item can end a line.
	     * if can't end the line, force a join for the next segment or
	     * for the rest of this segment.
	     */
	    if (False == _DtCvCheckLineSyntax(canvas,cur_seg,*cur_start,1,False))
		lay_info->join = True;

	    /*
	     * update the length and position information to
	     * include the character.
	     */
	    lay_info->line_bytes++;
	    lay_info->cur_len += textWidth;

	    *cur_start += 1;

	    /*
	     * check to see if this is the end of the segment.
	     */
	    pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
				_DtCvIsSegWideChar(cur_seg), *cur_start);
	    if ((_DtCvIsSegWideChar(cur_seg) && 0 == *((wchar_t *) pChar))
				||
		(_DtCvIsSegRegChar(cur_seg) && '\0' == *((char *) pChar)))
		return 0;
	  }

	while (1)
	  {
	    /*
	     * recalculate the width
	     */
	    workWidth = max_width - lay_info->text_x_pos -
						lay_info->cur_len - r_margin;

	    /*
	     * adjust the character pointer and get the
	     * length of the string.
	     */
	    pChar     = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
				_DtCvIsSegWideChar(cur_seg), *cur_start);
	    stringLen = _DtCvStrLen (pChar, _DtCvIsSegWideChar(cur_seg));
    
	    /*
	     * get the pixel width of the text string.
	     */
	    textWidth = _DtCvGetStringWidth(canvas,cur_seg,pChar,stringLen)
			+ _DtCvGetTraversalWidth(canvas, cur_seg,
					lay_info->lst_hyper);
	    /*
	     * Will it fit in the current width?
	     */
	    if (stat_flag == True || textWidth <= workWidth)
	      {
		/*
		 * Yes, this segment or part of a segment can fit in the
		 * current width. But can the last character of this
		 * segment end a line and can the beginning of the next
		 * segment start a new line?
		 */
		if (stat_flag == True ||
			_DtCvCheckLineSyntax (canvas, cur_seg,
					*cur_start, stringLen, False) == TRUE)
		  {
	            /*
	             * check to see if this a hypertext that needs
	             * to be remembered.
	             */
	            _DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvFALSE,
					&(lay_info->lst_vis),
					&(lay_info->lst_hyper),
					&(lay_info->cur_len));

		    /*
		     * The line syntax is good.
		     * Update the global and width variables.
		     */
		    lay_info->line_bytes += stringLen;
		    lay_info->cur_len    += textWidth;
		    _DtCvSetJoinInfo(lay_info,
					_DtCvIsSegNonBreakingChar(cur_seg),
					-1);

		    /*
		     * Check to see if this segment forces an end
		     */
		    if (_DtCvIsSegNewLine (cur_seg) && lay_info->line_bytes) {
			_DtCvSaveInfo (canvas, lay_info, max_width,
							r_margin, txt_justify);
			while (lay_info->delayed_search_saves > 0) {
	 		    _DtCvSetSearchEntryInfo(canvas,
						    canvas->txt_cnt - 1);
			    lay_info->delayed_search_saves--;
			}
		    }

		    return 0;
		  }
    
		/*
		 * CheckLineSyntax says that either this line couldn't
		 * end a line or the next segment couldn't start a line.
		 * Therefore, find out how much of the next segment or
		 * segments we need to incorporate to satisfy the Line
		 * Syntax rules.
		 */
		nWidth = _DtCvGetNextWidth (canvas, oldType,
				lay_info->lst_hyper,
				cur_seg->next_seg, 0, cur_seg,
				&retSeg, &retStart, &retCount);
		/*
		 * will this segment + the next segment fit?
		 */
		if (textWidth + nWidth <= workWidth)
		  {
	            /*
	             * check to see if this a hypertext that needs
	             * to be remembered.
	             */
	            _DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvFALSE,
					&(lay_info->lst_vis),
					&(lay_info->lst_hyper),
					&(lay_info->cur_len));

		    /*
		     * YEAH Team!! It Fits!!
		     *
		     * Update the global and width variables.
		     */
		    lay_info->line_bytes += stringLen;
		    lay_info->cur_len    += textWidth;
		    _DtCvSetJoinInfo(lay_info, False, -1);
    
		    return 0;
		  }
	      }
    
	    /*
	     * the text width plus the next segment is tooo big
	     * to fit. Reduce the current segment if possible
	     */
	    done = False;
	    textWidth = 0;
	    stringLen = 0;
	    while (!done)
	      {
		nWidth = _DtCvGetNextWidth (canvas, oldType,
				lay_info->lst_hyper,
				cur_seg, *cur_start, NULL,
				&retSeg, &retStart, &retCount);

		if (retSeg == cur_seg && textWidth + nWidth <= workWidth)
		  {
	            /*
	             * check to see if this a hypertext that needs
	             * to be remembered.
	             */
	            _DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvFALSE,
					&(lay_info->lst_vis),
					&(lay_info->lst_hyper),
					&(lay_info->cur_len));

		    _DtCvSetJoinInfo(lay_info, False, -1);
		    *cur_start     = retStart;
		    stringLen     += retCount;
		    textWidth     += nWidth;
		    spaceSize      = 0;

		    /*
		     * take into account a space if that is where it breaks.
		     */
		    pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
						_DtCvIsSegWideChar(cur_seg),
						*cur_start);
		    if ((_DtCvIsSegWideChar(cur_seg) &&
						(' ' == *((wchar_t *) pChar)))
					||
			(_DtCvIsSegRegChar(cur_seg) &&
						(' ' == *((char *) pChar))))
		      {
			    spaceSize = _DtCvGetStringWidth(canvas,
						cur_seg, pChar, 1)
					+ _DtCvGetTraversalWidth (canvas,
						cur_seg, lay_info->lst_hyper);
			    textWidth += spaceSize;
			    stringLen++;
			    (*cur_start)++;
			  }
		      }
		else
		  {
		    /*
		     * Done trying to find a segment that will
		     * fit in the size given
		     */
		    done = True;
		  }
	      }

	    /*
	     * Update the global variables
	     */
	    lay_info->line_bytes += stringLen;
	    lay_info->cur_len  += textWidth;

	    if (lay_info->join == True || lay_info->line_bytes == 0)
	      {
	        /*
		 * This line would be empty if we followed the rules.
		 * Or it would break a line improperly.
		 * Force this onto the line.
	         * check to see if this a hypertext that needs
	         * to be remembered.
	         */
	        _DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvTRUE,
					&(lay_info->lst_vis),
					&(lay_info->lst_hyper),
					&(lay_info->cur_len));

		/*
		 * Couldn't find a smaller, have to
		 * go with the larger segment.
		 */
		pChar      = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
						_DtCvIsSegWideChar(cur_seg),
						*cur_start);
		stringLen  = _DtCvStrLen (pChar, _DtCvIsSegWideChar(cur_seg));
		if (retCount > 0 && retCount < stringLen)
		    stringLen = retCount;

		lay_info->line_bytes += stringLen;
		lay_info->cur_len  += (_DtCvGetStringWidth(canvas, cur_seg,
							pChar, stringLen)
					+ _DtCvGetTraversalWidth (canvas,
					cur_seg, lay_info->lst_hyper));

		_DtCvSetJoinInfo(lay_info, False, -1);

		/*
		 * If we had to do a bigger segment,
		 * then we're done processing the target segment.
		 */
		if (stringLen == _DtCvStrLen(pChar,_DtCvIsSegWideChar(cur_seg)))
		  {
		    if (_DtCvCheckLineSyntax (canvas, cur_seg,
				*cur_start, stringLen, False) == False)
			_DtCvSetJoinInfo(lay_info, True, -1);
		    else if (_DtCvIsSegNewLine (cur_seg)) {
			_DtCvSaveInfo (canvas, lay_info, max_width,
							r_margin, txt_justify);
			while (lay_info->delayed_search_saves > 0) {
	 		    _DtCvSetSearchEntryInfo(canvas,
						    canvas->txt_cnt - 1);
			    lay_info->delayed_search_saves--;
			}
		    }
		    return 0;
		  }

		*cur_start     = retStart;
	      }
	    else if (spaceSize)
	      {
		/*
		 * If a space was included as the last character,
		 * remove it now.
		 */
		lay_info->line_bytes--;
		lay_info->cur_len -= spaceSize;
	      }

	    /*
	     * Save the information
	     */
	    _DtCvSaveInfo (canvas, lay_info, max_width, r_margin, txt_justify);
	    if (*cur_start == 0 && (cur_seg->type & _DtCvSEARCH_FLAG))
		lay_info->delayed_search_saves--;

	    while (lay_info->delayed_search_saves > 0) {
	        _DtCvSetSearchEntryInfo(canvas, canvas->txt_cnt - 1);
		lay_info->delayed_search_saves--;
	    }

	    /*
	     * Skip the spaces.
	     */
	    pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
						_DtCvIsSegWideChar(cur_seg),
						*cur_start);
	    if (_DtCvIsSegWideChar(cur_seg))
	      {
		wcp = pChar;
		while (' ' == *wcp)
		  {
		    wcp++;
		    (*cur_start)++;
		  }

	        pChar = wcp;
	      }
	    else /* single byte string */
	      {
		strPtr = pChar;
	        while (' ' == *strPtr)
		  {
		    strPtr++;
		    (*cur_start)++;
		  }

	        pChar = strPtr;
	      }

	    /*
	     * are we at the end of the segment?
	     */
	    if ((_DtCvIsSegWideChar(cur_seg) && 0 == *((wchar_t *) pChar))
			||
		(_DtCvIsSegRegChar(cur_seg) && 0 == *((char *) pChar)))
	        return 0;

	    if (*cur_start == 0 && (cur_seg->type & _DtCvSEARCH_FLAG))
		lay_info->delayed_search_saves++;

	    /*
	     * Initialize the global variables
	     */
	    lay_info->line_seg   = cur_seg;
	    lay_info->line_start = *cur_start;
	    lay_info->text_x_pos = l_margin;

	    if (CheckFormat(lay_info) == True)
		return 1;

            /*
             * check to see if this a hypertext that needs
             * to be remembered.
             */
            _DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvTRUE,
					&(lay_info->lst_vis),
					&(lay_info->lst_hyper),
					&(lay_info->cur_len));
	  }
      }
    else if (_DtCvIsSegNewLine (cur_seg))
      {
	/*
	 * Force a save - even if it is an empty line.
	 */
	_DtCvSaveInfo (canvas, lay_info, max_width, r_margin, txt_justify);
	while (lay_info->delayed_search_saves > 0) {
	    _DtCvSetSearchEntryInfo(canvas, canvas->txt_cnt - 1);
	    lay_info->delayed_search_saves--;
	}
      }

    return 0;

} /* End _DtCvProcessStringSegment */

/******************************************************************************
 * Function:	_DtCvSetJoinInfo
 *
 * Returns:	sets the joining information to the given information.
 *
 *****************************************************************************/
void
_DtCvSetJoinInfo (
    _DtCvLayoutInfo	*lay_info,
    _DtCvValue		 flag,
    int			 txt_ln)
{
    lay_info->join      = flag;
    lay_info->join_line = txt_ln;
}

/******************************************************************************
 * Function:	_DtCvGetNextTravEntry
 *
 * Returns:	 >= 0 if success,
 *		-1 if failure.
 *
 * Purpose:	Return the next available entry in the traversal list.
 *
 *****************************************************************************/
int
_DtCvGetNextTravEntry (
    _DtCanvasStruct  *canvas)
{
    int  nxtEntry = canvas->trav_cnt;

    /*
     * does the list need to grow?
     */
    if (nxtEntry >= canvas->trav_max)
      {
	/*
	 * grow by a set amount
	 */
	canvas->trav_max += GROW_SIZE;

	/*
	 * realloc or malloc?
	 */
	if (NULL != canvas->trav_lst)
	    canvas->trav_lst = (_DtCvTraversalInfo *) realloc (
			(char *) canvas->trav_lst,
			((sizeof(_DtCvTraversalInfo)) * canvas->trav_max));
	else
	    canvas->trav_lst = (_DtCvTraversalInfo *) malloc (
			((sizeof(_DtCvTraversalInfo)) * canvas->trav_max));

	/*
	 * did the memory allocation work? if not return error code.
	 */
	if (NULL == canvas->trav_lst)
	  {
	    canvas->trav_max = 0;
	    canvas->trav_cnt = 0;
	    nxtEntry = -1;
          }
      }

    canvas->trav_lst[nxtEntry] = DefTravData;

    return nxtEntry;
}

/******************************************************************************
 * Function:	_DtCvSetTravEntryInfo
 *
 * Returns:	 0 if success,
 *		-1 if failure.
 *
 * Purpose:	Set the high level information in an entry of the traversal
 *              list.
 *****************************************************************************/
int
_DtCvSetTravEntryInfo (
    _DtCanvasStruct	*canvas,
    int			 entry,
    _DtCvTraversalType	 type,
    _DtCvSegmentI	*p_seg,
    int			 line_idx,
    _DtCvValue		 inc)
{
    int  result = -1;

    if (-1 != entry && entry <= canvas->trav_cnt)
      {
	_DtCvTraversalInfo *travEntry = &(canvas->trav_lst[entry]);

	travEntry->type    = type;
	travEntry->seg_ptr = p_seg;
	travEntry->idx     = line_idx;

	if (_DtCvTRUE == inc)
	    canvas->trav_cnt++;

	result = 0;
      }

    return result;
}

int
_DtCvGetNextSearchEntry(_DtCanvasStruct* canvas)
{
    if (canvas->search_cnt >= canvas->search_max) {
	canvas->search_max += GROW_SIZE;

	if (canvas->searchs)
	    canvas->searchs = (_DtCvSearchData *)
			realloc((void*)canvas->searchs,
				canvas->search_max * sizeof(_DtCvSearchData));
	else
	    canvas->searchs = (_DtCvSearchData *)
			malloc(canvas->search_max * sizeof(_DtCvSearchData));
    }

    canvas->searchs[canvas->search_cnt].idx = -1;

    return canvas->search_cnt++;
}

void
_DtCvSetSearchEntryInfo(_DtCanvasStruct* canvas, int line_idx)
{
    int search_idx;

    /* get a next available slot for search */
    search_idx = _DtCvGetNextSearchEntry(canvas);

    /* save information (i.e. line_idx) */
    canvas->searchs[search_idx].idx = line_idx;
}

/******************************************************************************
 * Function:	_DtCvSetTravEntryPos
 *
 * Returns:	 0 if success,
 *		-1 if failure.
 *
 * Purpose:	Set the position and dimension information of an entry in
 *		the traversal list.
 *
 *****************************************************************************/
int
_DtCvSetTravEntryPos (
    _DtCanvasStruct	*canvas,
    int			 entry,
    _DtCvUnit		 x,
    _DtCvUnit		 y,
    _DtCvUnit		 width,
    _DtCvUnit		 height)
{
    int  result = -1;

    if (-1 != entry && entry <= canvas->trav_cnt)
      {
	_DtCvTraversalInfo *travEntry = &(canvas->trav_lst[entry]);

	travEntry->x_pos  = x;
	travEntry->y_pos  = y;
	travEntry->width  = width;
	travEntry->height = height;

	result = 0;
      }

    return result;
}

/******************************************************************************
 * Function:	_DtCvCalcMarkPos
 *
 * Returns:	 0 if success,
 *		-1 if failure.
 *
 * Purpose:	Calcalate the position and dimension information of a mark.
 *
 *****************************************************************************/
int
_DtCvCalcMarkPos (
    _DtCanvasStruct	*canvas,
    int			 entry,
    _DtCvUnit		*ret_x,
    _DtCvUnit		*ret_y,
    _DtCvUnit		*ret_width,
    _DtCvUnit		*ret_height)
{
    int            result = -1;

    if (-1 != entry && entry <= canvas->mark_cnt)
      {
        _DtCvMarkData *mark = &(canvas->marks[entry]);

	/*
	 * if we've got a line index for the mark, get the positions.
	 */
	if (-1 != mark->beg.line_idx && -1 != mark->end.line_idx)
	  {
	    _DtCvDspLine  *line = &(canvas->txt_lst[mark->beg.line_idx]);

	    *ret_x = mark->beg.x;
	    *ret_y = mark->beg.y - line->ascent;

	    if (mark->beg.line_idx == mark->end.line_idx)
	        *ret_width = mark->end.x - *ret_x;
	    else
	        *ret_width = canvas->txt_lst[mark->beg.line_idx].max_x - *ret_x;

	    *ret_height = line->ascent + line->descent + 1;

	    result = 0;
	  }
      }

    return result;
}

/******************************************************************************
 * Function:	_DtCvSortTraversalList
 *
 * Returns:	 nothing
 *
 * Purpose:	Sort the traversal list
 *
 *****************************************************************************/
void
_DtCvSortTraversalList (
    _DtCanvasStruct	*canvas,
    _DtCvValue		 retain)
{
    int curTrav = canvas->cur_trav;

    if (1 < canvas->trav_cnt)
      {
	/*
	 * indicate this is the current traversal
	 */
	if (-1 != curTrav)
	    canvas->trav_lst[curTrav].active = retain;

	/*
	 * sort the items.
	 */
	qsort (canvas->trav_lst, canvas->trav_cnt, sizeof(_DtCvTraversalInfo),
			CompareTraversalPos);

	if (_DtCvTRUE == retain && -1 != curTrav &&
				_DtCvFALSE == canvas->trav_lst[curTrav].active)
	  {
	    curTrav = 0;
	    while (_DtCvFALSE == canvas->trav_lst[curTrav].active)
		curTrav++;

	    canvas->cur_trav = curTrav;
	  }

	/*
	 * clear the active flag
	 */
	if (-1 != curTrav)
	    canvas->trav_lst[curTrav].active = _DtCvFALSE;
      }
}

/*****************************************************************************
 * Function:	_DtCvCvtSegsToPts()
 *
 * Purpose:	Given a set of segments, determine the ending points.
 *
 *****************************************************************************/
_DtCvStatus
_DtCvCvtSegsToPts (
    _DtCanvasStruct	 *canvas,
    _DtCvSegPtsI	**segs,
    _DtCvSelectData	 *beg,
    _DtCvSelectData	 *end,
    _DtCvUnit		 *ret_y1,
    _DtCvUnit		 *ret_y2,
    _DtCvSegmentI	**ret_seg)
{
    int			 count;
    int			 cnt;
    int			 start;
    int			 length;
    long		 lineIdx;
    int			 linkIdx = -1;
    _DtCvValue		 lastVisLnk = _DtCvFALSE;
    _DtCvUnit		 minY = -1;
    _DtCvUnit		 maxY = 0;
    _DtCvUnit		 startX;
    _DtCvUnit		 endX;
    _DtCvUnit		 segWidth;
    _DtCvSegmentI	*pSeg;
    _DtCvSegmentI	*saveSeg;
    _DtCvSegmentI	**retSeg;
    _DtCvDspLine	*lines = canvas->txt_lst;
    _DtCvFlags		 result = _DtCvSTATUS_NONE;
    _DtCvSelectData	*tmpBeg;
    _DtCvSelectData	*tmpEnd;
    _DtCvSelectData	 bReg;
    _DtCvSelectData	 eReg;

    /*
     * initialize the structures.
     */
    bReg = DefSelectData;
    eReg = DefSelectData;
    *beg = DefSelectData;
    *end = DefSelectData;

    /*
     * go through each segment and determine the starting positions.
     */
    while (NULL != *segs)
      {
	result = _DtCvSTATUS_OK;

	/*
	 * what line is this segment on?
	 */
	lineIdx = (long) ((*segs)->segment->internal_use);

	/*
	 * get some information about the line
	 */
	length = lines[lineIdx].length;
	start  = lines[lineIdx].byte_index;
	startX = _DtCvGetStartXOfLine(&(lines[lineIdx]), &pSeg);
	pSeg   = lines[lineIdx].seg_ptr;

	/*
	 * now skip the segments on this line that aren't in the data pt.
	 */
	while (NULL != pSeg && pSeg != (*segs)->segment)
	  {
	    /*
	     * advance past any hypertext link offsets.
	     */
	    startX = _DtCvAdvanceXOfLine(canvas, pSeg, startX,
						&linkIdx, &lastVisLnk);

	    /*
	     * we know that this is not the segment we are looking for,
	     * so go past it.
	     */
	    _DtCvGetWidthOfSegment(canvas, pSeg, start, length,
							&cnt, &segWidth, NULL);

	    /*
	     * skip the segment's width, decrease the overall length by
	     * the segment's count, reset the character start point and
	     * go to the next segment.
	     */
	    startX += segWidth;
	    length -= cnt;
	    start   = 0;
	    pSeg    = pSeg->next_disp;
	  }

	/*
	 * This segment should be all or partially selected.
	 */
	if (NULL == pSeg)
	    return _DtCvSTATUS_BAD;

	/*
	 * now figure the start location.
	 */
	startX = _DtCvAdvanceXOfLine(canvas, pSeg, startX,
						&linkIdx, &lastVisLnk);
	/*
	 * guarenteed that this is the *first* line that the segment
	 * exists on. Therefore, may have to go to another line for
	 * the correct offset
	 */
	while (start + length < (*segs)->offset)
	  {
	    do { lineIdx++; } while (lineIdx < canvas->txt_cnt
					&& pSeg != lines[lineIdx].seg_ptr);

	    if (lineIdx >= canvas->txt_cnt)
		return _DtCvSTATUS_BAD;

	    length = lines[lineIdx].length;
	    start  = lines[lineIdx].byte_index;
	    startX = lines[lineIdx].text_x;

	    linkIdx    = -1;
	    lastVisLnk = False;
	    startX     = _DtCvAdvanceXOfLine(canvas, pSeg, startX,
						&linkIdx, &lastVisLnk);
	  }

	/*
	 * how many characters do we need to skip?
	 */
	count    = (*segs)->offset - start;
	segWidth = 0;
	if (0 < count)
            _DtCvGetWidthOfSegment(canvas, pSeg, start, count,
							&cnt, &segWidth, NULL);
	/*
	 * adjust the info by the width of the skipped characters.
	 */
	start  += count;
	length -= count;
	startX += segWidth;

	/*
	 * is this a region? If so set the region information instead.
	 */
	tmpBeg = beg;
	tmpEnd = end;
	retSeg = ret_seg;
	if (_DtCvIsSegRegion((*segs)->segment))
	  {
	    tmpBeg = &bReg;
	    tmpEnd = &eReg;
	    retSeg = &saveSeg;
	  }

	/*
	 * does this segment start the selection? text or region?
	 */
	if (tmpBeg->x == -1 || tmpBeg->y > lines[lineIdx].baseline ||
			(tmpBeg->line_idx == lineIdx && tmpBeg->x > startX))
	  {
	    tmpBeg->x        = startX;
	    tmpBeg->y        = lines[lineIdx].baseline;
	    tmpBeg->line_idx = lineIdx;
	    tmpBeg->char_idx = lines[lineIdx].length - length;
	    if (NULL != retSeg)
		*retSeg = (*segs)->segment;
	  }

	/*
	 * get the amount of this segment that is selected.
	 */
	count = (*segs)->len;

	/*
	 * is it longer than what's (left) on this line?
	 */
	while (count > length)
	  {
	    /*
	     * go to the next line containing the segment
	     */
	    do { 
		/*
		 * does this line have the minium y?
		 */
	        if (minY == -1 ||
			minY > lines[lineIdx].baseline - lines[lineIdx].ascent)
		   minY = lines[lineIdx].baseline - lines[lineIdx].ascent;

		 lineIdx++; 
	      } while (lineIdx < canvas->txt_cnt
					&& pSeg != lines[lineIdx].seg_ptr);
	    /*
	     * did we run out of lines?
	     */
	    if (lineIdx >= canvas->txt_cnt)
		return _DtCvSTATUS_BAD;
		
	    /*
	     * start over on this line
	     */
	    segWidth = 0;

	    /*
	     * get the true count to the next offset
	     */
	    cnt      = lines[lineIdx].byte_index - start;

	    /*
	     * get the next lines starting info.
	     */
	    start    = lines[lineIdx].byte_index;
	    length   = lines[lineIdx].length;
	    startX   = _DtCvGetStartXOfLine(&(lines[lineIdx]), &pSeg);
	    linkIdx    = -1;
	    lastVisLnk = False;
	    startX     = _DtCvAdvanceXOfLine(canvas, pSeg, startX,
						&linkIdx, &lastVisLnk);
	    /*
	     * subtract the previous length
	     */
	    count -= cnt;
	  }

	/*
	 * now go down the line, examining each segment.
	 */
	while (0 < count)
	  {
	    /*
	     * findout how many characters are in the next segment, and its
	     * width.
	     */
	    _DtCvGetWidthOfSegment(canvas,pSeg,start,count,&cnt,&segWidth,NULL);

	    /*
	     * there are less than in the count, go to the next segment.
	     */
	    if (cnt < count)
	      {
		pSeg   = pSeg->next_disp;
		start  = 0;
		startX += segWidth;
	      }

	    length -= cnt;
	    count -= cnt;
	  }

	endX   = startX + segWidth;

	/*
	 * does this segment end a segment?
	 */
	if (tmpEnd->x == -1 || tmpEnd->y < lines[lineIdx].baseline ||
			(tmpEnd->line_idx == lineIdx && tmpEnd->x < endX))
	  {
	    tmpEnd->x        = endX;
	    tmpEnd->y        = lines[lineIdx].baseline;
	    tmpEnd->line_idx = lineIdx;
	    tmpEnd->char_idx = lines[lineIdx].length - length;
	  }

	/*
	 * check for min and max values
	 */
	if (minY == -1 ||
			minY > lines[lineIdx].baseline - lines[lineIdx].ascent)
	    minY = lines[lineIdx].baseline - lines[lineIdx].ascent;

	if (maxY < lines[lineIdx].baseline + lines[lineIdx].descent)
	    maxY = lines[lineIdx].baseline + lines[lineIdx].descent;
	/*
	 * go to the next segment
	 */
	segs++;
      }

    /*
     * now determine if a region really starts the beginning of a
     * selection or a text does.
     *
     * was a region found?
     */
    if (-1 != bReg.x)
      {
	/*
	 * if no text was found, take the region information.
	 */
	if (-1 == beg->x)
	  {
	    *beg = bReg;
	    if (NULL != ret_seg)
		*ret_seg = saveSeg;
	  }
	/*
	 * or if the region is inline to the other
	 * text and it is before the text, then take it's x value.
	 */
	else if (bReg.x < beg->x &&
			(bReg.line_idx == beg->line_idx ||
	/*
	 * Or if the region is 'standalone' (a bullet of a list, a
	 * graphic to wrap around, etc.)  then check to see if it
	 * straddles the other information and is before the text.  If
	 * it does, take it's x value.
	 */
			_DtCvStraddlesPt(beg->y,
				bReg.y - lines[bReg.line_idx].ascent,
				bReg.y - lines[bReg.line_idx].descent)))
	  {
	    beg->x = bReg.x;

	    if (NULL != ret_seg)
		*ret_seg = saveSeg;
	  }
      }

    /*
     * now determine if a region really ends the selection or a text does.
     *
     * was a region found?
     */
    if (-1 != eReg.x)
      {
	/*
	 * if no text was found, take the region information.
	 */
	if (-1 == end->x)
	    *end = eReg;
	/*
	 * or if the region is inline to the other
	 * text and it is before the text, then take it's x value.
	 */
	else if (eReg.x > end->x &&
			(eReg.line_idx == end->line_idx ||
	/*
	 * Or if the region is 'standalone' (a bullet of a list, a
	 * graphic to wrap around, etc.)  then check to see if it
	 * straddles the other information and is before the text.  If
	 * it does, take it's x value.
	 */
			_DtCvStraddlesPt(end->y,
				eReg.y - lines[eReg.line_idx].ascent,
				eReg.y - lines[eReg.line_idx].descent)))
	    end->x = eReg.x;

      }
    if (NULL != ret_y1)
	*ret_y1 = minY;
    if (NULL != ret_y2)
	*ret_y2 = maxY;

    return result;
}

/*****************************************************************************
 * Function:	_DtCvAddToMarkList()
 *
 * Purpose:	Add a mark to the list of marks.
 *
 *****************************************************************************/
int
_DtCvAddToMarkList (
    _DtCanvasStruct	 *canvas,
    _DtCvPointer	  client_data,
    _DtCvValue		  flag,
    _DtCvSelectData	 *beg,
    _DtCvSelectData	 *end)
{
    _DtCvMarkData  *nxtMark;

    /*
     * does the array need more memory?
     */
    if (canvas->mark_cnt >= canvas->mark_max)
      {
	canvas->mark_max += GROW_SIZE;

        if (NULL == canvas->marks)
	    canvas->marks = (_DtCvMarkData *) malloc(
				sizeof(_DtCvMarkData) * canvas->mark_max);
        else
	    canvas->marks = (_DtCvMarkData *) realloc((void *) canvas->marks,
				sizeof(_DtCvMarkData) * canvas->mark_max);

	/*
	 * memory loss - bail
	 */
	if (NULL == canvas->marks)
	    return -1;
      }

    /*
     * set the mark information
     */
    nxtMark              = &(canvas->marks[canvas->mark_cnt]);
    nxtMark->on          = flag;
    nxtMark->client_data = client_data;
    nxtMark->beg         = *beg;
    nxtMark->end         = *end;
    canvas->mark_cnt++;

    return (canvas->mark_cnt - 1);
}
