/********************************************************************* * TEXT_Ellipsify (static) * * Add an ellipsis to the end of the given string whilst ensuring it fits. * * If the ellipsis alone doesn't fit then it will be returned anyway. * * See Also TEXT_PathEllipsify * * Arguments * hdc [in] The handle to the DC that defines the font. * str [in/out] The string that needs to be modified. * max_str [in] The dimension of str (number of WCHAR). * len_str [in/out] The number of characters in str * width [in] The maximum width permitted (in logical coordinates) * size [out] The dimensions of the text * modstr [out] The modified form of the string, to be returned to the * calling program. It is assumed that the caller has * made sufficient space available so we don't need to * know the size of the space. This pointer may be NULL if * the modified string is not required. * len_before [out] The number of characters before the ellipsis. * len_ellip [out] The number of characters in the ellipsis. * * See for example Microsoft article Q249678. * * For now we will simply use three dots rather than worrying about whether * the font contains an explicit ellipsis character. */ static void TEXT_Ellipsify (HDC hdc, WCHAR *str, unsigned int max_len, unsigned int *len_str, int width, SIZE *size, WCHAR *modstr, int *len_before, int *len_ellip) { unsigned int len_ellipsis; unsigned int lo, mid, hi; len_ellipsis = strlenW (ELLIPSISW); if (len_ellipsis > max_len) len_ellipsis = max_len; if (*len_str > max_len - len_ellipsis) *len_str = max_len - len_ellipsis; /* First do a quick binary search to get an upper bound for *len_str. */ if (*len_str > 0 && GetTextExtentExPointW(hdc, str, *len_str, width, NULL, NULL, size) && size->cx > width) { for (lo = 0, hi = *len_str; lo < hi; ) { mid = (lo + hi) / 2; if (!GetTextExtentExPointW(hdc, str, mid, width, NULL, NULL, size)) break; if (size->cx > width) hi = mid; else lo = mid + 1; } *len_str = hi; } /* Now this should take only a couple iterations at most. */ for ( ; ; ) { memcpy(str + *len_str, ELLIPSISW, len_ellipsis*sizeof(WCHAR)); if (!GetTextExtentExPointW (hdc, str, *len_str + len_ellipsis, width, NULL, NULL, size)) break; if (!*len_str || size->cx <= width) break; (*len_str)--; } *len_ellip = len_ellipsis; *len_before = *len_str; *len_str += len_ellipsis; if (modstr) { memcpy (modstr, str, *len_str * sizeof(WCHAR)); modstr[*len_str] = '\0'; } }
/****************************************************************************** * ME_CharFromPointContext * * Returns a character position inside the run given a run-relative * pixel horizontal position. * * If closest is FALSE return the actual character * If closest is TRUE will round to the closest leading edge. * ie. if the second character is at pixel position 8 and third at 16 then for: * closest = FALSE cx = 0..7 return 0, cx = 8..15 return 1 * closest = TRUE cx = 0..3 return 0, cx = 4..11 return 1. */ int ME_CharFromPointContext(ME_Context *c, int cx, ME_Run *run, BOOL closest, BOOL visual_order) { ME_String *mask_text = NULL; WCHAR *str; int fit = 0; HGDIOBJ hOldFont; SIZE sz, sz2, sz3; if (!run->len || cx <= 0) return 0; if (run->nFlags & (MERF_TAB | MERF_ENDCELL)) { if (!closest || cx < run->nWidth / 2) return 0; return 1; } if (run->nFlags & MERF_GRAPHICS) { SIZE sz; ME_GetOLEObjectSize(c, run, &sz); if (!closest || cx < sz.cx / 2) return 0; return 1; } if (run->para->nFlags & MEPF_COMPLEX) { int cp, trailing; if (visual_order && run->script_analysis.fRTL) cx = run->nWidth - cx - 1; ScriptXtoCP( cx, run->len, run->num_glyphs, run->clusters, run->vis_attrs, run->advances, &run->script_analysis, &cp, &trailing ); TRACE("x %d cp %d trailing %d (run width %d) rtl %d log order %d\n", cx, cp, trailing, run->nWidth, run->script_analysis.fRTL, run->script_analysis.fLogicalOrder); return closest ? cp + trailing : cp; } if (c->editor->cPasswordMask) { mask_text = ME_MakeStringR( c->editor->cPasswordMask, run->len ); str = mask_text->szData; } else str = get_text( run, 0 ); hOldFont = ME_SelectStyleFont(c, run->style); GetTextExtentExPointW(c->hDC, str, run->len, cx, &fit, NULL, &sz); if (closest && fit != run->len) { GetTextExtentPoint32W(c->hDC, str, fit, &sz2); GetTextExtentPoint32W(c->hDC, str, fit + 1, &sz3); if (cx >= (sz2.cx+sz3.cx)/2) fit = fit + 1; } ME_DestroyString( mask_text ); ME_UnselectStyleFont(c, run->style, hOldFont); return fit; }
sInt sFont2D::GetCharCountFromWidth(sInt width,const sChar *text,sInt len) { SIZE out; INT count; if(width<0) return 0; if(len==-1) len=sGetStringLen(text); SelectObject(sGDIDCOffscreen,prv->Font); GetTextExtentExPointW(sGDIDCOffscreen,text,len,width,&count,0,&out); return count; }
/****************************************************************************** * ME_CharFromPointContext * * Returns a character position inside the run given a run-relative * pixel horizontal position. * * If closest is FALSE return the actual character * If closest is TRUE will round to the closest leading edge. * ie. if the second character is at pixel position 8 and third at 16 then for: * closest = FALSE cx = 0..7 return 0, cx = 8..15 return 1 * closest = TRUE cx = 0..3 return 0, cx = 4..11 return 1. */ int ME_CharFromPointContext(ME_Context *c, int cx, ME_Run *run, BOOL closest, BOOL visual_order) { ME_String *mask_text = NULL; WCHAR *str; int fit = 0; HGDIOBJ hOldFont; SIZE sz, sz2, sz3; if (!run->len || cx <= 0) return 0; if (run->nFlags & (MERF_TAB | MERF_ENDCELL)) { if (!closest || cx < run->nWidth / 2) return 0; return 1; } if (run->nFlags & MERF_GRAPHICS) { SIZE sz; ME_GetOLEObjectSize(c, run, &sz); if (!closest || cx < sz.cx / 2) return 0; return 1; } if (c->editor->cPasswordMask) { mask_text = ME_MakeStringR( c->editor->cPasswordMask, run->len ); str = mask_text->szData; } else str = get_text( run, 0 ); hOldFont = ME_SelectStyleFont(c, run->style); GetTextExtentExPointW(c->hDC, str, run->len, cx, &fit, NULL, &sz); if (closest && fit != run->len) { GetTextExtentPoint32W(c->hDC, str, fit, &sz2); GetTextExtentPoint32W(c->hDC, str, fit + 1, &sz3); if (cx >= (sz2.cx+sz3.cx)/2) fit = fit + 1; } ME_DestroyString( mask_text ); ME_UnselectStyleFont(c, run->style, hOldFont); return fit; }
static BOOL notepad_print_page(HDC hdc, RECT *rc, BOOL dopage, int page, LPTEXTINFO tInfo) { int b, y; TEXTMETRICW tm; SIZE szMetrics; WCHAR *footer_text = NULL; footer_text = expand_header_vars(Globals.szFooter, page); if (footer_text == NULL) return FALSE; if (dopage) { if (StartPage(hdc) <= 0) { static const WCHAR failedW[] = { 'S','t','a','r','t','P','a','g','e',' ','f','a','i','l','e','d',0 }; static const WCHAR errorW[] = { 'P','r','i','n','t',' ','E','r','r','o','r',0 }; MessageBoxW(Globals.hMainWnd, failedW, errorW, MB_ICONEXCLAMATION); HeapFree(GetProcessHeap(), 0, footer_text); return FALSE; } } GetTextMetricsW(hdc, &tm); y = rc->top + notepad_print_header(hdc, rc, dopage, TRUE, page, Globals.szFileName) * tm.tmHeight; b = rc->bottom - 2 * notepad_print_header(hdc, rc, FALSE, FALSE, page, footer_text) * tm.tmHeight; do { INT m, n; if (!tInfo->len) { /* find the end of the line */ while (tInfo->mptr < tInfo->mend && *tInfo->mptr != '\n' && *tInfo->mptr != '\r') { if (*tInfo->mptr == '\t') { /* replace tabs with spaces */ for (m = 0; m < SPACES_IN_TAB; m++) { if (tInfo->len < PRINT_LEN_MAX) tInfo->lptr[tInfo->len++] = ' '; else if (Globals.bWrapLongLines) break; } } else if (tInfo->len < PRINT_LEN_MAX) tInfo->lptr[tInfo->len++] = *tInfo->mptr; if (tInfo->len >= PRINT_LEN_MAX && Globals.bWrapLongLines) break; tInfo->mptr++; } } /* Find out how much we should print if line wrapping is enabled */ if (Globals.bWrapLongLines) { GetTextExtentExPointW(hdc, tInfo->lptr, tInfo->len, rc->right - rc->left, &n, NULL, &szMetrics); if (n < tInfo->len && tInfo->lptr[n] != ' ') { m = n; /* Don't wrap words unless it's a single word over the entire line */ while (m && tInfo->lptr[m] != ' ') m--; if (m > 0) n = m + 1; } } else n = tInfo->len; if (dopage) ExtTextOutW(hdc, rc->left, y, ETO_CLIPPED, rc, tInfo->lptr, n, NULL); tInfo->len -= n; if (tInfo->len) { memcpy(tInfo->lptr, tInfo->lptr + n, tInfo->len * sizeof(WCHAR)); y += tm.tmHeight + tm.tmExternalLeading; } else { /* find the next line */ while (tInfo->mptr < tInfo->mend && y < b && (*tInfo->mptr == '\n' || *tInfo->mptr == '\r')) { if (*tInfo->mptr == '\n') y += tm.tmHeight + tm.tmExternalLeading; tInfo->mptr++; } } } while (tInfo->mptr < tInfo->mend && y < b); notepad_print_header(hdc, rc, dopage, FALSE, page, footer_text); if (dopage) { EndPage(hdc); } HeapFree(GetProcessHeap(), 0, footer_text); return TRUE; }
/********************************************************************* * Return next line of text from a string. * * hdc - handle to DC. * str - string to parse into lines. * count - length of str. * dest - destination in which to return line. * len - dest buffer size in chars on input, copied length into dest on output. * width - maximum width of line in pixels. * format - format type passed to DrawText. * retsize - returned size of the line in pixels. * last_line - TRUE if is the last line that will be processed * p_retstr - If DT_MODIFYSTRING this points to a cursor in the buffer in which * the return string is built. * tabwidth - The width of a tab in logical coordinates * pprefix_offset - Here is where we return the offset within dest of the first * prefixed (underlined) character. -1 is returned if there * are none. Note that there may be more; the calling code * will need to use TEXT_Reprefix to find any later ones. * pellip - Here is where we return the information about any ellipsification * that was carried out. Note that if tabs are being expanded then * this data will correspond to the last text segment actually * returned in dest; by definition there would not have been any * ellipsification in earlier text segments of the line. * * Returns pointer to next char in str after end of the line * or NULL if end of str reached. */ static const WCHAR *TEXT_NextLineW( HDC hdc, const WCHAR *str, int *count, WCHAR *dest, int *len, int width, DWORD format, SIZE *retsize, int last_line, WCHAR **p_retstr, int tabwidth, int *pprefix_offset, ellipsis_data *pellip) { int i = 0, j = 0; int plen = 0; SIZE size = {0, 0}; int maxl = *len; int seg_i, seg_count, seg_j; int max_seg_width; int num_fit; int word_broken; int line_fits; unsigned int j_in_seg; int ellipsified; *pprefix_offset = -1; /* For each text segment in the line */ retsize->cy = 0; while (*count) { /* Skip any leading tabs */ if (str[i] == TAB && (format & DT_EXPANDTABS)) { plen = ((plen/tabwidth)+1)*tabwidth; (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; while (*count && str[i] == TAB) { plen += tabwidth; (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; } } /* Now copy as far as the next tab or cr/lf or eos */ seg_i = i; seg_count = *count; seg_j = j; while (*count && (str[i] != TAB || !(format & DT_EXPANDTABS)) && ((str[i] != CR && str[i] != LF) || (format & DT_SINGLELINE))) { if (str[i] == PREFIX && !(format & DT_NOPREFIX) && *count > 1) { (*count)--, i++; /* Throw away the prefix itself */ if (str[i] == PREFIX) { /* Swallow it before we see it again */ (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; } else if (*pprefix_offset == -1 || *pprefix_offset >= seg_j) { *pprefix_offset = j; } /* else the previous prefix was in an earlier segment of the * line; we will leave it to the drawing code to catch this * one. */ } else { (*count)--; if (j < maxl) dest[j++] = str[i++]; else i++; } } /* Measure the whole text segment and possibly WordBreak and * ellipsify it */ j_in_seg = j - seg_j; max_seg_width = width - plen; GetTextExtentExPointW (hdc, dest + seg_j, j_in_seg, max_seg_width, &num_fit, NULL, &size); /* The Microsoft handling of various combinations of formats is weird. * The following may very easily be incorrect if several formats are * combined, and may differ between versions (to say nothing of the * several bugs in the Microsoft versions). */ word_broken = 0; line_fits = (num_fit >= j_in_seg); if (!line_fits && (format & DT_WORDBREAK)) { const WCHAR *s; unsigned int chars_used; TEXT_WordBreak (hdc, dest+seg_j, maxl-seg_j, &j_in_seg, max_seg_width, format, num_fit, &chars_used, &size); line_fits = (size.cx <= max_seg_width); /* and correct the counts */ TEXT_SkipChars (count, &s, seg_count, str+seg_i, i-seg_i, chars_used, !(format & DT_NOPREFIX)); i = s - str; word_broken = 1; } pellip->before = j_in_seg; pellip->under = 0; pellip->after = 0; pellip->len = 0; ellipsified = 0; if (!line_fits && (format & DT_PATH_ELLIPSIS)) { TEXT_PathEllipsify (hdc, dest + seg_j, maxl-seg_j, &j_in_seg, max_seg_width, &size, *p_retstr, pellip); line_fits = (size.cx <= max_seg_width); ellipsified = 1; } /* NB we may end up ellipsifying a word-broken or path_ellipsified * string */ if ((!line_fits && (format & DT_WORD_ELLIPSIS)) || ((format & DT_END_ELLIPSIS) && ((last_line && *count) || (remainder_is_none_or_newline (*count, &str[i]) && !line_fits)))) { int before, len_ellipsis; TEXT_Ellipsify (hdc, dest + seg_j, maxl-seg_j, &j_in_seg, max_seg_width, &size, *p_retstr, &before, &len_ellipsis); if (before > pellip->before) { /* We must have done a path ellipsis too */ pellip->after = before - pellip->before - pellip->len; /* Leave the len as the length of the first ellipsis */ } else { /* If we are here after a path ellipsification it must be * because even the ellipsis itself didn't fit. */ assert (pellip->under == 0 && pellip->after == 0); pellip->before = before; pellip->len = len_ellipsis; /* pellip->after remains as zero as does * pellip->under */ } line_fits = (size.cx <= max_seg_width); ellipsified = 1; } /* As an optimisation if we have ellipsified and we are expanding * tabs and we haven't reached the end of the line we can skip to it * now rather than going around the loop again. */ if ((format & DT_EXPANDTABS) && ellipsified) { if (format & DT_SINGLELINE) *count = 0; else { while ((*count) && str[i] != CR && str[i] != LF) { (*count)--, i++; } } } j = seg_j + j_in_seg; if (*pprefix_offset >= seg_j + pellip->before) { *pprefix_offset = TEXT_Reprefix (str + seg_i, i - seg_i, pellip); if (*pprefix_offset != -1) *pprefix_offset += seg_j; } plen += size.cx; if (size.cy > retsize->cy) retsize->cy = size.cy; if (word_broken) break; else if (!*count) break; else if (str[i] == CR || str[i] == LF) { (*count)--, i++; if (*count && (str[i] == CR || str[i] == LF) && str[i] != str[i-1]) { (*count)--, i++; } break; } /* else it was a Tab and we go around again */ } retsize->cx = plen; *len = j; if (*count) return (&str[i]); else return NULL; }
static void TEXT_WordBreak (HDC hdc, WCHAR *str, unsigned int max_str, unsigned int *len_str, int width, int format, unsigned int chars_fit, unsigned int *chars_used, SIZE *size) { WCHAR *p; int word_fits; assert (format & DT_WORDBREAK); assert (chars_fit < *len_str); /* Work back from the last character that did fit to either a space or the * last character of a word, whichever is met first. */ p = str + chars_fit; /* The character that doesn't fit */ word_fits = TRUE; if (!chars_fit) ; /* we pretend that it fits anyway */ else if (*p == SPACE) /* chars_fit < *len_str so this is valid */ p--; /* the word just fitted */ else { while (p > str && *(--p) != SPACE && (!IsCJKT(p[1]) || p[1] == L'\0' || wcschr(KinsokuClassA, p[1]) != NULL)) ; word_fits = (p != str || *p == SPACE || IsCJKT(p[1])); } /* If there was one or the first character didn't fit then */ if (word_fits) { int next_is_space; /* break the line before/after that character */ if (!(format & (DT_RIGHT | DT_CENTER)) || *p != SPACE) p++; next_is_space = (unsigned int) (p - str) < *len_str && *p == SPACE; *len_str = p - str; /* and if the next character is a space then discard it. */ *chars_used = *len_str; if (next_is_space) (*chars_used)++; } /* Suppose there was none. */ else { if ((format & (DT_EDITCONTROL | DT_WORD_ELLIPSIS | DT_PATH_ELLIPSIS)) == DT_EDITCONTROL) { /* break the word after the last character that fits (there must be * at least one; none is caught earlier). */ *len_str = chars_fit; *chars_used = chars_fit; /* FIXME - possible error. Since the next character is now removed * this could make the text longer so that it no longer fits, and * so we need a loop to test and shrink. */ } /* Otherwise */ else { /* discard any trailing space. */ const WCHAR *e = str + *len_str; p = str + chars_fit; while (p < e && *p != SPACE) p++; *chars_used = p - str; if (p < e) /* i.e. loop failed because *p == SPACE */ (*chars_used)++; /* include the whole word; it may be ellipsified later */ *len_str = p - str; /* Possible optimisation; if DT_WORD_ELLIPSIS only use chars_fit+1 * so that it will be too long */ } } /* Remeasure the string */ GetTextExtentExPointW (hdc, str, *len_str, 0, NULL, NULL, size); }
/********************************************************************* * TEXT_PathEllipsify (static) * * Add an ellipsis to the provided string in order to make it fit within * the width. The ellipsis is added as specified for the DT_PATH_ELLIPSIS * flag. * * See Also TEXT_Ellipsify * * Arguments * hdc [in] The handle to the DC that defines the font. * str [in/out] The string that needs to be modified * max_str [in] The dimension of str (number of WCHAR). * len_str [in/out] The number of characters in str * width [in] The maximum width permitted (in logical coordinates) * size [out] The dimensions of the text * modstr [out] The modified form of the string, to be returned to the * calling program. It is assumed that the caller has * made sufficient space available so we don't need to * know the size of the space. This pointer may be NULL if * the modified string is not required. * pellip [out] The ellipsification results * * For now we will simply use three dots rather than worrying about whether * the font contains an explicit ellipsis character. * * The following applies, I think to Win95. We will need to extend it for * Win98 which can have both path and end ellipsis at the same time (e.g. * C:\MyLongFileName.Txt becomes ...\MyLongFileN...) * * The resulting string consists of as much as possible of the following: * 1. The ellipsis itself * 2. The last \ or / of the string (if any) * 3. Everything after the last \ or / of the string (if any) or the whole * string if there is no / or \. I believe that under Win95 this would * include everything even though some might be clipped off the end whereas * under Win98 that might be ellipsified too. * Yet to be investigated is whether this would include wordbreaking if the * filename is more than 1 word and splitting if DT_EDITCONTROL was in * effect. (If DT_EDITCONTROL is in effect then on occasions text will be * broken within words). * 4. All the stuff before the / or \, which is placed before the ellipsis. */ static void TEXT_PathEllipsify (HDC hdc, WCHAR *str, unsigned int max_len, unsigned int *len_str, int width, SIZE *size, WCHAR *modstr, ellipsis_data *pellip) { int len_ellipsis; int len_trailing; int len_under; WCHAR *lastBkSlash, *lastFwdSlash, *lastSlash; len_ellipsis = strlenW (ELLIPSISW); if (!max_len) return; if (len_ellipsis >= max_len) len_ellipsis = max_len - 1; if (*len_str + len_ellipsis >= max_len) *len_str = max_len - len_ellipsis-1; /* Hopefully this will never happen, otherwise it would probably lose * the wrong character */ str[*len_str] = '\0'; /* to simplify things */ lastBkSlash = strrchrW (str, BACK_SLASH); lastFwdSlash = strrchrW (str, FORWARD_SLASH); lastSlash = lastBkSlash > lastFwdSlash ? lastBkSlash : lastFwdSlash; if (!lastSlash) lastSlash = str; len_trailing = *len_str - (lastSlash - str); /* overlap-safe movement to the right */ memmove (lastSlash+len_ellipsis, lastSlash, len_trailing * sizeof(WCHAR)); memcpy (lastSlash, ELLIPSISW, len_ellipsis*sizeof(WCHAR)); len_trailing += len_ellipsis; /* From this point on lastSlash actually points to the ellipsis in front * of the last slash and len_trailing includes the ellipsis */ len_under = 0; for ( ; ; ) { if (!GetTextExtentExPointW (hdc, str, *len_str + len_ellipsis, width, NULL, NULL, size)) break; if (lastSlash == str || size->cx <= width) break; /* overlap-safe movement to the left */ memmove (lastSlash-1, lastSlash, len_trailing * sizeof(WCHAR)); lastSlash--; len_under++; assert (*len_str); (*len_str)--; } pellip->before = lastSlash-str; pellip->len = len_ellipsis; pellip->under = len_under; pellip->after = len_trailing - len_ellipsis; *len_str += len_ellipsis; if (modstr) { memcpy(modstr, str, *len_str * sizeof(WCHAR)); modstr[*len_str] = '\0'; } }
/*********************************************************************** * SYSLINK_Render * Renders the document in memory */ static VOID SYSLINK_Render (const SYSLINK_INFO *infoPtr, HDC hdc, PRECT pRect) { RECT rc; PDOC_ITEM Current; HGDIOBJ hOldFont; int x, y, LineHeight; SIZE szDoc; TEXTMETRICW tm; szDoc.cx = szDoc.cy = 0; rc = *pRect; rc.right -= SL_RIGHTMARGIN; rc.bottom -= SL_BOTTOMMARGIN; if(rc.right - SL_LEFTMARGIN < 0) rc.right = MAXLONG; if (rc.bottom - SL_TOPMARGIN < 0) rc.bottom = MAXLONG; hOldFont = SelectObject(hdc, infoPtr->Font); x = SL_LEFTMARGIN; y = SL_TOPMARGIN; GetTextMetricsW( hdc, &tm ); LineHeight = tm.tmHeight + tm.tmExternalLeading; for(Current = infoPtr->Items; Current != NULL; Current = Current->Next) { int n, nBlocks; LPWSTR tx; PDOC_TEXTBLOCK bl, cbl; INT nFit; SIZE szDim; int SkipChars = 0; if(Current->nText == 0) { continue; } tx = Current->Text; n = Current->nText; Free(Current->Blocks); Current->Blocks = NULL; bl = NULL; nBlocks = 0; if(Current->Type == slText) { SelectObject(hdc, infoPtr->Font); } else if(Current->Type == slLink) { SelectObject(hdc, infoPtr->LinkFont); } while(n > 0) { /* skip break characters unless they're the first of the doc item */ if(tx != Current->Text || x == SL_LEFTMARGIN) { if (n && *tx == '\n') { tx++; SkipChars++; n--; } while(n > 0 && (*tx) == infoPtr->BreakChar) { tx++; SkipChars++; n--; } } if((n == 0 && SkipChars != 0) || GetTextExtentExPointW(hdc, tx, n, rc.right - x, &nFit, NULL, &szDim)) { int LineLen = n; BOOL Wrap = FALSE; PDOC_TEXTBLOCK nbl; if(n != 0) { Wrap = SYSLINK_WrapLine(tx, infoPtr->BreakChar, x, &LineLen, nFit, &szDim); if(LineLen == 0) { /* move one line down, the word didn't fit into the line */ x = SL_LEFTMARGIN; y += LineHeight; continue; } if(LineLen != n) { if(!GetTextExtentExPointW(hdc, tx, LineLen, rc.right - x, NULL, NULL, &szDim)) { if(bl != NULL) { Free(bl); bl = NULL; nBlocks = 0; } break; } } } nbl = ReAlloc(bl, (nBlocks + 1) * sizeof(DOC_TEXTBLOCK)); if (nbl != NULL) { bl = nbl; nBlocks++; cbl = bl + nBlocks - 1; cbl->nChars = LineLen; cbl->nSkip = SkipChars; cbl->rc.left = x; cbl->rc.top = y; cbl->rc.right = x + szDim.cx; cbl->rc.bottom = y + szDim.cy; if (cbl->rc.right > szDoc.cx) szDoc.cx = cbl->rc.right; if (cbl->rc.bottom > szDoc.cy) szDoc.cy = cbl->rc.bottom; if(LineLen != 0) { x += szDim.cx; if(Wrap) { x = SL_LEFTMARGIN; y += LineHeight; } } } else { Free(bl); bl = NULL; nBlocks = 0; ERR("Failed to alloc DOC_TEXTBLOCK structure!\n"); break; } n -= LineLen; tx += LineLen; SkipChars = 0; } else { n--; } } if(nBlocks != 0) { Current->Blocks = bl; } } SelectObject(hdc, hOldFont); pRect->right = pRect->left + szDoc.cx; pRect->bottom = pRect->top + szDoc.cy; }
static void TEXT_WordBreak (HDC hdc, WCHAR *str, unsigned int max_str, unsigned int *len_str, int width, int format, unsigned int chars_fit, unsigned int *chars_used, SIZE *size) { WCHAR *p; int word_fits; assert (format & DT_WORDBREAK); assert (chars_fit < *len_str); /* Work back from the last character that did fit to either a space or the * last character of a word, whichever is met first. */ p = str + chars_fit; /* The character that doesn't fit */ word_fits = TRUE; if (!chars_fit) ; /* we pretend that it fits anyway */ else if (*p == SPACE) /* chars_fit < *len_str so this is valid */ p--; /* the word just fitted */ else if (*p >= 0x2e81 && *p <= 0xffe5) { /* Chinese punctuation character: , . ? ! ) >> ' ' " ; : */ if ( *p == 0x2018 /* ) */ || *p == 0x2019 /* ! */ || *p == 0x201D /* ? */ || *p == 0x3002 /* ; */ || *p == 0x300B /* >> */ || *p == 0xFF01 /* '(right) */ || *p == 0xFF09 /* '(left) */ || *p == 0xFF0C /* : */ || *p == 0xFF1A /* , */ || *p == 0xFF1B /* . */ || *p == 0xFF1F /* "(right) */ ) p--; /* CJK characters are treated breakable */ p--; } else { /* if Chinese and English mix up */ while (p > str && *p != SPACE && (*p < 0x3012 || *p > 0xFA29 ) ) p--; word_fits = (p != str || *p == SPACE); } /* If there was one or the first character didn't fit then */ if (word_fits) { int next_is_space; /* break the line before/after that character */ if (!(format & (DT_RIGHT | DT_CENTER)) || *p != SPACE) p++; next_is_space = (p - str) < *len_str && *p == SPACE; *len_str = p - str; /* and if the next character is a space then discard it. */ *chars_used = *len_str; if (next_is_space) (*chars_used)++; } /* Suppose there was none. */ else { if ((format & (DT_EDITCONTROL | DT_WORD_ELLIPSIS | DT_PATH_ELLIPSIS)) == DT_EDITCONTROL) { /* break the word after the last character that fits (there must be * at least one; none is caught earlier). */ *len_str = chars_fit; *chars_used = chars_fit; /* FIXME - possible error. Since the next character is now removed * this could make the text longer so that it no longer fits, and * so we need a loop to test and shrink. */ } /* Otherwise */ else { /* discard any trailing space. */ const WCHAR *e = str + *len_str; p = str + chars_fit; while (p < e && *p != SPACE) p++; *chars_used = p - str; if (p < e) /* i.e. loop failed because *p == SPACE */ (*chars_used)++; /* include the whole word; it may be ellipsified later */ *len_str = p - str; /* Possible optimisation; if DT_WORD_ELLIPSIS only use chars_fit+1 * so that it will be too long */ } } /* Remeasure the string */ GetTextExtentExPointW (hdc, str, *len_str, 0, NULL, NULL, size); }
FskErr winTextBox(FskTextEngineState state, FskBitmap bits, const char *text, UInt32 textLen, FskConstRectangle bounds, FskConstRectangleFloat boundsFloat, FskConstRectangle clipRect, FskConstColorRGBA color, UInt32 blendLevel, UInt32 textSize, UInt32 textStyle, UInt16 hAlign, UInt16 vAlign, FskFixed textExtra, const char *fontName, FskTextFormatCache formatCacheIn) { FskTextFormatCacheGDI formatCache = (FskTextFormatCacheGDI)formatCacheIn; RECT r; UINT flags = DT_SINGLELINE | DT_NOPREFIX | ((kFskTextTruncateEnd & textStyle) ? DT_END_ELLIPSIS : 0); Boolean direct = (blendLevel >= 255) && (NULL != bits->hbmp) && !((kFskTextOutline | kFskTextOutlineHeavy) & textStyle) && !bits->hasAlpha; FskBitmap scratchBits = NULL; HFONT font; HDC dc = direct ? bits->hdc : state->dc; HGDIOBJ saveFont; FskRectangleRecord clip; unsigned char scratchBuffer[256]; int saveCharExtra; // combine bounds and clip to total clip if (NULL == clipRect) { clip = *bounds; } else { if (false == FskRectangleIntersect(clipRect, bounds, &clip)) return kFskErrNone; } if (direct) { if (clipRect) IntersectClipRect(dc, clipRect->x, clipRect->y, clipRect->x + clipRect->width, clipRect->y + clipRect->height); SetRect(&r, bounds->x, bounds->y, bounds->x + bounds->width, bounds->y + bounds->height); SetTextColor(dc, RGB(color->r, color->g, color->b)); } else { FskErr err; const UInt32 kFskTextOffscreenFormat = kFskBitmapFormat24BGR; /* Negative width below indicates that we want a native bitmap */ err = FskBitmapNew(-bounds->width, bounds->height, kFskTextOffscreenFormat, &scratchBits); if (kFskErrNone == err) SetRect(&r, 0, 0, bounds->width, bounds->height); else { FskRectangleRecord b; err = winTextGetBounds(state, bits, text, textLen, textSize, textStyle, textExtra, fontName, &b, NULL, formatCacheIn); if (err) return err; err = FskBitmapNew(-b.width, b.height, kFskTextOffscreenFormat, &scratchBits); if (err) return err; SetRect(&r, 0, 0, b.width, b.height); } dc = scratchBits->hdc; SetTextColor(dc, RGB(255, 255, 255)); } if (NULL == formatCache) saveFont = syncFont(textSize, textStyle, fontName, &font, state); else saveFont = SelectObject(dc, formatCache->font); saveCharExtra = SetTextCharacterExtra(dc, textExtra >> 16); #if 0 switch (hAlign) { case kFskTextAlignLeft: default: flags |= DT_LEFT; break; case kFskTextAlignCenter: flags |= DT_CENTER; break; case kFskTextAlignRight: flags |= DT_RIGHT; break; } switch (vAlign) { case kFskTextAlignTop: default: flags |= DT_TOP; break; case kFskTextAlignCenter: flags |= DT_VCENTER; break; case kFskTextAlignBottom: flags |= DT_BOTTOM; break; } #endif { SIZE sz; UINT align; SInt32 y; char *encodedText = (char *)scratchBuffer; UInt32 encodedTextLen = sizeof(scratchBuffer); if (kFskErrNone != FskTextUTF8ToUnicode16NENoAlloc(text, textLen, (UInt16 *)scratchBuffer, &encodedTextLen)) { if (kFskErrNone != FskMemPtrNew(encodedTextLen, &encodedText)) { FskBitmapDispose(scratchBits); goto done; } FskTextUTF8ToUnicode16NENoAlloc(text, textLen, (UInt16 *)encodedText, &encodedTextLen); } encodedTextLen >>= 1; remapForMicrosoft((UInt16 *)encodedText, encodedTextLen); if (kFskTextTruncateCenter & textStyle) { // work hard to truncate the center, since Windows doesn't support this directly SIZE size; int *widths; if (kFskErrNone == FskMemPtrNew(sizeof(int) * encodedTextLen, (FskMemPtr *)&widths)) { int maxC, i, fitWidth = (r.right - r.left); GetTextExtentExPointW(dc, (WCHAR *)encodedText, encodedTextLen, 32768, &maxC, widths, &size); if (size.cx > fitWidth) { SInt32 currentWidth = size.cx; SInt32 truncBegin, truncEnd; WCHAR ellipsis = 0x2026; SIZE ellipsisSize; UInt16 *uniChars = (UInt16 *)encodedText; for (i = encodedTextLen - 1; i > 0; i--) widths[i] -= widths[i - 1]; GetTextExtentPoint32W(dc, (LPWSTR)&ellipsis, 1, &ellipsisSize); //@@ could use ellipsisWidth in cache here fitWidth -= ellipsisSize.cx; if (fitWidth > 0) { Boolean phase = true; // start towards the end truncBegin = truncEnd = encodedTextLen / 2; while ((currentWidth > fitWidth) && ((truncEnd - truncBegin) != encodedTextLen)) { if (phase) { if (truncEnd < (SInt32)encodedTextLen) { currentWidth -= widths[truncEnd]; truncEnd += 1; } } else { if (0 != truncBegin) { truncBegin -= 1; currentWidth -= widths[truncBegin]; } } phase = !phase; } FskMemMove(&uniChars[truncBegin + 1], &uniChars[truncEnd], (encodedTextLen - truncEnd) * 2); uniChars[truncBegin] = ellipsis; encodedTextLen -= (truncEnd - truncBegin); encodedTextLen += 1; flags &= ~DT_END_ELLIPSIS; } } FskMemPtrDispose(widths); } } #if 0 DrawTextW(dc, (LPWSTR)encodedText, encodedTextLen, &r, flags); #else if (kFskTextTruncateEnd & textStyle) { int fitChars; int stackWidths[256], *widths, width = r.right - r.left; WCHAR ellipsis = 0x2026; SIZE ellipsisSz; if (encodedTextLen < 256) widths = stackWidths; else { if (kFskErrNone != FskMemPtrNew(sizeof(int) * encodedTextLen, (FskMemPtr *)&widths)) { widths = stackWidths; encodedTextLen = 256; } } GetTextExtentExPointW(dc, (WCHAR *)encodedText, encodedTextLen, width, &fitChars, widths, &sz); if ((UInt32)fitChars < encodedTextLen) { // remove trailing white space if (formatCache) { if (!formatCache->haveEllipsisWidth) { GetTextExtentExPointW(dc, (WCHAR *)&ellipsis, 1, 0, NULL, NULL, &ellipsisSz); formatCache->haveEllipsisWidth = true; formatCache->ellipsisWidth = ellipsisSz.cx; } else ellipsisSz.cx = formatCache->ellipsisWidth; } else GetTextExtentExPointW(dc, (WCHAR *)&ellipsis, 1, 0, NULL, NULL, &ellipsisSz); if (width > ellipsisSz.cx) { width -= ellipsisSz.cx; while (fitChars > 2) { UInt16 c = ((UInt16 *)encodedText)[fitChars - 2]; if ((32 != c) && (9 != c) && (0x3000 != c)) break; fitChars -= 1; } // truncate if needed to make room for the ellipsis while ((widths[fitChars - 1] > width) && (fitChars > 2)) fitChars -= 1; // add ellipsis ((UInt16 *)encodedText)[fitChars - 1] = 0x2026; // ellipsis encodedTextLen = fitChars; } else encodedTextLen = 0; } if (widths != stackWidths) FskMemPtrDispose(widths); } else { if (kFskTextAlignCenter == vAlign) GetTextExtentExPointW(dc, (WCHAR *)encodedText, encodedTextLen, 0, NULL, NULL, &sz); } if (kFskTextAlignCenter == vAlign) { y = (r.top + r.bottom - sz.cy) >> 1; align = TA_TOP; } else if (kFskTextAlignTop == vAlign) {