Merge branch 'master' into cde-next
[oweals/cde.git] / cde / lib / DtHelp / LayoutUtil.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these libraries and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: LayoutUtil.c /main/26 1996/11/06 12:25:09 cde-hp $ */
24 /************************************<+>*************************************
25  ****************************************************************************
26  **
27  **   File:       LayoutUtil.c
28  **
29  **   Project:    Cde DtHelp
30  **
31  **   Description:
32  **
33  **  (c) Copyright 1987, 1988, 1989, 1990, 1991, 1992 Hewlett-Packard Company
34  **
35  **  (c) Copyright 1993, 1994 Hewlett-Packard Company
36  **  (c) Copyright 1993, 1994 International Business Machines Corp.
37  **  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
38  **  (c) Copyright 1993, 1994 Novell, Inc.
39  **
40  **
41  **
42  ****************************************************************************
43  ************************************<+>*************************************/
44
45 /*
46  * system includes
47  */
48 #include <stdlib.h>
49 #include <string.h>
50 #include <limits.h>
51
52 /*
53  * Canvas Engine includes
54  */
55 #include "CanvasP.h"
56 #include "CanvasSegP.h"
57
58 /*
59  * private includes
60  */
61 #include "CanvasI.h"
62 #include "CvStringI.h"
63 #include "LayoutUtilI.h"
64 #include "StringFuncsI.h"
65 #include "VirtFuncsI.h"
66
67 #if defined(NLS16) || !defined(NO_MESSAGE_CATALOG)
68 #include <nl_types.h>
69 #endif
70
71 #ifndef NL_CAT_LOCALE
72 static const int NL_CAT_LOCALE = 0;
73 #endif
74
75 /******************************************************************************
76  *
77  * Private Defines
78  *
79  *****************************************************************************/
80 #define GROW_SIZE       10
81 #define CheckFormat(x) \
82         (((x)->format_y == -1 || (x)->format_y > (x)->y_pos) ? False : True)
83
84 /******************************************************************************
85  *
86  * Private Variables
87  *
88  *****************************************************************************/
89 static  char *OneByteCantBeginList = "])}`\"\'.,;?:!";
90 static  char *OneByteCantEndList   = "[({`\"";
91
92 static  _DtCvLayoutInfo DefLayInfo =
93     {
94         NULL,           /* _DtCvSegmentI *line_seg;             */
95          0,             /* unsigned int  line_start;            */
96          0,             /* unsigned int  line_bytes;            */
97          0,             /* _DtCvUnit     cur_len;               */
98          0,             /* _DtCvUnit     max_x_pos;             */
99          0,             /* _DtCvUnit     cur_max_x;             */
100          0,             /* _DtCvUnit     y_pos;                 */
101          0,             /* _DtCvUnit     text_x_pos;            */
102          0,             /* _DtCvUnit     leading;               */
103         -1,             /* int           lst_hyper;             */
104         _CEFORMAT_ALL,  /* int           format_y;              */
105         -1,             /* int           join_line;             */
106         FALSE,          /* _DtCvValue    lst_vis;               */
107         FALSE,          /* _DtCvValue    join;                  */
108         FALSE,          /* _DtCvValue    align_flag;            */
109         NULL,           /* const char   *align_char;            */
110         -1,             /* _DtCvUnit     align_pos;             */
111         0,              /* int           delayed_search_saves   */
112     };
113
114 static const _DtCvSelectData DefSelectData = { -1, -1, -1, -1 };
115 static const _DtCvTraversalInfo DefTravData =
116   {
117     _DtCvFALSE          /* active   */,
118     _DtCvTraversalNone  /* type     */,
119     -1                  /* idx      */,
120     0                   /* x_pos    */,
121     0                   /* y_pos    */,
122     0                   /* width    */,
123     0                   /* height   */,
124     NULL                /* *seg_ptr */
125   };
126
127 /******************************************************************************
128  *
129  * Private Functions
130  *
131  *****************************************************************************/
132 /******************************************************************************
133  * Function:    IsTrueMultiByte
134  *
135  * Returns:     True    if the character is a multibyte character
136  *              False   if the character is a single byte character.
137  *****************************************************************************/
138 static _DtCvValue
139 IsTrueMultiByte (wchar_t wc_char)
140 {
141     char buf[MB_LEN_MAX];
142
143     /*
144      * check to see if this is a one byte character
145      * There might not be a multibyte list for this locale.
146      * Can't break on single byte characters.
147      */
148     if (1 != wctomb(buf, wc_char))
149         return True;
150
151     return False;
152 }
153
154 /******************************************************************************
155  * Function:    CheckList
156  *
157  * Returns:     True    if the character matches one of the characters in
158  *                      the MultiCantEndList.
159  *              False   if the character does not match an item in
160  *                      the MultiCantEndList.
161  *****************************************************************************/
162 static _DtCvValue
163 CheckList (
164     wchar_t              wc_char,
165     const wchar_t       *list)
166 {
167     /*
168      * check the multibyte list for the character
169      */
170     if (list != NULL)
171       {
172         while ('\0' != *list)
173           {
174             /*
175              * it matches, return true
176              */
177             if (*list == wc_char)
178                 return True;
179             list++;
180           }
181       }
182
183     return False;
184 }
185
186 /*****************************************************************************
187  * Function:    static int CompareTraversalPos (_DtCvHandle canvas);
188  *
189  * Parameters:
190  *
191  * Returns:
192  *
193  * Purpose:
194  *
195  *****************************************************************************/
196 static int
197 CompareTraversalPos (
198     const void  *a,
199     const void  *b)
200 {
201     _DtCvTraversalInfo *linkA = (_DtCvTraversalInfo *) a;
202     _DtCvTraversalInfo *linkB = (_DtCvTraversalInfo *) b;
203     _DtCvUnit           centA = linkA->y_pos + (linkA->height >> 1);
204     _DtCvUnit           centB = linkB->y_pos + (linkB->height >> 1);
205
206     if (linkA->y_pos + linkA->height < centB && centA < linkB->y_pos)
207         return -1;
208
209     if (linkB->y_pos + linkB->height < centA && centB < linkA->y_pos)
210         return 1;
211
212     if (linkA->x_pos != linkB->x_pos)
213         return ((linkA->x_pos < linkB->x_pos) ? -1 : 1);
214
215     if (linkA->y_pos != linkB->y_pos)
216         return ((linkA->y_pos < linkB->y_pos) ? -1 : 1);
217
218     if (linkA->height != linkB->height)
219         return ((linkA->height < linkB->height) ? -1 : 1);
220
221     if (linkA->width != linkB->width)
222         return ((linkA->width < linkB->width) ? -1 : 1);
223
224     return 0;
225 }
226
227 /******************************************************************************
228  *
229  * Private Layout Utility Functions
230  *
231  *****************************************************************************/
232 /******************************************************************************
233  * Function:    void _DtCvInitLayoutInfo ()
234  *
235  * Parameters:
236  *
237  * Returns:     Nothing.
238  *
239  *****************************************************************************/
240 void
241 _DtCvInitLayoutInfo (
242     _DtCanvasStruct      *canvas,
243     _DtCvLayoutInfo     *layout)
244 {
245    *layout = DefLayInfo;
246
247    layout->y_pos = canvas->metrics.top_margin;
248 }
249
250 /******************************************************************************
251  * Function:    int _DtCvGetTraversalWidth ()
252  *
253  * Parameters:
254  *
255  * Returns:     The total amount of space to add before and after the
256  *              segment to take into account traversal/link metrics on
257  *              this segment including any necessary to 'close' out the
258  *              link on the previous segment.
259  *
260  *****************************************************************************/
261 int
262 _DtCvGetTraversalWidth (
263     _DtCanvasStruct      *canvas,
264     _DtCvSegmentI        *p_seg,
265     int                  lst_hyper)
266 {
267     int  value = 0;
268     int  lnkBefore = 0;
269     int  lnkAfter  = 0;
270
271     /*
272      * does this segment have a different link than the previous one?
273      */
274     if (lst_hyper != p_seg->link_idx)
275       {
276         /*
277          * is the link visible?
278          */
279         if (_DtCvIsSegVisibleLink(p_seg))
280           {
281             /*
282              * get the visible link metrics
283              */
284             lnkBefore = canvas->link_info.space_before;
285             lnkAfter  = canvas->link_info.space_after;
286           }
287         if (_DtCvIsSegALink(p_seg))
288           {
289             /*
290              * if the last 'link' was really a link, close it out by
291              * leaving room for the traversal and link end indicators
292              */
293             if (lst_hyper != -1)
294                 value += (canvas->traversal_info.space_after + lnkAfter);
295
296             /*
297              * leave space for the traversal/link begin and end
298              * indicators for this segment.
299              */
300             value += (canvas->traversal_info.space_before
301                                 + canvas->traversal_info.space_after
302                                 + lnkBefore
303                                 + lnkAfter);
304           }
305       }
306
307     return value;
308 }
309
310 /******************************************************************************
311  * Function: _DtCvAddLines
312  *
313  * makes sure the last x number of lines are blank.
314  *****************************************************************************/
315 void
316 _DtCvAddSpace (
317     _DtCvUnit            number,
318     _DtCvUnit           *ret_y)
319 {
320     /*
321      * anything to do?
322      */
323     if (0 >= number)
324         return;
325
326     /*
327      * adjust the global Y position to allow the extra room
328      */
329     *ret_y = *ret_y + number;
330 }
331
332 /******************************************************************************
333  * Function:    CheckOneByteCantEndList
334  *
335  * Returns:     True    if the character matches one of the characters in
336  *                      the OneByteCantEndList.
337  *              False   if the character does not match an item in
338  *                      the OneByteCantEndList.
339  *****************************************************************************/
340 _DtCvValue
341 _DtCvCheckOneByteCantEndList (
342     char         c,
343     char        *cant_end_list)
344 {
345     int  i;
346
347     for (i = 0; cant_end_list[i]; i++)
348         if (cant_end_list[i] == c)
349             return True;
350
351     return False;
352 }
353
354 /******************************************************************************
355  * Function:    CheckOneByteCantBeginList
356  *
357  * Returns:     True    if the character matches one of the characters in
358  *                      the OneByteCantBeginList.
359  *              False   if the character does not match an item in
360  *                      the OneByteCantBeginList.
361  *****************************************************************************/
362 _DtCvValue
363 _DtCvCheckOneByteCantBeginList (
364     char         c,
365     char        *cant_begin_list)
366 {
367     int  i;
368
369     for (i = 0; cant_begin_list[i]; i++)
370         if (cant_begin_list[i] == c)
371             return True;
372
373     return False;
374 }
375
376 /******************************************************************************
377  * Function:    CheckLineSyntax
378  *
379  * Returns:     True    if the segment can end a line.
380  *              False   if the segment can not end a line.
381  *
382  * Purpose:     Checks the line syntax. Will not allow a segment to end
383  *              a line if:
384  *                      the segment does not end with a hypen.
385  *                      the segment does not end with a space and the
386  *                              next segment does not begin with a space.
387  *                      the segment ends with a two byte characters that
388  *                              can not end a line.
389  *                      The next segment starts with a two byte character
390  *                              that can not begin a line.
391  *                      the segment ends with an one-byte open type and
392  *                              the next segment starts with a
393  *                              two byte character.
394  *                      the segment ends with a two byte character and
395  *                              the next segment starts with a one-byte
396  *                              close type.
397  *                      the next segment is a non-breaking string or region.
398  *
399  *****************************************************************************/
400 _DtCvValue
401 _DtCvCheckLineSyntax (
402     _DtCanvasStruct      *canvas,
403     _DtCvSegmentI        *pSeg,
404     int                   start,
405     int                   str_len,
406     _DtCvValue            skip_hypen_ck)
407 {
408     int     myStrLen;
409     int     wcFlag;
410     void    *pChar;
411     wchar_t  nextChar;
412     wchar_t  lastChar;
413     _DtCvValue lstCharMb = False;
414     _DtCvValue nxtCharMb = False;
415
416     /*
417      * while this is a marker or a noop without a end-of-line, go to the
418      * next segment.
419      */
420     while (NULL != pSeg && (_DtCvIsSegMarker(pSeg) ||
421                                 (_DtCvIsSegNoop(pSeg) && !_DtCvIsSegNewLine(pSeg))))
422         pSeg = pSeg->next_seg;
423
424     /*
425      * if this segment is null or not a string or region, stop the
426      * test right now.
427      */
428     if (pSeg == NULL || !(_DtCvIsSegString(pSeg) || _DtCvIsSegRegion(pSeg)))
429         return True;
430
431     /*
432      * Get the string segment stats
433      */
434     if (_DtCvIsSegString(pSeg))
435       {
436         wcFlag   = _DtCvIsSegWideChar(pSeg);
437         pChar    = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg), wcFlag, start);
438         myStrLen = _DtCvStrLen (pChar, wcFlag);
439       }
440
441     /*
442      * if this is a region or a string segment (at the end of its string)
443      * and it has a newline on it, then it can end a line.
444      */
445     if ((_DtCvIsSegRegion(pSeg) ||
446                         (_DtCvIsSegString(pSeg) && myStrLen == str_len))
447                 && (_DtCvIsSegNewLine (pSeg) || pSeg->next_seg == NULL))
448         return True;
449
450     /*
451      * if this is a region, then check it's breaking flag.
452      */
453     if (_DtCvIsSegRegion(pSeg))
454       {
455         if (_DtCvIsSegNonBreakingChar(pSeg))
456             return False;
457         return True;
458       }
459
460     /*
461      * so, to get this far, this is a string segment.
462      *
463      * Problems with indexing?
464      */
465     if (str_len <= 0)
466         return True;
467
468     /*
469      * do we need to check the last character in the string?
470      * If skip_hypen_ck is true, it means that 'lastChar' would be a hypen.
471      */
472     if (False == skip_hypen_ck)
473       {
474         /*
475          * this region is a string, get its string information.
476          */
477         lastChar = _DtCvChar(pChar, wcFlag, str_len - 1);
478
479         /*
480          * check to make sure the last character is a valid last
481          * character.
482          */
483         if (' ' == lastChar || '-' == lastChar)
484             return True;
485
486         /*
487          * If this string is a multi-byte, check the list of multi-bytes
488          * that can't end a line.  If one is found it can't end a line.
489          */
490         if (wcFlag &&
491                 CheckList(lastChar, canvas->locale.cant_end_chars) == True)
492             return False;
493
494         /*
495          * so at the end of these tests, the last character is
496          *    -) not a blank.
497          *    -) not a hypen.
498          *    -) either a single byte character or multibyte character
499          *       that can end the line (including a single byte in
500          *       wide char form).
501          *
502          * set the flag for the type of character lastChar is.
503          * if skip_hypen_ck was True, then lstCharMb remains False
504          * which is logical since it means that the caller has already
505          * processed a hypen (a single byte character).
506          */
507         lstCharMb = IsTrueMultiByte(lastChar);
508       }
509
510     /*
511      * Check for more characters in the string and
512      * check its next character for breakable space.
513      */
514     if (myStrLen > str_len)
515       {
516         /*
517          * go to the next character.
518          */
519         nextChar = _DtCvChar(pChar, wcFlag, str_len);
520
521         /*
522          * Is it a valid break point?
523          */
524         if (' ' == nextChar)
525             return True;
526
527         /*
528          * set the multibyte flag for the next character
529          */
530         nxtCharMb = IsTrueMultiByte(nextChar);
531
532         /*
533          * If this is wide char string, check the list of multi-byte
534          * that can't begin a line.
535          * 
536          * But only if the last character wasn't a hypen!  Otherwise
537          * it's a character after a hypen and should not be broken on.
538          * 
539          * if this character is in the 'cannot begin line' list, then it
540          * can't be broken on (a return value of true).  (A wide char
541          * encoding of a single byte character should come back as False
542          * as long as the character is not in the list.)
543          *
544          * Have to double check to make sure it is a multibyte
545          * character (want it to go through the CheckMulti list just in
546          * case it's specified in there, before eliminating it).
547          */
548         if (False == skip_hypen_ck && wcFlag
549                 && CheckList(nextChar,canvas->locale.cant_begin_chars) == False
550                 && True == nxtCharMb)
551             return True;
552
553         /*
554          * either the character is after a hypen (starting a line) OR it
555          * is a multibyte character in the 'cannot begin a line' list OR
556          * it is a single byte character.  Therefore, this is a
557          * nonbreakable character.
558          */
559         return False;
560       }
561
562     /*
563      * We were at the last character of the string.
564      * go to the next segment and see if it can start a new line.
565      */
566     do
567       {
568         pSeg = pSeg->next_seg;
569       } while (pSeg != NULL && (_DtCvIsSegMarker(pSeg) ||
570                         (_DtCvIsSegNoop (pSeg) && !(_DtCvIsSegNewLine(pSeg)))));
571     /*
572      * If there isn't another valid segment, then the original segment
573      * can end the line.
574      */
575     if (pSeg == NULL || !(_DtCvIsSegString(pSeg) || _DtCvIsSegRegion(pSeg)))
576         return True;
577
578     /*
579      * if the last if fell through, then pSeg is a string or region.
580      * check to see if you can break on it.
581      */
582     if (_DtCvIsSegNonBreakingChar(pSeg))
583         return False;
584
585     /*
586      * if the last if fell through, then this is a breaking string or
587      * region. Therefore, if a region, you can break on it.
588      */
589     if (_DtCvIsSegRegion(pSeg))
590         return True;
591
592     /*
593      * To get this far, the next segment must be a string.  Check the
594      * first character of the string to see if it can start a new line.
595      */
596     nextChar = _DtCvChar(_DtCvStringOfStringSeg(pSeg),
597                                                 _DtCvIsSegWideChar(pSeg), 0);
598     if (' ' == nextChar)
599             return True;
600
601     /*
602      * If the previous character was a single byte character (or a hypen),
603      * it couldn't end a line. If this is a single byte string, then
604      * this string can't start a line......
605      */
606     if (_DtCvIsSegRegChar(pSeg) &&
607                                 (True == skip_hypen_ck || False == lstCharMb))
608         return False;
609
610     /*
611      * If this is multi-byte, check the list of multi-byte
612      * that can't begin a line.
613      */
614     if (_DtCvIsSegWideChar(pSeg))
615       {
616         /*
617          * plus checking the 'can not begin a line' list, check
618          * if the previous character was a hypen, then this can't be
619          * broken on either.
620          */
621         if (True == skip_hypen_ck ||
622                 CheckList(nextChar, canvas->locale.cant_begin_chars) == True)
623             return False;
624
625         /*
626          * if the previous character was a multi-byte and this
627          * character is a multibyte, then it is a valid break.
628          */
629         nxtCharMb = IsTrueMultiByte(nextChar);
630         if (True == lstCharMb && True == nxtCharMb)
631             return True;
632       }
633
634     /*
635      * if the last character was a single byte character, then there
636      * is still more to check - 1 byte punctuation around multi-byte.
637      */
638     if (False == lstCharMb &&
639         _DtCvCheckOneByteCantEndList((char)lastChar,OneByteCantEndList) == True)
640         return False;
641     
642     /*
643      * or was the last character a multibyte and is followed by single byte
644      * punctuation?
645      */
646     if (True == lstCharMb && False == nxtCharMb &&
647         _DtCvCheckOneByteCantBeginList((char)nextChar, OneByteCantBeginList)
648                                                                         == True)
649         return False;
650
651     return True;
652 }
653
654 /******************************************************************************
655  * Function:    _DtCvGetNextWidth
656  *
657  * Purpose:     Determines the width of the next legal segment.
658  *
659  * Returns:     The width of the next legal segment.
660  *
661  *****************************************************************************/
662 int
663 _DtCvGetNextWidth (
664     _DtCanvasStruct      *canvas,
665     int                   old_type,
666     int                   lst_hyper,
667     _DtCvSegmentI        *pSeg,
668     int                   start,
669     _DtCvSegmentI        *prev_seg,
670     _DtCvSegmentI       **nextSeg,
671     int                  *nextStart,
672     int                  *widthCount)
673 {
674     int      result;
675     int      len = 0;
676     int      tLen;
677     int      wcFlag;
678     int      curWidth;
679     int      myLength;
680     int      nextLen = 0;
681     void    *pChar;
682     char    *tChar;
683     _DtCvValue  good_len;
684
685     /*
686      * pass over noops that don't have newlines and markers
687      */
688     while (pSeg != NULL && (_DtCvIsSegMarker(pSeg) ||
689                         (_DtCvIsSegNoop (pSeg) && !(_DtCvIsSegNewLine(pSeg)))))
690       {
691         pSeg = pSeg->next_seg;
692         start = 0;
693       }
694
695     if (nextSeg != NULL)
696         *nextSeg = pSeg;
697     if (nextStart != NULL)
698         *nextStart = start;
699
700     /*
701      * if the next segment is null or anything else but a string or region;
702      * return that there is no more after this segment.
703      */
704     if (pSeg == NULL || !(_DtCvIsSegString(pSeg) || _DtCvIsSegRegion(pSeg)))
705         return 0;
706
707     /*
708      * this segment is a region or string
709      * check for region...anything left is a string.
710      */
711     if (_DtCvIsSegRegion(pSeg))
712       {
713         /*
714          * can I break on this region
715          */
716         if (_DtCvIsSegNonBreakingChar(pSeg))
717           {
718             /*
719              * no...set the lengths and continue
720              */
721             len      = 1;
722             curWidth = _DtCvWidthOfRegionSeg(pSeg);
723           }
724         else
725             return 0;
726       }
727     /*
728      * is this a non breaking string?
729      */
730     else if (_DtCvIsSegNonBreakingChar(pSeg))
731       {
732         pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg),
733                                         _DtCvIsSegWideChar(pSeg), start);
734         len   = _DtCvStrLen (pChar, _DtCvIsSegWideChar(pSeg));
735
736         curWidth = _DtCvGetStringWidth (canvas, pSeg, pChar, len)
737                         + _DtCvGetTraversalWidth(canvas, pSeg, lst_hyper);
738       }
739     /*
740      * so this is a string with possible breaks in it.
741      */
742     else
743       {
744         /*
745          * get the string stats
746          */
747         wcFlag   = _DtCvIsSegWideChar (pSeg);
748         pChar    = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg), wcFlag, start);
749         myLength = _DtCvStrLen (pChar, wcFlag);
750     
751             /*
752          * if a single byte string, zoom through it looking for
753          * specific breaking characters.
754              */
755         if (0 == wcFlag)
756           {
757             tChar = pChar;
758             len = 0;
759             do
760               {
761                 /*
762                  * checking for a hypen or space
763                  */
764                 good_len = True;
765                 result = _DtCvStrcspn ((void *) tChar, " -", 0, &tLen);
766                 len += tLen;
767     
768                 /*
769                  * check for '-'. Some of the possible combinations are:
770                  *    -text
771                  *    - text
772                  *    -/text/
773                  *    text/-text/
774                  *    text-text
775                  *    text text
776                  *
777                  * if it is the first character to check and there is no
778                  * previous segment, then it is starting a line and can
779                  * not be broken on.
780                  *
781                  * _DtCvStrcpn return 0 if one of the characters in the
782                  * test string was found.
783                  */
784                 if (0 == result && '-' == tChar[tLen] && 0 == len &&
785                     NULL == prev_seg &&
786                     _DtCvCheckLineSyntax(canvas,pSeg,start,1,True) == False)
787                   {
788                     len++;
789                     tLen++;
790                     tChar += tLen;
791                     good_len = False;
792                   }
793               } while (!good_len);
794     
795             /*
796              * found either a space or a hypen or null byte.
797              * If we found a hypen, include it.
798              */
799             if ('-' == *tChar)
800                 len++;
801     
802             curWidth = _DtCvGetStringWidth (canvas, pSeg, pChar, len)
803                         + _DtCvGetTraversalWidth(canvas, pSeg, lst_hyper);
804     
805             /*
806              * Did we find a space or hypen?
807              * If not, can this segment stand alone?
808              */
809             if (result == 0 ||
810                     _DtCvCheckLineSyntax(canvas,pSeg,start,len,False) == True)
811               {
812                 if (nextSeg != NULL)
813                     *nextSeg   = pSeg;
814                 if (nextStart != NULL)
815                     *nextStart = start + len;
816                 if (widthCount != NULL)
817                     *widthCount = len;
818                  return curWidth;
819               }
820           }
821         /*
822          * multibyte (wide char string), look for a break the hard way.
823          */
824         else
825           {
826             len = 0;
827             while (len < myLength)
828               {
829                 len++;
830                 if (_DtCvCheckLineSyntax(canvas,pSeg,start,len,False) == True)
831                   {
832                     pChar    = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg),
833                                         _DtCvIsSegWideChar(pSeg), start);
834                     curWidth = _DtCvGetStringWidth(canvas,pSeg,pChar,len)
835                             + _DtCvGetTraversalWidth(canvas,pSeg,lst_hyper);
836     
837                     if (nextSeg != NULL)
838                         *nextSeg    = pSeg;
839                     if (nextStart != NULL)
840                         *nextStart  = start + len;
841                     if (widthCount != NULL)
842                         *widthCount = len;
843                     return curWidth;
844                   }
845               }
846     
847             /*
848              * Didn't find a smaller segment that satisfied the requirements.
849              * Determine the length of the current segment.
850              */
851             curWidth = _DtCvGetStringWidth (canvas, pSeg, pChar, len)
852                                     + _DtCvGetTraversalWidth(canvas, pSeg,
853                                                         lst_hyper);
854           }
855       }
856
857     /*
858      * sigh...need to go further...this segment can't end a line
859      * either.
860      */
861     prev_seg = pSeg;
862     pSeg = pSeg->next_seg;
863     if (pSeg != NULL)
864       {
865         start = 0;
866         curWidth += _DtCvGetNextWidth (canvas, 
867                         _DtCvPrimaryTypeOfSeg (prev_seg), lst_hyper,
868                                 pSeg, start, prev_seg,
869                                 nextSeg, nextStart, &nextLen);
870
871       }
872
873     if (widthCount != NULL)
874         *widthCount = len + nextLen;
875     return (curWidth);
876 }
877
878 /******************************************************************************
879  * Function: _DtCvSaveInfo
880  *
881  * Initializes a line table element to the segment it should display.
882  *****************************************************************************/
883 void
884 _DtCvSaveInfo (
885     _DtCanvasStruct     *canvas,
886     _DtCvLayoutInfo     *layout,
887     _DtCvUnit            max_width,
888     _DtCvUnit            r_margin,
889     _DtCvFrmtOption      txt_justify)
890 {
891     /*****************************************************************
892      * The ascent for a line is described as the number of units
893      * above the baseline.
894      *
895      * The descent for a line is described as the number of units
896      * below the baseline.
897      * 
898      * Neither the ascent or decent value includes the baseline
899      ****************************************************************/
900     int    len;
901     int    start      = layout->line_start;
902     int    count      = layout->line_bytes;
903     long   txtCnt     = canvas->txt_cnt;
904     _DtCvUnit   maxAscent  = 0;
905     _DtCvUnit   maxDescent = 0;
906     _DtCvUnit   maxRegion  = 0;
907     _DtCvUnit   superY     = 0;
908     _DtCvUnit   subY       = 0;
909     _DtCvUnit   fontAscent;
910     _DtCvUnit   fontDescent;
911     _DtCvValue fndLnk = False;
912     _DtCvValue visLnk = False;
913
914     void  *pChar;
915
916     _DtCvSegmentI       *pSeg = layout->line_seg;
917
918     if (txtCnt >= canvas->txt_max)
919       {
920         canvas->txt_max += GROW_SIZE;
921         if (canvas->txt_lst)
922             canvas->txt_lst = (_DtCvDspLine *) realloc (
923                                 (void *) canvas->txt_lst,
924                                 (sizeof(_DtCvDspLine) * canvas->txt_max));
925         else
926             canvas->txt_lst = (_DtCvDspLine *) malloc (
927                                 (sizeof(_DtCvDspLine) * canvas->txt_max));
928 /*
929  * NOTE....should this routine return a value?
930  * If (re)alloc error occurs, this simply ignores the problem.
931  */
932         if (canvas->txt_lst == NULL)
933           {
934             canvas->txt_max = 0;
935             canvas->txt_cnt = 0;
936             return;
937           }
938       }
939
940     while (pSeg != NULL && count > 0)
941       {
942         /*
943          * set which line will this segment sit on, iff this is the
944          * first access to the segment.
945          */
946         if ((void *) -1 == pSeg->internal_use)
947             pSeg->internal_use = (void *) txtCnt;
948         
949         /*
950          * now get the segment's sizing so we can determine
951          * the height and depth of the line.
952          */
953         len = 1;
954         fontAscent  = 0;
955         fontDescent = 0;
956
957         if (_DtCvIsSegVisibleLink(pSeg))
958             visLnk = True;
959
960         if (_DtCvIsSegALink(pSeg))
961             fndLnk = True;
962
963         /*
964          * get the ascent and descent of the segment along with a length
965          */
966         if (_DtCvIsSegString(pSeg))
967           {
968             _DtCvFontMetrics(canvas,_DtCvFontOfStringSeg(pSeg),
969                         &fontAscent, &fontDescent, NULL, NULL, NULL);
970
971             pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(pSeg),
972                                         _DtCvIsSegWideChar(pSeg), start);
973             len   = _DtCvStrLen (pChar, _DtCvIsSegWideChar(pSeg));
974
975             if (len > count)
976                 len = count;
977           }
978         else if (_DtCvIsSegRegion(pSeg))
979           {
980             if (-1 == _DtCvAscentOfRegionSeg(pSeg))
981               {
982                 if (maxRegion < _DtCvHeightOfRegionSeg(pSeg))
983                     maxRegion = _DtCvHeightOfRegionSeg(pSeg);
984               }
985             else
986               {
987                 fontAscent  = _DtCvAscentOfRegionSeg(pSeg);
988                 fontDescent = _DtCvHeightOfRegionSeg(pSeg) - fontAscent;
989               }
990           }
991
992         /*
993          * adjust the ascent and descent values by their subscript
994          * or superscript adjustments.
995          */
996         if (_DtCvIsSegSuperScript(pSeg))
997           {
998             fontAscent  += superY;
999             fontDescent -= superY;
1000
1001             if (_DtCvIsSegRegion(pSeg) && -1 == _DtCvAscentOfRegionSeg(pSeg)
1002                 && maxRegion < _DtCvHeightOfRegionSeg(pSeg) + superY)
1003                 maxRegion = _DtCvHeightOfRegionSeg(pSeg) + superY;
1004           }
1005         else if (_DtCvIsSegSubScript(pSeg))
1006           {
1007             fontAscent  -= subY;
1008             fontDescent += subY;
1009             if (_DtCvIsSegRegion(pSeg) && -1 == _DtCvAscentOfRegionSeg(pSeg)
1010                 && maxRegion < _DtCvHeightOfRegionSeg(pSeg) + subY)
1011                 maxRegion = _DtCvHeightOfRegionSeg(pSeg) + subY;
1012           }
1013         else /* not a subscript or superscript */
1014           {
1015             /*
1016              * set up the super and sub script offsets for following
1017              * segments.
1018              */
1019             if (_DtCvIsSegString (pSeg))
1020                 _DtCvFontMetrics(canvas,_DtCvFontOfStringSeg(pSeg),
1021                                         NULL, NULL, NULL, &superY, &subY);
1022             else if (_DtCvIsSegRegion(pSeg))
1023               {
1024                 superY = _DtCvHeightOfRegionSeg(pSeg) * 4 / 10;
1025                 subY   = superY;
1026               }
1027           }
1028
1029         /*
1030          * now determine the maximums for ascent and descent.
1031          */
1032         if (fontAscent > maxAscent)
1033             maxAscent = fontAscent;
1034         if (fontDescent > maxDescent)
1035             maxDescent = fontDescent;
1036
1037         /*
1038          * decrement the count
1039          */
1040         count -= len;
1041
1042         /*
1043          * If this segment terminates the paragraph
1044          * force the end of the loop.
1045          */
1046         pSeg  = pSeg->next_disp;
1047         start = 0;
1048       }
1049
1050     if (txt_justify == _DtCvJUSTIFY_RIGHT || _DtCvJUSTIFY_CENTER == txt_justify)
1051       {
1052         /*
1053          * justify the line.
1054          */
1055         _DtCvUnit   workWidth = max_width - layout->text_x_pos -
1056                                                 r_margin - layout->cur_len;
1057         if (txt_justify == _DtCvJUSTIFY_CENTER)
1058             workWidth = workWidth / 2;
1059
1060         if (workWidth < 0)
1061             workWidth = 0;
1062
1063         layout->text_x_pos += workWidth;
1064       }
1065
1066     /*
1067      * adjust for any special characters found
1068      */
1069     if (maxRegion > maxAscent + maxDescent + 1)
1070         maxAscent = maxRegion - maxDescent - 1;
1071
1072     /*
1073      * check to see if the max values have even been touched.
1074      */
1075     if (layout->line_bytes == 0 && maxAscent == 0 && maxDescent == 0)
1076         maxAscent = canvas->metrics.line_height;
1077
1078     /*
1079      * adjust ascent and descent by the traversal and link info
1080      */
1081     maxDescent += layout->leading;
1082     if (fndLnk)
1083       {
1084         maxAscent  += canvas->traversal_info.space_above;
1085         maxDescent += canvas->traversal_info.space_below;
1086         if (visLnk)
1087           {
1088             maxAscent  += canvas->link_info.space_above;
1089             maxDescent += canvas->link_info.space_below;
1090           }
1091       }
1092
1093     /*
1094      * save the line information, if there is a string here.
1095      */
1096     if (layout->line_bytes > 0)
1097       {
1098         canvas->txt_lst[txtCnt].processed  = _DtCvFALSE;
1099         canvas->txt_lst[txtCnt].text_x     = layout->text_x_pos;
1100         canvas->txt_lst[txtCnt].max_x      = layout->text_x_pos;
1101         canvas->txt_lst[txtCnt].baseline   = layout->y_pos + maxAscent;
1102         canvas->txt_lst[txtCnt].descent    = maxDescent;
1103         canvas->txt_lst[txtCnt].ascent     = maxAscent;
1104         canvas->txt_lst[txtCnt].byte_index = layout->line_start;
1105         canvas->txt_lst[txtCnt].length     = layout->line_bytes;
1106         canvas->txt_lst[txtCnt].seg_ptr    = layout->line_seg;
1107
1108         canvas->txt_cnt++;
1109       }
1110     /*
1111      * blank line is one half the normal size line
1112      */
1113     else
1114       {
1115         maxAscent  = (maxAscent + maxDescent) / 2;
1116         maxDescent = 0;
1117       }
1118
1119     if (layout->text_x_pos + layout->cur_len > layout->cur_max_x)
1120         layout->cur_max_x = layout->text_x_pos + layout->cur_len;
1121
1122     if (layout->text_x_pos + layout->cur_len > layout->max_x_pos)
1123         layout->max_x_pos = layout->text_x_pos + layout->cur_len;
1124
1125     /*
1126      * zero the string info
1127      */
1128     layout->line_bytes = 0;
1129     layout->cur_len    = 0;
1130     layout->lst_hyper  = -1;
1131     layout->lst_vis    = False;
1132
1133     _DtCvSetJoinInfo(layout, False, -1);
1134
1135     /*
1136      * adjust where the next line is positioned.
1137      */
1138     layout->y_pos = layout->y_pos + maxAscent + maxDescent + 1;
1139 }
1140
1141 /******************************************************************************
1142  * Function: _DtCvCheckAddHyperToTravList
1143  *
1144  *****************************************************************************/
1145 void
1146 _DtCvCheckAddHyperToTravList (
1147     _DtCanvasStruct     *canvas,
1148     _DtCvSegmentI       *p_seg,
1149     _DtCvValue           flag,
1150     _DtCvValue          *lst_vis,
1151     int                 *lst_hyper,
1152     _DtCvUnit           *cur_len)
1153 {
1154     int   nxtHyper;
1155     int   prevIdx;
1156     _DtCvValue  junk;
1157     _DtCvUnit   retLen = *cur_len;
1158
1159     if (_DtCvIsSegALink (p_seg))
1160       {
1161         nxtHyper = _DtCvGetNextTravEntry(canvas);
1162         if (-1 == nxtHyper)
1163 /*
1164  * NOTE....should this routine return a value?
1165  * If (re)alloc error occurs, this simply ignores the problem.
1166  */
1167                 return;
1168
1169         prevIdx = nxtHyper - 1;
1170         if (prevIdx < 0
1171              || _DtCvTraversalLink != canvas->trav_lst[prevIdx].type
1172              || p_seg->link_idx != canvas->trav_lst[prevIdx].seg_ptr->link_idx)
1173           {
1174             /*
1175              * save this hypertext link in the traversal list
1176              */
1177             _DtCvSetTravEntryInfo (canvas, nxtHyper, _DtCvTraversalLink, p_seg,
1178                                         canvas->txt_cnt, _DtCvTRUE);
1179           }
1180       }
1181
1182     /*
1183      * take into account the link metrics.
1184      */
1185     junk = _DtCvIsSegVisibleLink(p_seg);
1186     *lst_vis = _DtCvModifyXpos(canvas->link_info, p_seg, junk,
1187                         *lst_vis, *lst_hyper,
1188                         &retLen);
1189     /*
1190      * take into account the traversal metrics
1191      */
1192     junk = _DtCvIsSegALink(p_seg);
1193     (void) _DtCvModifyXpos(canvas->traversal_info, p_seg, junk,
1194                         ((_DtCvValue) True), *lst_hyper,
1195                         &retLen);
1196
1197     *lst_hyper = p_seg->link_idx;
1198
1199     if (_DtCvTRUE == flag)
1200         *cur_len = retLen;
1201 }
1202
1203 /******************************************************************************
1204  * Function: ProcessStringSegment
1205  *
1206  * chops a string segment up until its completely used.
1207  *
1208  * Returns:
1209  *      0       if the entire string segment was processed.
1210  *      1       if the required number of lines were processed.
1211  *****************************************************************************/
1212 int
1213 _DtCvProcessStringSegment(
1214     _DtCanvasStruct     *canvas,
1215     _DtCvLayoutInfo     *lay_info,
1216     _DtCvUnit            max_width,
1217     _DtCvUnit            l_margin,
1218     _DtCvUnit            r_margin,
1219     _DtCvSegmentI       *cur_seg,
1220     unsigned int        *cur_start,
1221     _DtCvFrmtOption      txt_justify,
1222     _DtCvValue           stat_flag)
1223 {
1224     _DtCvUnit   workWidth;
1225     _DtCvUnit   stringLen;
1226     _DtCvUnit   textWidth;
1227     _DtCvUnit   nWidth;
1228     _DtCvUnit   spaceSize = 0;
1229     int    oldType;
1230     int    retStart;
1231     int    retCount;
1232     wchar_t      *wcp;
1233     void         *pChar;
1234     char         *strPtr;
1235     _DtCvValue    done    = False;
1236     _DtCvSegmentI *retSeg;
1237
1238     if (NULL != _DtCvStringOfStringSeg(cur_seg))
1239       {
1240         if (lay_info->cur_len == 0)
1241           {
1242             lay_info->line_seg   = cur_seg;
1243             lay_info->line_start = *cur_start;
1244           }
1245
1246         if (*cur_start == 0 && (cur_seg->type & _DtCvSEARCH_FLAG))
1247             lay_info->delayed_search_saves++;
1248
1249         oldType = _DtCvPrimaryTypeOfSeg (cur_seg);
1250
1251         /*
1252          * is alignment in effect?
1253          */
1254         if (TRUE == lay_info->align_flag)
1255           {
1256             pChar     = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
1257                                 _DtCvIsSegWideChar(cur_seg), *cur_start);
1258             nWidth = _DtCvStrcspn (pChar, lay_info->align_char,
1259                                         _DtCvIsSegWideChar(cur_seg),
1260                                         &stringLen);
1261             if (-1 == nWidth)
1262                 return -1;
1263
1264             /*
1265              * we got a valid length back, calculate the length
1266              */
1267             textWidth = 0;
1268             if (0 != stringLen)
1269                 textWidth = _DtCvGetStringWidth(canvas,cur_seg,pChar,stringLen);
1270
1271             /*
1272              * check to see if this a hypertext that needs
1273              * to be remembered.
1274              */
1275             _DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvTRUE,
1276                                         &(lay_info->lst_vis),
1277                                         &(lay_info->lst_hyper),
1278                                         &(lay_info->cur_len));
1279
1280             /*
1281              * update the length and position information
1282              * to skip past the characters before the alignment character.
1283              */
1284             lay_info->line_bytes += stringLen;
1285             lay_info->cur_len    += (textWidth
1286                                         + _DtCvGetTraversalWidth(canvas,
1287                                                 cur_seg, lay_info->lst_hyper));
1288
1289             *cur_start += stringLen;
1290
1291             /*
1292              * if we didn't find the character, check to see if this forces
1293              * a newline - honor it if it does. We'll check the next
1294              * string segment for the alignment character.
1295              */
1296             if (1 == nWidth && _DtCvIsSegNewLine (cur_seg)
1297                                                 && lay_info->line_bytes)
1298               {
1299                 _DtCvSaveInfo (canvas,lay_info,max_width,r_margin,txt_justify);
1300
1301                 while (lay_info->delayed_search_saves > 0) {
1302                     _DtCvSetSearchEntryInfo(canvas, canvas->txt_cnt - 1);
1303                     lay_info->delayed_search_saves--;
1304                 }
1305                 return 0;
1306               }
1307
1308             /*
1309              * so we found the character, now get it's width.
1310              */
1311             pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
1312                                 _DtCvIsSegWideChar(cur_seg), *cur_start);
1313             textWidth = _DtCvGetStringWidth(canvas, cur_seg, pChar, 1)
1314                         + _DtCvGetTraversalWidth(canvas, cur_seg,
1315                                         lay_info->lst_hyper);
1316             /*
1317              * is this the second or more align position?
1318              * if so, need to shift the character to align with others.
1319              */
1320             if (lay_info->align_pos >
1321                       lay_info->text_x_pos + lay_info->cur_len + textWidth / 2)
1322                 lay_info->text_x_pos = lay_info->align_pos - lay_info->cur_len
1323                                         - textWidth / 2;
1324             /*
1325              * otherwise, does this exceed the previous alignments?
1326              * if so, the table processing should catch that we've
1327              * changed the alignment position and re-format the others.
1328              */
1329             else if (lay_info->align_pos <
1330                       lay_info->text_x_pos + lay_info->cur_len + textWidth / 2)
1331                 lay_info->align_pos =
1332                       lay_info->text_x_pos + lay_info->cur_len + textWidth / 2;
1333
1334             /*
1335              * indicate that the character has been found.
1336              */
1337             lay_info->align_flag = False;
1338
1339             /*
1340              * check to see if this item can end a line.
1341              * if can't end the line, force a join for the next segment or
1342              * for the rest of this segment.
1343              */
1344             if (False == _DtCvCheckLineSyntax(canvas,cur_seg,*cur_start,1,False))
1345                 lay_info->join = True;
1346
1347             /*
1348              * update the length and position information to
1349              * include the character.
1350              */
1351             lay_info->line_bytes++;
1352             lay_info->cur_len += textWidth;
1353
1354             *cur_start += 1;
1355
1356             /*
1357              * check to see if this is the end of the segment.
1358              */
1359             pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
1360                                 _DtCvIsSegWideChar(cur_seg), *cur_start);
1361             if ((_DtCvIsSegWideChar(cur_seg) && 0 == *((wchar_t *) pChar))
1362                                 ||
1363                 (_DtCvIsSegRegChar(cur_seg) && '\0' == *((char *) pChar)))
1364                 return 0;
1365           }
1366
1367         while (1)
1368           {
1369             /*
1370              * recalculate the width
1371              */
1372             workWidth = max_width - lay_info->text_x_pos -
1373                                                 lay_info->cur_len - r_margin;
1374
1375             /*
1376              * adjust the character pointer and get the
1377              * length of the string.
1378              */
1379             pChar     = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
1380                                 _DtCvIsSegWideChar(cur_seg), *cur_start);
1381             stringLen = _DtCvStrLen (pChar, _DtCvIsSegWideChar(cur_seg));
1382     
1383             /*
1384              * get the pixel width of the text string.
1385              */
1386             textWidth = _DtCvGetStringWidth(canvas,cur_seg,pChar,stringLen)
1387                         + _DtCvGetTraversalWidth(canvas, cur_seg,
1388                                         lay_info->lst_hyper);
1389             /*
1390              * Will it fit in the current width?
1391              */
1392             if (stat_flag == True || textWidth <= workWidth)
1393               {
1394                 /*
1395                  * Yes, this segment or part of a segment can fit in the
1396                  * current width. But can the last character of this
1397                  * segment end a line and can the beginning of the next
1398                  * segment start a new line?
1399                  */
1400                 if (stat_flag == True ||
1401                         _DtCvCheckLineSyntax (canvas, cur_seg,
1402                                         *cur_start, stringLen, False) == TRUE)
1403                   {
1404                     /*
1405                      * check to see if this a hypertext that needs
1406                      * to be remembered.
1407                      */
1408                     _DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvFALSE,
1409                                         &(lay_info->lst_vis),
1410                                         &(lay_info->lst_hyper),
1411                                         &(lay_info->cur_len));
1412
1413                     /*
1414                      * The line syntax is good.
1415                      * Update the global and width variables.
1416                      */
1417                     lay_info->line_bytes += stringLen;
1418                     lay_info->cur_len    += textWidth;
1419                     _DtCvSetJoinInfo(lay_info,
1420                                         _DtCvIsSegNonBreakingChar(cur_seg),
1421                                         -1);
1422
1423                     /*
1424                      * Check to see if this segment forces an end
1425                      */
1426                     if (_DtCvIsSegNewLine (cur_seg) && lay_info->line_bytes) {
1427                         _DtCvSaveInfo (canvas, lay_info, max_width,
1428                                                         r_margin, txt_justify);
1429                         while (lay_info->delayed_search_saves > 0) {
1430                             _DtCvSetSearchEntryInfo(canvas,
1431                                                     canvas->txt_cnt - 1);
1432                             lay_info->delayed_search_saves--;
1433                         }
1434                     }
1435
1436                     return 0;
1437                   }
1438     
1439                 /*
1440                  * CheckLineSyntax says that either this line couldn't
1441                  * end a line or the next segment couldn't start a line.
1442                  * Therefore, find out how much of the next segment or
1443                  * segments we need to incorporate to satisfy the Line
1444                  * Syntax rules.
1445                  */
1446                 nWidth = _DtCvGetNextWidth (canvas, oldType,
1447                                 lay_info->lst_hyper,
1448                                 cur_seg->next_seg, 0, cur_seg,
1449                                 &retSeg, &retStart, &retCount);
1450                 /*
1451                  * will this segment + the next segment fit?
1452                  */
1453                 if (textWidth + nWidth <= workWidth)
1454                   {
1455                     /*
1456                      * check to see if this a hypertext that needs
1457                      * to be remembered.
1458                      */
1459                     _DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvFALSE,
1460                                         &(lay_info->lst_vis),
1461                                         &(lay_info->lst_hyper),
1462                                         &(lay_info->cur_len));
1463
1464                     /*
1465                      * YEAH Team!! It Fits!!
1466                      *
1467                      * Update the global and width variables.
1468                      */
1469                     lay_info->line_bytes += stringLen;
1470                     lay_info->cur_len    += textWidth;
1471                     _DtCvSetJoinInfo(lay_info, False, -1);
1472     
1473                     return 0;
1474                   }
1475               }
1476     
1477             /*
1478              * the text width plus the next segment is tooo big
1479              * to fit. Reduce the current segment if possible
1480              */
1481             done = False;
1482             textWidth = 0;
1483             stringLen = 0;
1484             while (!done)
1485               {
1486                 nWidth = _DtCvGetNextWidth (canvas, oldType,
1487                                 lay_info->lst_hyper,
1488                                 cur_seg, *cur_start, NULL,
1489                                 &retSeg, &retStart, &retCount);
1490
1491                 if (retSeg == cur_seg && textWidth + nWidth <= workWidth)
1492                   {
1493                     /*
1494                      * check to see if this a hypertext that needs
1495                      * to be remembered.
1496                      */
1497                     _DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvFALSE,
1498                                         &(lay_info->lst_vis),
1499                                         &(lay_info->lst_hyper),
1500                                         &(lay_info->cur_len));
1501
1502                     _DtCvSetJoinInfo(lay_info, False, -1);
1503                     *cur_start     = retStart;
1504                     stringLen     += retCount;
1505                     textWidth     += nWidth;
1506                     spaceSize      = 0;
1507
1508                     /*
1509                      * take into account a space if that is where it breaks.
1510                      */
1511                     pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
1512                                                 _DtCvIsSegWideChar(cur_seg),
1513                                                 *cur_start);
1514                     if ((_DtCvIsSegWideChar(cur_seg) &&
1515                                                 (' ' == *((wchar_t *) pChar)))
1516                                         ||
1517                         (_DtCvIsSegRegChar(cur_seg) &&
1518                                                 (' ' == *((char *) pChar))))
1519                       {
1520                             spaceSize = _DtCvGetStringWidth(canvas,
1521                                                 cur_seg, pChar, 1)
1522                                         + _DtCvGetTraversalWidth (canvas,
1523                                                 cur_seg, lay_info->lst_hyper);
1524                             textWidth += spaceSize;
1525                             stringLen++;
1526                             (*cur_start)++;
1527                           }
1528                       }
1529                 else
1530                   {
1531                     /*
1532                      * Done trying to find a segment that will
1533                      * fit in the size given
1534                      */
1535                     done = True;
1536                   }
1537               }
1538
1539             /*
1540              * Update the global variables
1541              */
1542             lay_info->line_bytes += stringLen;
1543             lay_info->cur_len  += textWidth;
1544
1545             if (lay_info->join == True || lay_info->line_bytes == 0)
1546               {
1547                 /*
1548                  * This line would be empty if we followed the rules.
1549                  * Or it would break a line improperly.
1550                  * Force this onto the line.
1551                  * check to see if this a hypertext that needs
1552                  * to be remembered.
1553                  */
1554                 _DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvTRUE,
1555                                         &(lay_info->lst_vis),
1556                                         &(lay_info->lst_hyper),
1557                                         &(lay_info->cur_len));
1558
1559                 /*
1560                  * Couldn't find a smaller, have to
1561                  * go with the larger segment.
1562                  */
1563                 pChar      = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
1564                                                 _DtCvIsSegWideChar(cur_seg),
1565                                                 *cur_start);
1566                 stringLen  = _DtCvStrLen (pChar, _DtCvIsSegWideChar(cur_seg));
1567                 if (retCount > 0 && retCount < stringLen)
1568                     stringLen = retCount;
1569
1570                 lay_info->line_bytes += stringLen;
1571                 lay_info->cur_len  += (_DtCvGetStringWidth(canvas, cur_seg,
1572                                                         pChar, stringLen)
1573                                         + _DtCvGetTraversalWidth (canvas,
1574                                         cur_seg, lay_info->lst_hyper));
1575
1576                 _DtCvSetJoinInfo(lay_info, False, -1);
1577
1578                 /*
1579                  * If we had to do a bigger segment,
1580                  * then we're done processing the target segment.
1581                  */
1582                 if (stringLen == _DtCvStrLen(pChar,_DtCvIsSegWideChar(cur_seg)))
1583                   {
1584                     if (_DtCvCheckLineSyntax (canvas, cur_seg,
1585                                 *cur_start, stringLen, False) == False)
1586                         _DtCvSetJoinInfo(lay_info, True, -1);
1587                     else if (_DtCvIsSegNewLine (cur_seg)) {
1588                         _DtCvSaveInfo (canvas, lay_info, max_width,
1589                                                         r_margin, txt_justify);
1590                         while (lay_info->delayed_search_saves > 0) {
1591                             _DtCvSetSearchEntryInfo(canvas,
1592                                                     canvas->txt_cnt - 1);
1593                             lay_info->delayed_search_saves--;
1594                         }
1595                     }
1596                     return 0;
1597                   }
1598
1599                 *cur_start     = retStart;
1600               }
1601             else if (spaceSize)
1602               {
1603                 /*
1604                  * If a space was included as the last character,
1605                  * remove it now.
1606                  */
1607                 lay_info->line_bytes--;
1608                 lay_info->cur_len -= spaceSize;
1609               }
1610
1611             /*
1612              * Save the information
1613              */
1614             _DtCvSaveInfo (canvas, lay_info, max_width, r_margin, txt_justify);
1615             if (*cur_start == 0 && (cur_seg->type & _DtCvSEARCH_FLAG))
1616                 lay_info->delayed_search_saves--;
1617
1618             while (lay_info->delayed_search_saves > 0) {
1619                 _DtCvSetSearchEntryInfo(canvas, canvas->txt_cnt - 1);
1620                 lay_info->delayed_search_saves--;
1621             }
1622
1623             /*
1624              * Skip the spaces.
1625              */
1626             pChar = _DtCvStrPtr(_DtCvStringOfStringSeg(cur_seg),
1627                                                 _DtCvIsSegWideChar(cur_seg),
1628                                                 *cur_start);
1629             if (_DtCvIsSegWideChar(cur_seg))
1630               {
1631                 wcp = pChar;
1632                 while (' ' == *wcp)
1633                   {
1634                     wcp++;
1635                     (*cur_start)++;
1636                   }
1637
1638                 pChar = wcp;
1639               }
1640             else /* single byte string */
1641               {
1642                 strPtr = pChar;
1643                 while (' ' == *strPtr)
1644                   {
1645                     strPtr++;
1646                     (*cur_start)++;
1647                   }
1648
1649                 pChar = strPtr;
1650               }
1651
1652             /*
1653              * are we at the end of the segment?
1654              */
1655             if ((_DtCvIsSegWideChar(cur_seg) && 0 == *((wchar_t *) pChar))
1656                         ||
1657                 (_DtCvIsSegRegChar(cur_seg) && 0 == *((char *) pChar)))
1658                 return 0;
1659
1660             if (*cur_start == 0 && (cur_seg->type & _DtCvSEARCH_FLAG))
1661                 lay_info->delayed_search_saves++;
1662
1663             /*
1664              * Initialize the global variables
1665              */
1666             lay_info->line_seg   = cur_seg;
1667             lay_info->line_start = *cur_start;
1668             lay_info->text_x_pos = l_margin;
1669
1670             if (CheckFormat(lay_info) == True)
1671                 return 1;
1672
1673             /*
1674              * check to see if this a hypertext that needs
1675              * to be remembered.
1676              */
1677             _DtCvCheckAddHyperToTravList (canvas, cur_seg, _DtCvTRUE,
1678                                         &(lay_info->lst_vis),
1679                                         &(lay_info->lst_hyper),
1680                                         &(lay_info->cur_len));
1681           }
1682       }
1683     else if (_DtCvIsSegNewLine (cur_seg))
1684       {
1685         /*
1686          * Force a save - even if it is an empty line.
1687          */
1688         _DtCvSaveInfo (canvas, lay_info, max_width, r_margin, txt_justify);
1689         while (lay_info->delayed_search_saves > 0) {
1690             _DtCvSetSearchEntryInfo(canvas, canvas->txt_cnt - 1);
1691             lay_info->delayed_search_saves--;
1692         }
1693       }
1694
1695     return 0;
1696
1697 } /* End _DtCvProcessStringSegment */
1698
1699 /******************************************************************************
1700  * Function:    _DtCvSetJoinInfo
1701  *
1702  * Returns:     sets the joining information to the given information.
1703  *
1704  *****************************************************************************/
1705 void
1706 _DtCvSetJoinInfo (
1707     _DtCvLayoutInfo     *lay_info,
1708     _DtCvValue           flag,
1709     int                  txt_ln)
1710 {
1711     lay_info->join      = flag;
1712     lay_info->join_line = txt_ln;
1713 }
1714
1715 /******************************************************************************
1716  * Function:    _DtCvGetNextTravEntry
1717  *
1718  * Returns:      >= 0 if success,
1719  *              -1 if failure.
1720  *
1721  * Purpose:     Return the next available entry in the traversal list.
1722  *
1723  *****************************************************************************/
1724 int
1725 _DtCvGetNextTravEntry (
1726     _DtCanvasStruct  *canvas)
1727 {
1728     int  nxtEntry = canvas->trav_cnt;
1729
1730     /*
1731      * does the list need to grow?
1732      */
1733     if (nxtEntry >= canvas->trav_max)
1734       {
1735         /*
1736          * grow by a set amount
1737          */
1738         canvas->trav_max += GROW_SIZE;
1739
1740         /*
1741          * realloc or malloc?
1742          */
1743         if (NULL != canvas->trav_lst)
1744             canvas->trav_lst = (_DtCvTraversalInfo *) realloc (
1745                         (char *) canvas->trav_lst,
1746                         ((sizeof(_DtCvTraversalInfo)) * canvas->trav_max));
1747         else
1748             canvas->trav_lst = (_DtCvTraversalInfo *) malloc (
1749                         ((sizeof(_DtCvTraversalInfo)) * canvas->trav_max));
1750
1751         /*
1752          * did the memory allocation work? if not return error code.
1753          */
1754         if (NULL == canvas->trav_lst)
1755           {
1756             canvas->trav_max = 0;
1757             canvas->trav_cnt = 0;
1758             nxtEntry = -1;
1759           }
1760       }
1761
1762     canvas->trav_lst[nxtEntry] = DefTravData;
1763
1764     return nxtEntry;
1765 }
1766
1767 /******************************************************************************
1768  * Function:    _DtCvSetTravEntryInfo
1769  *
1770  * Returns:      0 if success,
1771  *              -1 if failure.
1772  *
1773  * Purpose:     Set the high level information in an entry of the traversal
1774  *              list.
1775  *****************************************************************************/
1776 int
1777 _DtCvSetTravEntryInfo (
1778     _DtCanvasStruct     *canvas,
1779     int                  entry,
1780     _DtCvTraversalType   type,
1781     _DtCvSegmentI       *p_seg,
1782     int                  line_idx,
1783     _DtCvValue           inc)
1784 {
1785     int  result = -1;
1786
1787     if (-1 != entry && entry <= canvas->trav_cnt)
1788       {
1789         _DtCvTraversalInfo *travEntry = &(canvas->trav_lst[entry]);
1790
1791         travEntry->type    = type;
1792         travEntry->seg_ptr = p_seg;
1793         travEntry->idx     = line_idx;
1794
1795         if (_DtCvTRUE == inc)
1796             canvas->trav_cnt++;
1797
1798         result = 0;
1799       }
1800
1801     return result;
1802 }
1803
1804 int
1805 _DtCvGetNextSearchEntry(_DtCanvasStruct* canvas)
1806 {
1807     if (canvas->search_cnt >= canvas->search_max) {
1808         canvas->search_max += GROW_SIZE;
1809
1810         if (canvas->searchs)
1811             canvas->searchs = (_DtCvSearchData *)
1812                         realloc((void*)canvas->searchs,
1813                                 canvas->search_max * sizeof(_DtCvSearchData));
1814         else
1815             canvas->searchs = (_DtCvSearchData *)
1816                         malloc(canvas->search_max * sizeof(_DtCvSearchData));
1817     }
1818
1819     canvas->searchs[canvas->search_cnt].idx = -1;
1820
1821     return canvas->search_cnt++;
1822 }
1823
1824 int
1825 _DtCvSetSearchEntryInfo(_DtCanvasStruct* canvas, int line_idx)
1826 {
1827     int search_idx;
1828
1829     /* get a next available slot for search */
1830     search_idx = _DtCvGetNextSearchEntry(canvas);
1831
1832     /* save information (i.e. line_idx) */
1833     canvas->searchs[search_idx].idx = line_idx;
1834 }
1835
1836 /******************************************************************************
1837  * Function:    _DtCvSetTravEntryPos
1838  *
1839  * Returns:      0 if success,
1840  *              -1 if failure.
1841  *
1842  * Purpose:     Set the position and dimension information of an entry in
1843  *              the traversal list.
1844  *
1845  *****************************************************************************/
1846 int
1847 _DtCvSetTravEntryPos (
1848     _DtCanvasStruct     *canvas,
1849     int                  entry,
1850     _DtCvUnit            x,
1851     _DtCvUnit            y,
1852     _DtCvUnit            width,
1853     _DtCvUnit            height)
1854 {
1855     int  result = -1;
1856
1857     if (-1 != entry && entry <= canvas->trav_cnt)
1858       {
1859         _DtCvTraversalInfo *travEntry = &(canvas->trav_lst[entry]);
1860
1861         travEntry->x_pos  = x;
1862         travEntry->y_pos  = y;
1863         travEntry->width  = width;
1864         travEntry->height = height;
1865
1866         result = 0;
1867       }
1868
1869     return result;
1870 }
1871
1872 /******************************************************************************
1873  * Function:    _DtCvCalcMarkPos
1874  *
1875  * Returns:      0 if success,
1876  *              -1 if failure.
1877  *
1878  * Purpose:     Calcalate the position and dimension information of a mark.
1879  *
1880  *****************************************************************************/
1881 int
1882 _DtCvCalcMarkPos (
1883     _DtCanvasStruct     *canvas,
1884     int                  entry,
1885     _DtCvUnit           *ret_x,
1886     _DtCvUnit           *ret_y,
1887     _DtCvUnit           *ret_width,
1888     _DtCvUnit           *ret_height)
1889 {
1890     int            result = -1;
1891
1892     if (-1 != entry && entry <= canvas->mark_cnt)
1893       {
1894         _DtCvMarkData *mark = &(canvas->marks[entry]);
1895
1896         /*
1897          * if we've got a line index for the mark, get the positions.
1898          */
1899         if (-1 != mark->beg.line_idx && -1 != mark->end.line_idx)
1900           {
1901             _DtCvDspLine  *line = &(canvas->txt_lst[mark->beg.line_idx]);
1902
1903             *ret_x = mark->beg.x;
1904             *ret_y = mark->beg.y - line->ascent;
1905
1906             if (mark->beg.line_idx == mark->end.line_idx)
1907                 *ret_width = mark->end.x - *ret_x;
1908             else
1909                 *ret_width = canvas->txt_lst[mark->beg.line_idx].max_x - *ret_x;
1910
1911             *ret_height = line->ascent + line->descent + 1;
1912
1913             result = 0;
1914           }
1915       }
1916
1917     return result;
1918 }
1919
1920 /******************************************************************************
1921  * Function:    _DtCvSortTraversalList
1922  *
1923  * Returns:      nothing
1924  *
1925  * Purpose:     Sort the traversal list
1926  *
1927  *****************************************************************************/
1928 void
1929 _DtCvSortTraversalList (
1930     _DtCanvasStruct     *canvas,
1931     _DtCvValue           retain)
1932 {
1933     int curTrav = canvas->cur_trav;
1934
1935     if (1 < canvas->trav_cnt)
1936       {
1937         /*
1938          * indicate this is the current traversal
1939          */
1940         if (-1 != curTrav)
1941             canvas->trav_lst[curTrav].active = retain;
1942
1943         /*
1944          * sort the items.
1945          */
1946         qsort (canvas->trav_lst, canvas->trav_cnt, sizeof(_DtCvTraversalInfo),
1947                         CompareTraversalPos);
1948
1949         if (_DtCvTRUE == retain && -1 != curTrav &&
1950                                 _DtCvFALSE == canvas->trav_lst[curTrav].active)
1951           {
1952             curTrav = 0;
1953             while (_DtCvFALSE == canvas->trav_lst[curTrav].active)
1954                 curTrav++;
1955
1956             canvas->cur_trav = curTrav;
1957           }
1958
1959         /*
1960          * clear the active flag
1961          */
1962         if (-1 != curTrav)
1963             canvas->trav_lst[curTrav].active = _DtCvFALSE;
1964       }
1965 }
1966
1967 /*****************************************************************************
1968  * Function:    _DtCvCvtSegsToPts()
1969  *
1970  * Purpose:     Given a set of segments, determine the ending points.
1971  *
1972  *****************************************************************************/
1973 _DtCvStatus
1974 _DtCvCvtSegsToPts (
1975     _DtCanvasStruct      *canvas,
1976     _DtCvSegPtsI        **segs,
1977     _DtCvSelectData      *beg,
1978     _DtCvSelectData      *end,
1979     _DtCvUnit            *ret_y1,
1980     _DtCvUnit            *ret_y2,
1981     _DtCvSegmentI       **ret_seg)
1982 {
1983     int                  count;
1984     int                  cnt;
1985     int                  start;
1986     int                  length;
1987     long                 lineIdx;
1988     int                  linkIdx = -1;
1989     _DtCvValue           lastVisLnk = _DtCvFALSE;
1990     _DtCvUnit            minY = -1;
1991     _DtCvUnit            maxY = 0;
1992     _DtCvUnit            startX;
1993     _DtCvUnit            endX;
1994     _DtCvUnit            segWidth;
1995     _DtCvSegmentI       *pSeg;
1996     _DtCvSegmentI       *saveSeg;
1997     _DtCvSegmentI       **retSeg;
1998     _DtCvDspLine        *lines = canvas->txt_lst;
1999     _DtCvFlags           result = _DtCvSTATUS_NONE;
2000     _DtCvSelectData     *tmpBeg;
2001     _DtCvSelectData     *tmpEnd;
2002     _DtCvSelectData      bReg;
2003     _DtCvSelectData      eReg;
2004
2005     /*
2006      * initialize the structures.
2007      */
2008     bReg = DefSelectData;
2009     eReg = DefSelectData;
2010     *beg = DefSelectData;
2011     *end = DefSelectData;
2012
2013     /*
2014      * go through each segment and determine the starting positions.
2015      */
2016     while (NULL != *segs)
2017       {
2018         result = _DtCvSTATUS_OK;
2019
2020         /*
2021          * what line is this segment on?
2022          */
2023         lineIdx = (long) ((*segs)->segment->internal_use);
2024
2025         /*
2026          * get some information about the line
2027          */
2028         length = lines[lineIdx].length;
2029         start  = lines[lineIdx].byte_index;
2030         startX = _DtCvGetStartXOfLine(&(lines[lineIdx]), &pSeg);
2031         pSeg   = lines[lineIdx].seg_ptr;
2032
2033         /*
2034          * now skip the segments on this line that aren't in the data pt.
2035          */
2036         while (NULL != pSeg && pSeg != (*segs)->segment)
2037           {
2038             /*
2039              * advance past any hypertext link offsets.
2040              */
2041             startX = _DtCvAdvanceXOfLine(canvas, pSeg, startX,
2042                                                 &linkIdx, &lastVisLnk);
2043
2044             /*
2045              * we know that this is not the segment we are looking for,
2046              * so go past it.
2047              */
2048             _DtCvGetWidthOfSegment(canvas, pSeg, start, length,
2049                                                         &cnt, &segWidth, NULL);
2050
2051             /*
2052              * skip the segment's width, decrease the overall length by
2053              * the segment's count, reset the character start point and
2054              * go to the next segment.
2055              */
2056             startX += segWidth;
2057             length -= cnt;
2058             start   = 0;
2059             pSeg    = pSeg->next_disp;
2060           }
2061
2062         /*
2063          * This segment should be all or partially selected.
2064          */
2065         if (NULL == pSeg)
2066             return _DtCvSTATUS_BAD;
2067
2068         /*
2069          * now figure the start location.
2070          */
2071         startX = _DtCvAdvanceXOfLine(canvas, pSeg, startX,
2072                                                 &linkIdx, &lastVisLnk);
2073         /*
2074          * guarenteed that this is the *first* line that the segment
2075          * exists on. Therefore, may have to go to another line for
2076          * the correct offset
2077          */
2078         while (start + length < (*segs)->offset)
2079           {
2080             do { lineIdx++; } while (lineIdx < canvas->txt_cnt
2081                                         && pSeg != lines[lineIdx].seg_ptr);
2082
2083             if (lineIdx >= canvas->txt_cnt)
2084                 return _DtCvSTATUS_BAD;
2085
2086             length = lines[lineIdx].length;
2087             start  = lines[lineIdx].byte_index;
2088             startX = lines[lineIdx].text_x;
2089
2090             linkIdx    = -1;
2091             lastVisLnk = False;
2092             startX     = _DtCvAdvanceXOfLine(canvas, pSeg, startX,
2093                                                 &linkIdx, &lastVisLnk);
2094           }
2095
2096         /*
2097          * how many characters do we need to skip?
2098          */
2099         count    = (*segs)->offset - start;
2100         segWidth = 0;
2101         if (0 < count)
2102             _DtCvGetWidthOfSegment(canvas, pSeg, start, count,
2103                                                         &cnt, &segWidth, NULL);
2104         /*
2105          * adjust the info by the width of the skipped characters.
2106          */
2107         start  += count;
2108         length -= count;
2109         startX += segWidth;
2110
2111         /*
2112          * is this a region? If so set the region information instead.
2113          */
2114         tmpBeg = beg;
2115         tmpEnd = end;
2116         retSeg = ret_seg;
2117         if (_DtCvIsSegRegion((*segs)->segment))
2118           {
2119             tmpBeg = &bReg;
2120             tmpEnd = &eReg;
2121             retSeg = &saveSeg;
2122           }
2123
2124         /*
2125          * does this segment start the selection? text or region?
2126          */
2127         if (tmpBeg->x == -1 || tmpBeg->y > lines[lineIdx].baseline ||
2128                         (tmpBeg->line_idx == lineIdx && tmpBeg->x > startX))
2129           {
2130             tmpBeg->x        = startX;
2131             tmpBeg->y        = lines[lineIdx].baseline;
2132             tmpBeg->line_idx = lineIdx;
2133             tmpBeg->char_idx = lines[lineIdx].length - length;
2134             if (NULL != retSeg)
2135                 *retSeg = (*segs)->segment;
2136           }
2137
2138         /*
2139          * get the amount of this segment that is selected.
2140          */
2141         count = (*segs)->len;
2142
2143         /*
2144          * is it longer than what's (left) on this line?
2145          */
2146         while (count > length)
2147           {
2148             /*
2149              * go to the next line containing the segment
2150              */
2151             do { 
2152                 /*
2153                  * does this line have the minium y?
2154                  */
2155                 if (minY == -1 ||
2156                         minY > lines[lineIdx].baseline - lines[lineIdx].ascent)
2157                    minY = lines[lineIdx].baseline - lines[lineIdx].ascent;
2158
2159                  lineIdx++; 
2160               } while (lineIdx < canvas->txt_cnt
2161                                         && pSeg != lines[lineIdx].seg_ptr);
2162             /*
2163              * did we run out of lines?
2164              */
2165             if (lineIdx >= canvas->txt_cnt)
2166                 return _DtCvSTATUS_BAD;
2167                 
2168             /*
2169              * start over on this line
2170              */
2171             segWidth = 0;
2172
2173             /*
2174              * get the true count to the next offset
2175              */
2176             cnt      = lines[lineIdx].byte_index - start;
2177
2178             /*
2179              * get the next lines starting info.
2180              */
2181             start    = lines[lineIdx].byte_index;
2182             length   = lines[lineIdx].length;
2183             startX   = _DtCvGetStartXOfLine(&(lines[lineIdx]), &pSeg);
2184             linkIdx    = -1;
2185             lastVisLnk = False;
2186             startX     = _DtCvAdvanceXOfLine(canvas, pSeg, startX,
2187                                                 &linkIdx, &lastVisLnk);
2188             /*
2189              * subtract the previous length
2190              */
2191             count -= cnt;
2192           }
2193
2194         /*
2195          * now go down the line, examining each segment.
2196          */
2197         while (0 < count)
2198           {
2199             /*
2200              * findout how many characters are in the next segment, and its
2201              * width.
2202              */
2203             _DtCvGetWidthOfSegment(canvas,pSeg,start,count,&cnt,&segWidth,NULL);
2204
2205             /*
2206              * there are less than in the count, go to the next segment.
2207              */
2208             if (cnt < count)
2209               {
2210                 pSeg   = pSeg->next_disp;
2211                 start  = 0;
2212                 startX += segWidth;
2213               }
2214
2215             length -= cnt;
2216             count -= cnt;
2217           }
2218
2219         endX   = startX + segWidth;
2220
2221         /*
2222          * does this segment end a segment?
2223          */
2224         if (tmpEnd->x == -1 || tmpEnd->y < lines[lineIdx].baseline ||
2225                         (tmpEnd->line_idx == lineIdx && tmpEnd->x < endX))
2226           {
2227             tmpEnd->x        = endX;
2228             tmpEnd->y        = lines[lineIdx].baseline;
2229             tmpEnd->line_idx = lineIdx;
2230             tmpEnd->char_idx = lines[lineIdx].length - length;
2231           }
2232
2233         /*
2234          * check for min and max values
2235          */
2236         if (minY == -1 ||
2237                         minY > lines[lineIdx].baseline - lines[lineIdx].ascent)
2238             minY = lines[lineIdx].baseline - lines[lineIdx].ascent;
2239
2240         if (maxY < lines[lineIdx].baseline + lines[lineIdx].descent)
2241             maxY = lines[lineIdx].baseline + lines[lineIdx].descent;
2242         /*
2243          * go to the next segment
2244          */
2245         segs++;
2246       }
2247
2248     /*
2249      * now determine if a region really starts the beginning of a
2250      * selection or a text does.
2251      *
2252      * was a region found?
2253      */
2254     if (-1 != bReg.x)
2255       {
2256         /*
2257          * if no text was found, take the region information.
2258          */
2259         if (-1 == beg->x)
2260           {
2261             *beg = bReg;
2262             if (NULL != ret_seg)
2263                 *ret_seg = saveSeg;
2264           }
2265         /*
2266          * or if the region is inline to the other
2267          * text and it is before the text, then take it's x value.
2268          */
2269         else if (bReg.x < beg->x &&
2270                         (bReg.line_idx == beg->line_idx ||
2271         /*
2272          * Or if the region is 'standalone' (a bullet of a list, a
2273          * graphic to wrap around, etc.)  then check to see if it
2274          * straddles the other information and is before the text.  If
2275          * it does, take it's x value.
2276          */
2277                         _DtCvStraddlesPt(beg->y,
2278                                 bReg.y - lines[bReg.line_idx].ascent,
2279                                 bReg.y - lines[bReg.line_idx].descent)))
2280           {
2281             beg->x = bReg.x;
2282
2283             if (NULL != ret_seg)
2284                 *ret_seg = saveSeg;
2285           }
2286       }
2287
2288     /*
2289      * now determine if a region really ends the selection or a text does.
2290      *
2291      * was a region found?
2292      */
2293     if (-1 != eReg.x)
2294       {
2295         /*
2296          * if no text was found, take the region information.
2297          */
2298         if (-1 == end->x)
2299             *end = eReg;
2300         /*
2301          * or if the region is inline to the other
2302          * text and it is before the text, then take it's x value.
2303          */
2304         else if (eReg.x > end->x &&
2305                         (eReg.line_idx == end->line_idx ||
2306         /*
2307          * Or if the region is 'standalone' (a bullet of a list, a
2308          * graphic to wrap around, etc.)  then check to see if it
2309          * straddles the other information and is before the text.  If
2310          * it does, take it's x value.
2311          */
2312                         _DtCvStraddlesPt(end->y,
2313                                 eReg.y - lines[eReg.line_idx].ascent,
2314                                 eReg.y - lines[eReg.line_idx].descent)))
2315             end->x = eReg.x;
2316
2317       }
2318     if (NULL != ret_y1)
2319         *ret_y1 = minY;
2320     if (NULL != ret_y2)
2321         *ret_y2 = maxY;
2322
2323     return result;
2324 }
2325
2326 /*****************************************************************************
2327  * Function:    _DtCvAddToMarkList()
2328  *
2329  * Purpose:     Add a mark to the list of marks.
2330  *
2331  *****************************************************************************/
2332 int
2333 _DtCvAddToMarkList (
2334     _DtCanvasStruct      *canvas,
2335     _DtCvPointer          client_data,
2336     _DtCvValue            flag,
2337     _DtCvSelectData      *beg,
2338     _DtCvSelectData      *end)
2339 {
2340     _DtCvMarkData  *nxtMark;
2341
2342     /*
2343      * does the array need more memory?
2344      */
2345     if (canvas->mark_cnt >= canvas->mark_max)
2346       {
2347         canvas->mark_max += GROW_SIZE;
2348
2349         if (NULL == canvas->marks)
2350             canvas->marks = (_DtCvMarkData *) malloc(
2351                                 sizeof(_DtCvMarkData) * canvas->mark_max);
2352         else
2353             canvas->marks = (_DtCvMarkData *) realloc((void *) canvas->marks,
2354                                 sizeof(_DtCvMarkData) * canvas->mark_max);
2355
2356         /*
2357          * memory loss - bail
2358          */
2359         if (NULL == canvas->marks)
2360             return -1;
2361       }
2362
2363     /*
2364      * set the mark information
2365      */
2366     nxtMark              = &(canvas->marks[canvas->mark_cnt]);
2367     nxtMark->on          = flag;
2368     nxtMark->client_data = client_data;
2369     nxtMark->beg         = *beg;
2370     nxtMark->end         = *end;
2371     canvas->mark_cnt++;
2372
2373     return (canvas->mark_cnt - 1);
2374 }