diff --git a/dlls/gdi32/font.c b/dlls/gdi32/font.c index 876e49a7f0f..f23761271b2 100644 --- a/dlls/gdi32/font.c +++ b/dlls/gdi32/font.c @@ -1231,6 +1231,7 @@ BOOL WINAPI GetTextExtentExPointW( HDC hdc, LPCWSTR str, INT count, LPINT dxs = NULL; DC *dc; BOOL ret = FALSE; + TEXTMETRICW tm; TRACE("(%p, %s, %d)\n",hdc,debugstr_wn(str,count),maxExt); @@ -1238,6 +1239,8 @@ BOOL WINAPI GetTextExtentExPointW( HDC hdc, LPCWSTR str, INT count, if (! dc) return FALSE; + GetTextMetricsW(hdc, &tm); + /* If we need to calculate nFit, then we need the partial extents even if the user hasn't provided us with an array. */ if (lpnFit) @@ -1263,22 +1266,49 @@ BOOL WINAPI GetTextExtentExPointW( HDC hdc, LPCWSTR str, INT count, /* Perform device size to world size transformations. */ if (ret) { - INT extra = dc->charExtra, breakRem = dc->breakRem; + INT extra = dc->charExtra, + breakExtra = dc->breakExtra, + breakRem = dc->breakRem, + i; if (dxs) { - INT i; for (i = 0; i < count; ++i) { dxs[i] = abs(INTERNAL_XDSTOWS(dc, dxs[i])); - dxs[i] += (i+1) * extra + breakRem; + dxs[i] += (i+1) * extra; + if (count > 1 && (breakExtra || breakRem) && str[i] == tm.tmBreakChar) + { + dxs[i] += breakExtra; + if (breakRem > 0) + { + breakRem--; + dxs[i]++; + } + } if (dxs[i] <= maxExt) ++nFit; } + breakRem = dc->breakRem; } size->cx = abs(INTERNAL_XDSTOWS(dc, size->cx)); size->cy = abs(INTERNAL_YDSTOWS(dc, size->cy)); - size->cx += count * extra + breakRem; + + if (!dxs && count > 1 && (breakExtra || breakRem)) + { + for (i = 0; i < count; i++) + { + if (str[i] == tm.tmBreakChar) + { + size->cx += breakExtra; + if (breakRem > 0) + { + breakRem--; + (size->cx)++; + } + } + } + } } if (lpnFit) @@ -1812,12 +1842,15 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x, INT y, UINT flags, SIZE sz; RECT rc; BOOL done_extents = FALSE; - INT width, xwidth = 0, ywidth = 0; + INT width = 0, xwidth = 0, ywidth = 0; DWORD type; DC * dc = DC_GetDCUpdate( hdc ); + INT breakRem; if (!dc) return FALSE; + breakRem = dc->breakRem; + if (flags & (ETO_NUMERICSLOCAL | ETO_NUMERICSLATIN | ETO_PDY)) FIXME("flags ETO_NUMERICSLOCAL | ETO_NUMERICSLATIN | ETO_PDY unimplemented\n"); @@ -1927,8 +1960,7 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x, INT y, UINT flags, y = pt.y; char_extra = GetTextCharacterExtra(hdc); - width = 0; - if(char_extra || dc->breakExtra || lpDx) + if(char_extra || dc->breakExtra || breakRem || lpDx) { UINT i; SIZE tmpsz; @@ -1949,9 +1981,14 @@ BOOL WINAPI ExtTextOutW( HDC hdc, INT x, INT y, UINT flags, deltas[i] = tmpsz.cx; } - if (!(flags & ETO_GLYPH_INDEX) && dc->breakExtra && reordered_str[i] == tm.tmBreakChar) + if (!(flags & ETO_GLYPH_INDEX) && (dc->breakExtra || breakRem) && reordered_str[i] == tm.tmBreakChar) { deltas[i] = deltas[i] + dc->breakExtra; + if (breakRem > 0) + { + breakRem--; + deltas[i]++; + } } deltas[i] = INTERNAL_XWSTODS(dc, deltas[i]); width += deltas[i]; diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index 5dddf3a8c2e..e8e25270885 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -887,6 +887,129 @@ static BOOL get_glyph_indices(INT charset, UINT code_page, WORD *idx, UINT count return TRUE; } +static void testJustification(HDC hdc, PSTR str, RECT *clientArea) +{ + INT x, y, + breakCount, + outputWidth = 0, /* to test TabbedTextOut() */ + justifiedWidth = 0, /* to test GetTextExtentExPointW() */ + areaWidth = clientArea->right - clientArea->left, + nErrors = 0, e; + BOOL lastExtent = FALSE; + PSTR pFirstChar, pLastChar; + SIZE size; + TEXTMETRICA tm; + struct err + { + char extent[100]; + int GetTextExtentExPointWWidth; + int TabbedTextOutWidth; + } error[10]; + + GetTextMetricsA(hdc, &tm); + y = clientArea->top; + do { + breakCount = 0; + while (*str == tm.tmBreakChar) str++; /* skip leading break chars */ + pFirstChar = str; + + do { + pLastChar = str; + + /* if not at the end of the string, ... */ + if (*str == '\0') break; + /* ... add the next word to the current extent */ + while (*str != '\0' && *str++ != tm.tmBreakChar); + breakCount++; + SetTextJustification(hdc, 0, 0); + GetTextExtentPoint32(hdc, pFirstChar, str - pFirstChar - 1, &size); + } while ((int) size.cx < areaWidth); + + /* ignore trailing break chars */ + breakCount--; + while (*(pLastChar - 1) == tm.tmBreakChar) + { + pLastChar--; + breakCount--; + } + + if (*str == '\0' || breakCount <= 0) pLastChar = str; + + SetTextJustification(hdc, 0, 0); + GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size); + + /* do not justify the last extent */ + if (*str != '\0' && breakCount > 0) + { + SetTextJustification(hdc, areaWidth - size.cx, breakCount); + GetTextExtentPoint32(hdc, pFirstChar, pLastChar - pFirstChar, &size); + justifiedWidth = size.cx; + } + else lastExtent = TRUE; + + x = clientArea->left; + + outputWidth = LOWORD(TabbedTextOut( + hdc, x, y, pFirstChar, pLastChar - pFirstChar, + 0, NULL, 0)); + /* catch errors and report them */ + if (!lastExtent && ((outputWidth != areaWidth) || (justifiedWidth != areaWidth))) + { + memset(error[nErrors].extent, 0, 100); + memcpy(error[nErrors].extent, pFirstChar, pLastChar - pFirstChar); + error[nErrors].TabbedTextOutWidth = outputWidth; + error[nErrors].GetTextExtentExPointWWidth = justifiedWidth; + nErrors++; + } + + y += size.cy; + str = pLastChar; + } while (*str && y < clientArea->bottom); + + for (e = 0; e < nErrors; e++) + { + ok(error[e].TabbedTextOutWidth == areaWidth, + "The output text (\"%s\") width should be %d, not %d.\n", + error[e].extent, areaWidth, error[e].TabbedTextOutWidth); + /* The width returned by GetTextExtentPoint32() is exactly the same + returned by GetTextExtentExPointW() - see dlls/gdi32/font.c */ + ok(error[e].GetTextExtentExPointWWidth == areaWidth, + "GetTextExtentPointW() for \"%s\" should have returned a width of %d, not %d.\n", + error[e].extent, areaWidth, error[e].GetTextExtentExPointWWidth); + } +} + +static void test_SetTextJustification(void) +{ + HDC hdc = GetDC(0); + RECT clientArea = {0, 0, 400, 400}; + LOGFONTA lf; + HFONT hfont; + static char testText[] = + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do " + "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut " + "enim ad minim veniam, quis nostrud exercitation ullamco laboris " + "nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in " + "reprehenderit in voluptate velit esse cillum dolore eu fugiat " + "nulla pariatur. Excepteur sint occaecat cupidatat non proident, " + "sunt in culpa qui officia deserunt mollit anim id est laborum."; + + memset(&lf, 0, sizeof lf); + lf.lfCharSet = ANSI_CHARSET; + lf.lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf.lfWeight = FW_DONTCARE; + lf.lfHeight = 20; + lf.lfQuality = DEFAULT_QUALITY; + lstrcpyA(lf.lfFaceName, "Times New Roman"); + hfont = create_font("Times New Roman", &lf); + SelectObject(hdc, hfont); + + testJustification(hdc, testText, &clientArea); + + DeleteObject(hfont); + ReleaseDC(0, hdc); +} + static void test_font_charset(void) { static struct charset_data @@ -940,5 +1063,6 @@ START_TEST(font) test_GetGlyphIndices(); test_GetKerningPairs(); test_GetOutlineTextMetrics(); + test_SetTextJustification(); test_font_charset(); }