gdi32: Add helper functions for GetTextExtentExPoint and fix handling of text justification.

This commit is contained in:
Alexandre Julliard 2012-12-18 17:41:42 +01:00
parent 17f098869f
commit 1c2f23cf2f
3 changed files with 202 additions and 110 deletions

View File

@ -308,6 +308,83 @@ static UINT get_default_smoothing( HKEY key )
return GGO_GRAY4_BITMAP; return GGO_GRAY4_BITMAP;
} }
/* compute positions for text rendering, in device coords */
static BOOL get_char_positions( DC *dc, const WCHAR *str, INT count, INT *dx, SIZE *size )
{
TEXTMETRICW tm;
PHYSDEV dev = GET_DC_PHYSDEV( dc, pGetTextExtentExPoint );
size->cx = size->cy = 0;
if (!count) return TRUE;
dev = GET_DC_PHYSDEV( dc, pGetTextMetrics );
dev->funcs->pGetTextMetrics( dev, &tm );
if (!dev->funcs->pGetTextExtentExPoint( dev, str, count, 0, NULL, dx, size )) return FALSE;
if (dc->breakExtra || dc->breakRem)
{
int i, space = 0, rem = dc->breakRem;
for (i = 0; i < count; i++)
{
if (str[i] == tm.tmBreakChar)
{
space += dc->breakExtra;
if (rem > 0)
{
space++;
rem--;
}
}
dx[i] += space;
}
}
size->cx = dx[count - 1];
size->cy = tm.tmHeight;
return TRUE;
}
/* compute positions for text rendering, in device coords */
static BOOL get_char_positions_indices( DC *dc, const WORD *indices, INT count, INT *dx, SIZE *size )
{
TEXTMETRICW tm;
PHYSDEV dev = GET_DC_PHYSDEV( dc, pGetTextExtentExPoint );
size->cx = size->cy = 0;
if (!count) return TRUE;
dev = GET_DC_PHYSDEV( dc, pGetTextMetrics );
dev->funcs->pGetTextMetrics( dev, &tm );
if (!dev->funcs->pGetTextExtentExPointI( dev, indices, count, 0, NULL, dx, size )) return FALSE;
if (dc->breakExtra || dc->breakRem)
{
WORD space_index;
int i, space = 0, rem = dc->breakRem;
dev = GET_DC_PHYSDEV( dc, pGetGlyphIndices );
dev->funcs->pGetGlyphIndices( dev, &tm.tmBreakChar, 1, &space_index, 0 );
for (i = 0; i < count; i++)
{
if (indices[i] == space_index)
{
space += dc->breakExtra;
if (rem > 0)
{
space++;
rem--;
}
}
dx[i] += space;
}
}
size->cx = dx[count - 1];
size->cy = tm.tmHeight;
return TRUE;
}
/*********************************************************************** /***********************************************************************
* GdiGetCodePage (GDI32.@) * GdiGetCodePage (GDI32.@)
@ -1015,20 +1092,45 @@ BOOL WINAPI GetTextExtentPoint32W(
BOOL WINAPI GetTextExtentExPointI( HDC hdc, const WORD *indices, INT count, INT max_ext, BOOL WINAPI GetTextExtentExPointI( HDC hdc, const WORD *indices, INT count, INT max_ext,
LPINT nfit, LPINT dxs, LPSIZE size ) LPINT nfit, LPINT dxs, LPSIZE size )
{ {
PHYSDEV dev;
BOOL ret;
DC *dc; DC *dc;
int i;
BOOL ret;
INT buffer[256], *pos = dxs;
if (count < 0) return FALSE; if (count < 0) return FALSE;
dc = get_dc_ptr( hdc ); dc = get_dc_ptr( hdc );
if (!dc) return FALSE; if (!dc) return FALSE;
dev = GET_DC_PHYSDEV( dc, pGetTextExtentExPointI ); if (!dxs)
ret = dev->funcs->pGetTextExtentExPointI( dev, indices, count, max_ext, nfit, dxs, size ); {
size->cx = abs(INTERNAL_XDSTOWS(dc, size->cx)); pos = buffer;
size->cy = abs(INTERNAL_YDSTOWS(dc, size->cy)); if (count > 256 && !(pos = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*pos) )))
size->cx += count * dc->charExtra; {
release_dc_ptr( dc );
return FALSE;
}
}
ret = get_char_positions_indices( dc, indices, count, pos, size );
if (ret)
{
if (dxs || nfit)
{
for (i = 0; i < count; i++)
{
unsigned int dx = abs( INTERNAL_XDSTOWS( dc, pos[i] )) + (i + 1) * dc->charExtra;
if (dx > (unsigned int)max_ext) break;
if (dxs) dxs[i] = dx;
}
if (nfit) *nfit = i;
}
size->cx = abs( INTERNAL_XDSTOWS( dc, size->cx )) + count * dc->charExtra;
size->cy = abs( INTERNAL_YDSTOWS( dc, size->cy ));
}
if (pos != buffer && pos != dxs) HeapFree( GetProcessHeap(), 0, pos );
release_dc_ptr( dc ); release_dc_ptr( dc );
TRACE("(%p %p %d %p): returning %d x %d\n", TRACE("(%p %p %d %p): returning %d x %d\n",
@ -1091,6 +1193,7 @@ BOOL WINAPI GetTextExtentExPointA( HDC hdc, LPCSTR str, INT count,
LPWSTR p = NULL; LPWSTR p = NULL;
if (count < 0) return FALSE; if (count < 0) return FALSE;
if (maxExt < -1) return FALSE;
if (alpDx) if (alpDx)
{ {
@ -1122,126 +1225,52 @@ BOOL WINAPI GetTextExtentExPointA( HDC hdc, LPCSTR str, INT count,
* *
* Return the size of the string as it would be if it was output properly by * Return the size of the string as it would be if it was output properly by
* e.g. TextOut. * e.g. TextOut.
*
* This should include
* - Intercharacter spacing
* - justification spacing (not yet done)
* - kerning? see below
*
* Kerning. Since kerning would be carried out by the rendering code it should
* be done by the driver. However they don't support it yet. Also I am not
* yet persuaded that (certainly under Win95) any kerning is actually done.
*
* str: According to MSDN this should be null-terminated. That is not true; a
* null will not terminate it early.
* size: Certainly under Win95 this appears buggy or weird if *lpnFit is less
* than count. I have seen it be either the size of the full string or
* 1 less than the size of the full string. I have not seen it bear any
* resemblance to the portion that would fit.
* lpnFit: What exactly is fitting? Stupidly, in my opinion, it includes the
* trailing intercharacter spacing and any trailing justification.
*
* FIXME
* Currently we do this by measuring each character etc. We should do it by
* passing the request to the driver, perhaps by extending the
* pGetTextExtentPoint function to take the alpDx argument. That would avoid
* thinking about kerning issues and rounding issues in the justification.
*/ */
BOOL WINAPI GetTextExtentExPointW( HDC hdc, LPCWSTR str, INT count, INT max_ext,
BOOL WINAPI GetTextExtentExPointW( HDC hdc, LPCWSTR str, INT count, LPINT nfit, LPINT dxs, LPSIZE size )
INT maxExt, LPINT lpnFit,
LPINT alpDx, LPSIZE size )
{ {
INT nFit = 0;
LPINT dxs = NULL;
DC *dc; DC *dc;
BOOL ret = FALSE; int i;
TEXTMETRICW tm; BOOL ret;
PHYSDEV dev; INT buffer[256], *pos = dxs;
TRACE("(%p, %s, %d)\n",hdc,debugstr_wn(str,count),maxExt);
if (count < 0) return FALSE; if (count < 0) return FALSE;
dc = get_dc_ptr(hdc); dc = get_dc_ptr(hdc);
if (!dc) return FALSE; if (!dc) return FALSE;
GetTextMetricsW(hdc, &tm); if (!dxs)
/* 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)
{ {
dxs = alpDx ? alpDx : HeapAlloc(GetProcessHeap(), 0, count * sizeof alpDx[0]); pos = buffer;
if (! dxs) if (count > 256 && !(pos = HeapAlloc( GetProcessHeap(), 0, count * sizeof(*pos) )))
{ {
release_dc_ptr(dc); release_dc_ptr( dc );
SetLastError(ERROR_OUTOFMEMORY); return FALSE;
return FALSE; }
}
} }
else
dxs = alpDx;
dev = GET_DC_PHYSDEV( dc, pGetTextExtentExPoint ); ret = get_char_positions( dc, str, count, pos, size );
ret = dev->funcs->pGetTextExtentExPoint(dev, str, count, 0, NULL, dxs, size);
/* Perform device size to world size transformations. */
if (ret) if (ret)
{ {
INT extra = abs(INTERNAL_XWSTODS(dc, dc->charExtra)), if (dxs || nfit)
breakExtra = dc->breakExtra,
breakRem = dc->breakRem,
i;
if (dxs)
{
for (i = 0; i < count; ++i)
{
dxs[i] += (i+1) * extra;
if (count > 1 && (breakExtra || breakRem) && str[i] == tm.tmBreakChar)
{
dxs[i] += breakExtra;
if (breakRem > 0)
{
breakRem--;
dxs[i]++;
}
}
dxs[i] = abs(INTERNAL_XDSTOWS(dc, dxs[i]));
if (dxs[i] <= maxExt)
++nFit;
}
}
else if (count > 1 && (breakExtra || breakRem))
{ {
for (i = 0; i < count; i++) for (i = 0; i < count; i++)
{ {
if (str[i] == tm.tmBreakChar) unsigned int dx = abs( INTERNAL_XDSTOWS( dc, pos[i] )) + (i + 1) * dc->charExtra;
{ if (dx > (unsigned int)max_ext) break;
size->cx += breakExtra; if (dxs) dxs[i] = dx;
if (breakRem > 0)
{
breakRem--;
(size->cx)++;
}
}
} }
if (nfit) *nfit = i;
} }
size->cx += count * extra;
size->cx = abs(INTERNAL_XDSTOWS(dc, size->cx)); size->cx = abs( INTERNAL_XDSTOWS( dc, size->cx )) + count * dc->charExtra;
size->cy = abs(INTERNAL_YDSTOWS(dc, size->cy)); size->cy = abs( INTERNAL_YDSTOWS( dc, size->cy ));
} }
if (lpnFit) if (pos != buffer && pos != dxs) HeapFree( GetProcessHeap(), 0, pos );
*lpnFit = nFit;
if (! alpDx)
HeapFree(GetProcessHeap(), 0, dxs);
release_dc_ptr( dc ); release_dc_ptr( dc );
TRACE("returning %d %d x %d\n",nFit,size->cx,size->cy); TRACE("(%p, %s, %d) returning %dx%d\n", hdc, debugstr_wn(str,count), max_ext, size->cx, size->cy );
return ret; return ret;
} }

View File

@ -45,6 +45,8 @@ static BOOL (WINAPI *pGetCharABCWidthsFloatW)(HDC hdc, UINT first, UINT last, L
static DWORD (WINAPI *pGetFontUnicodeRanges)(HDC hdc, LPGLYPHSET lpgs); static DWORD (WINAPI *pGetFontUnicodeRanges)(HDC hdc, LPGLYPHSET lpgs);
static DWORD (WINAPI *pGetGlyphIndicesA)(HDC hdc, LPCSTR lpstr, INT count, LPWORD pgi, DWORD flags); static DWORD (WINAPI *pGetGlyphIndicesA)(HDC hdc, LPCSTR lpstr, INT count, LPWORD pgi, DWORD flags);
static DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags); static DWORD (WINAPI *pGetGlyphIndicesW)(HDC hdc, LPCWSTR lpstr, INT count, LPWORD pgi, DWORD flags);
static BOOL (WINAPI *pGetTextExtentExPointI)(HDC hdc, const WORD *indices, INT count, INT max_ext,
LPINT nfit, LPINT dxs, LPSIZE size );
static BOOL (WINAPI *pGdiRealizationInfo)(HDC hdc, DWORD *); static BOOL (WINAPI *pGdiRealizationInfo)(HDC hdc, DWORD *);
static HFONT (WINAPI *pCreateFontIndirectExA)(const ENUMLOGFONTEXDV *); static HFONT (WINAPI *pCreateFontIndirectExA)(const ENUMLOGFONTEXDV *);
static HANDLE (WINAPI *pAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *); static HANDLE (WINAPI *pAddFontMemResourceEx)(PVOID, DWORD, PVOID, DWORD *);
@ -69,6 +71,7 @@ static void init(void)
pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges"); pGetFontUnicodeRanges = (void *)GetProcAddress(hgdi32, "GetFontUnicodeRanges");
pGetGlyphIndicesA = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesA"); pGetGlyphIndicesA = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesA");
pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW"); pGetGlyphIndicesW = (void *)GetProcAddress(hgdi32, "GetGlyphIndicesW");
pGetTextExtentExPointI = (void *)GetProcAddress(hgdi32, "GetTextExtentExPointI");
pGdiRealizationInfo = (void *)GetProcAddress(hgdi32, "GdiRealizationInfo"); pGdiRealizationInfo = (void *)GetProcAddress(hgdi32, "GdiRealizationInfo");
pCreateFontIndirectExA = (void *)GetProcAddress(hgdi32, "CreateFontIndirectExA"); pCreateFontIndirectExA = (void *)GetProcAddress(hgdi32, "CreateFontIndirectExA");
pAddFontMemResourceEx = (void *)GetProcAddress(hgdi32, "AddFontMemResourceEx"); pAddFontMemResourceEx = (void *)GetProcAddress(hgdi32, "AddFontMemResourceEx");
@ -1294,20 +1297,20 @@ static void test_text_extents(void)
fit1 = fit2 = -215; fit1 = fit2 = -215;
ret = GetTextExtentExPointA(hdc, "One", 3, -1, &fit1, NULL, &sz); ret = GetTextExtentExPointA(hdc, "One", 3, -1, &fit1, NULL, &sz);
ok(ret == TRUE, "got %d\n", ret); ok(ret == TRUE, "got %d\n", ret);
todo_wine ok(fit1 == 3, "fit = %d\n", fit1); ok(fit1 == 3, "fit = %d\n", fit1);
ret = GetTextExtentExPointW(hdc, wt, 3, -1, &fit2, NULL, &sz); ret = GetTextExtentExPointW(hdc, wt, 3, -1, &fit2, NULL, &sz);
ok(ret == TRUE, "got %d\n", ret); ok(ret == TRUE, "got %d\n", ret);
todo_wine ok(fit2 == 3, "fit = %d\n", fit2); ok(fit2 == 3, "fit = %d\n", fit2);
/* max_extent = -2 is interpreted similarly, but the Ansi version /* max_extent = -2 is interpreted similarly, but the Ansi version
* rejects it while the Unicode one accepts it */ * rejects it while the Unicode one accepts it */
fit1 = fit2 = -215; fit1 = fit2 = -215;
ret = GetTextExtentExPointA(hdc, "One", 3, -2, &fit1, NULL, &sz); ret = GetTextExtentExPointA(hdc, "One", 3, -2, &fit1, NULL, &sz);
todo_wine ok(ret == FALSE, "got %d\n", ret); ok(ret == FALSE, "got %d\n", ret);
todo_wine ok(fit1 == -215, "fit = %d\n", fit1); ok(fit1 == -215, "fit = %d\n", fit1);
ret = GetTextExtentExPointW(hdc, wt, 3, -2, &fit2, NULL, &sz); ret = GetTextExtentExPointW(hdc, wt, 3, -2, &fit2, NULL, &sz);
ok(ret == TRUE, "got %d\n", ret); ok(ret == TRUE, "got %d\n", ret);
todo_wine ok(fit2 == 3, "fit = %d\n", fit2); ok(fit2 == 3, "fit = %d\n", fit2);
hfont = SelectObject(hdc, hfont); hfont = SelectObject(hdc, hfont);
DeleteObject(hfont); DeleteObject(hfont);
@ -1834,6 +1837,9 @@ static void test_SetTextJustification(void)
LOGFONTA lf; LOGFONTA lf;
HFONT hfont; HFONT hfont;
HWND hwnd; HWND hwnd;
SIZE size, expect;
int i;
WORD indices[2];
static char testText[] = static char testText[] =
"Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do " "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
"eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut " "eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut "
@ -1859,17 +1865,74 @@ static void test_SetTextJustification(void)
testJustification(hdc, testText, &clientArea); testJustification(hdc, testText, &clientArea);
if (!pGetGlyphIndicesA || !pGetTextExtentExPointI) goto done;
pGetGlyphIndicesA( hdc, "A ", 2, indices, 0 );
SetTextJustification(hdc, 0, 0);
GetTextExtentPoint32(hdc, " ", 1, &expect);
GetTextExtentPoint32(hdc, " ", 3, &size);
ok( size.cx == 3 * expect.cx, "wrong size %d/%d\n", size.cx, expect.cx );
SetTextJustification(hdc, 4, 1);
GetTextExtentPoint32(hdc, " ", 1, &size);
ok( size.cx == expect.cx + 4, "wrong size %d/%d\n", size.cx, expect.cx );
SetTextJustification(hdc, 9, 2);
GetTextExtentPoint32(hdc, " ", 2, &size);
ok( size.cx == 2 * expect.cx + 9, "wrong size %d/%d\n", size.cx, expect.cx );
SetTextJustification(hdc, 7, 3);
GetTextExtentPoint32(hdc, " ", 3, &size);
ok( size.cx == 3 * expect.cx + 7, "wrong size %d/%d\n", size.cx, expect.cx );
SetTextJustification(hdc, 7, 3);
SetTextCharacterExtra(hdc, 2 );
GetTextExtentPoint32(hdc, " ", 3, &size);
ok( size.cx == 3 * (expect.cx + 2) + 7, "wrong size %d/%d\n", size.cx, expect.cx );
SetTextJustification(hdc, 0, 0);
SetTextCharacterExtra(hdc, 0);
size.cx = size.cy = 1234;
GetTextExtentPoint32(hdc, " ", 0, &size);
ok( size.cx == 0 && size.cy == 0, "wrong size %d,%d\n", size.cx, size.cy );
pGetTextExtentExPointI(hdc, indices, 2, -1, NULL, NULL, &expect);
SetTextJustification(hdc, 5, 1);
pGetTextExtentExPointI(hdc, indices, 2, -1, NULL, NULL, &size);
ok( size.cx == expect.cx + 5, "wrong size %d/%d\n", size.cx, expect.cx );
SetTextJustification(hdc, 0, 0);
SetMapMode( hdc, MM_ANISOTROPIC ); SetMapMode( hdc, MM_ANISOTROPIC );
SetWindowExtEx( hdc, 2, 2, NULL ); SetWindowExtEx( hdc, 2, 2, NULL );
GetClientRect( hwnd, &clientArea ); GetClientRect( hwnd, &clientArea );
DPtoLP( hdc, (POINT *)&clientArea, 2 ); DPtoLP( hdc, (POINT *)&clientArea, 2 );
testJustification(hdc, testText, &clientArea); testJustification(hdc, testText, &clientArea);
GetTextExtentPoint32(hdc, "A", 1, &expect);
for (i = 0; i < 10; i++)
{
SetTextCharacterExtra(hdc, i);
GetTextExtentPoint32(hdc, "A", 1, &size);
ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
}
SetTextCharacterExtra(hdc, 0);
pGetTextExtentExPointI(hdc, indices, 1, -1, NULL, NULL, &expect);
for (i = 0; i < 10; i++)
{
SetTextCharacterExtra(hdc, i);
pGetTextExtentExPointI(hdc, indices, 1, -1, NULL, NULL, &size);
ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
}
SetTextCharacterExtra(hdc, 0);
SetViewportExtEx( hdc, 3, 3, NULL ); SetViewportExtEx( hdc, 3, 3, NULL );
GetClientRect( hwnd, &clientArea ); GetClientRect( hwnd, &clientArea );
DPtoLP( hdc, (POINT *)&clientArea, 2 ); DPtoLP( hdc, (POINT *)&clientArea, 2 );
testJustification(hdc, testText, &clientArea); testJustification(hdc, testText, &clientArea);
GetTextExtentPoint32(hdc, "A", 1, &expect);
for (i = 0; i < 10; i++)
{
SetTextCharacterExtra(hdc, i);
GetTextExtentPoint32(hdc, "A", 1, &size);
ok( size.cx == expect.cx + i, "wrong size %d/%d+%d\n", size.cx, expect.cx, i );
}
done:
DeleteObject(hfont); DeleteObject(hfont);
ReleaseDC(hwnd, hdc); ReleaseDC(hwnd, hdc);
DestroyWindow(hwnd); DestroyWindow(hwnd);

View File

@ -3074,7 +3074,7 @@ static void test_string_functions(void)
expectf(0.0, bounds.X); expectf(0.0, bounds.X);
expectf(0.0, bounds.Y); expectf(0.0, bounds.Y);
expectf_(char_bounds.Width, bounds.Width, 0.01); expectf_(char_bounds.Width, bounds.Width, 0.01);
todo_wine expectf_(char_bounds.Height + char_height * 3, bounds.Height, 0.05); expectf_(char_bounds.Height + char_height * 3, bounds.Height, 0.05);
expect(6, codepointsfitted); expect(6, codepointsfitted);
todo_wine expect(4, linesfilled); todo_wine expect(4, linesfilled);