/* * 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 #include #include /* * 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); }