📄 font.c
字号:
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 ( ; ; )
{
wcsncpy (str + *len_str, ELLIPSISW, len_ellipsis);
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)
{
wcsncpy (modstr, str, *len_str);
*(str+*len_str) = '\0';
}
}
/*********************************************************************
* 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)
{
unsigned int len_ellipsis;
int len_trailing;
int len_under;
WCHAR *lastBkSlash, *lastFwdSlash, *lastSlash;
len_ellipsis = wcslen (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 = wcsrchr (str, BACK_SLASH);
lastFwdSlash = wcsrchr (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));
wcsncpy (lastSlash, ELLIPSISW, len_ellipsis);
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)
{
wcsncpy (modstr, str, *len_str);
*(str+*len_str) = '\0';
}
}
/*********************************************************************
* TEXT_WordBreak (static)
*
* Perform wordbreak processing on the given string
*
* Assumes that DT_WORDBREAK has been specified and not all the characters
* fit. Note that this function should even be called when the first character
* that doesn't fit is known to be a space or tab, so that it can swallow them.
*
* Note that the Windows processing has some strange properties.
* 1. If the text is left-justified and there is room for some of the spaces
* that follow the last word on the line then those that fit are included on
* the line.
* 2. If the text is centred or right-justified and there is room for some of
* the spaces that follow the last word on the line then all but one of those
* that fit are included on the line.
* 3. (Reasonable behaviour) If the word breaking causes a space to be the first
* character of a new line it will be skipped.
*
* Arguments
* hdc [in] The handle to the DC that defines the font.
* str [in/out] The string that needs to be broken.
* 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
* format [in] The format flags in effect
* chars_fit [in] The maximum number of characters of str that are already
* known to fit; chars_fit+1 is known not to fit.
* chars_used [out] The number of characters of str that have been "used" and
* do not need to be included in later text. For example this will
* include any spaces that have been discarded from the start of
* the next line.
* size [out] The size of the returned text in logical coordinates
*
* Pedantic assumption - Assumes that the text length is monotonically
* increasing with number of characters (i.e. no weird kernings)
*
* Algorithm
*
* Work back from the last character that did fit to either a space or the last
* character of a word, whichever is met first.
* If there was one or the first character didn't fit then
* If the text is centred or right justified and that one character was a
* space then break the line before that character
* Otherwise break the line after that character
* and if the next character is a space then discard it.
* Suppose there was none (and the first character did fit).
* If Break Within Word is permitted
* break the word after the last character that fits (there must be
* at least one; none is caught earlier).
* Otherwise
* discard any trailing space.
* include the whole word; it may be ellipsified later
*
* Break Within Word is permitted under a set of circumstances that are not
* totally clear yet. Currently our best guess is:
* If DT_EDITCONTROL is in effect and neither DT_WORD_ELLIPSIS nor
* DT_PATH_ELLIPSIS is
*/
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)
;
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 = (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_SkipChars
*
* Skip over the given number of characters, bearing in mind prefix
* substitution and the fact that a character may take more than one
* WCHAR (Unicode surrogates are two words long) (and there may have been
* a trailing &)
*
* Parameters
* new_count [out] The updated count
* new_str [out] The updated pointer
* start_count [in] The count of remaining characters corresponding to the
* start of the string
* start_str [in] The starting point of the string
* max [in] The number of characters actually in this segment of the
* string (the & counts)
* n [in] The number of characters to skip (if prefix then
* &c counts as one)
* prefix [in] Apply prefix substitution
*
* Return Values
* none
*
* Remarks
* There must be at least n characters in the string
* We need max because the "line" may have ended with a & followed by a tab
* or newline etc. which we don't want to swallow
*/
static void TEXT_SkipChars (int *new_count, const WCHAR **new_str,
int start_count, const WCHAR *start_str,
int max, int n, int prefix)
{
/* This is specific to wide characters, MSDN doesn't say anything much
* about Unicode surrogates yet and it isn't clear if _wcsinc will
* correctly handle them so we'll just do this the easy way for now
*/
if (prefix)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -