From: Torge Matthies tmatthies@codeweavers.com
These rules only apply when the glyph is passed by unicode code point, not by glyph index in the font (GGO_GLYPH_INDEX).
Signed-off-by: Torge Matthies tmatthies@codeweavers.com --- dlls/gdi32/tests/font.c | 4 -- dlls/gdiplus/tests/graphics.c | 2 +- dlls/win32u/font.c | 108 ++++++++++++++++++++++++++++++---- 3 files changed, 99 insertions(+), 15 deletions(-)
diff --git a/dlls/gdi32/tests/font.c b/dlls/gdi32/tests/font.c index 2388b91b5e6..65e8064491c 100644 --- a/dlls/gdi32/tests/font.c +++ b/dlls/gdi32/tests/font.c @@ -1462,11 +1462,8 @@ static void test_text_extents(void) ok(extents[len-1] == sz1.cx, "GetTextExtentExPointW extents and size don't match\n"); ok(0 <= fit1 && fit1 <= len, "GetTextExtentExPointW generated illegal value %d for fit\n", fit1); ok(0 < fit1, "GetTextExtentExPointW says we can't even fit one letter in 32767 logical units\n"); - todo_wine ok(extents[2] == extents[3], "Carriage return has width: %d != %d\n", extents[2], extents[3]); - todo_wine ok(extents[3] == extents[4], "Line feed has width: %d != %d\n", extents[3], extents[4]); - todo_wine ok(extents[9] == extents[10], "Unix-style newline has width: %d != %d\n", extents[3], extents[4]); GetTextExtentExPointW(hdc, wt, len, extents[2], &fit2, NULL, &sz2); ok(sz1.cx == sz2.cx && sz1.cy == sz2.cy, "GetTextExtentExPointW returned different sizes for the same string\n"); @@ -1474,7 +1471,6 @@ static void test_text_extents(void) GetTextExtentExPointW(hdc, wt, len, extents[2]-1, &fit2, NULL, &sz2); ok(fit2 == 2, "GetTextExtentExPointW extents isn't consistent with fit\n"); GetTextExtentExPointW(hdc, wt, len, extents[2]+1, &fit2, NULL, &sz2); - todo_wine ok(fit2 == 5, "GetTextExtentExPointW newline doesn't fit in 1-pixel space\n"); GetTextExtentExPointW(hdc, wt, 2, 0, NULL, extents + 2, &sz2); ok(extents[0] == extents[2] && extents[1] == extents[3], diff --git a/dlls/gdiplus/tests/graphics.c b/dlls/gdiplus/tests/graphics.c index 89baff64fa4..309a1b1e8ed 100644 --- a/dlls/gdiplus/tests/graphics.c +++ b/dlls/gdiplus/tests/graphics.c @@ -3358,7 +3358,7 @@ static void test_string_functions(void) expectf_(char_bounds.Width, bounds.Width, 0.01); expectf_(char_bounds.Height + char_height * 3, bounds.Height, 0.05); expect(6, codepointsfitted); - todo_wine expect(4, linesfilled); + expect(4, linesfilled);
for (i = 0; i < 4; i++) regions[i] = (GpRegion *)0xdeadbeef; diff --git a/dlls/win32u/font.c b/dlls/win32u/font.c index c21d87c1fbc..977f079285b 100644 --- a/dlls/win32u/font.c +++ b/dlls/win32u/font.c @@ -3830,15 +3830,72 @@ static UINT get_glyph_index_linked( struct gdi_font **font, UINT glyph ) return 0; }
+struct zero_width_unicode_range +{ + WORD start; + WORD count; +} zw_ranges[] = { +#define X1(c) { (c), 1 } +#define X2(start, end) { (start), (end) + 1 - (start) } + + X2(0x001c, 0x001f), /* information separators */ + X2(0x0080, 0x009f), /* C1 controls */ + X1(0x034f), /* COMBINING GRAPHEME JOINER */ /* squishing */ + X1(0x0605), /* ARABIC NUMBER MARK ABOVE */ + X1(0x061c), /* ARABIC LETTER MARK */ + X1(0x0674), /* ARABIC LETTER HIGH HAMZA */ + X2(0x06e5, 0x06e6), /* ARABIC SMALL WAW, ARABIC SMALL YEH */ + X1(0x09fe), /* BENGALI SANDHI MARK */ + X1(0x0a51), /* GURMUKHI SIGN UDAAT */ + X1(0x0a75), /* GURMUKHI SIGN YAKASH */ + X2(0x0afa, 0x0aff), /* GUJARATI SIGN SUKUN - GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE */ + X1(0x0c04), /* TELUGU SIGN COMBINING ANUSVARA ABOVE */ + X1(0x0d00), /* MALAYALAM SIGN COMBINING ANUSVARA ABOVE */ + X2(0x0d3b, 0x0d3c), /* MALAYALAM SIGN VERTICAL BAR VIRAMA, MALAYALAM SIGN CIRCULAR VIRAMA */ + X1(0x0e31), /* THAI CHARACTER MAI HAN-AKAT */ + X1(0x0e33), /* THAI CHARACTER SARA AM */ /* squishing */ + X2(0x0e34, 0x0e3a), /* THAI CHARACTER SARA I - THAI CHARACTER PHINTHU */ + X2(0x0e47, 0x0e4e), /* THAI CHARACTER MAITAIKHU - THAI CHARACTER YAMAKKAN */ + X1(0x180e), /* MONGOLIAN VOWEL SEPARATOR */ + X1(0x1878), /* MONGOLIAN LETTER CHA WITH TWO DOTS */ + X1(0x1ce1), /* VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA */ + X1(0x1cf7), /* VEDIC SIGN ATIKRAMA */ + X2(0x200b, 0x200f), /* ZWSP, ZWNJ, ZWJ, LRM, RLM */ + X2(0x2029, 0x202e), /* Paragraph separator, LRE, RLE, PDF, LRO, RLO */ + X2(0x2061, 0x2064), /* invisible operators */ + X2(0x206a, 0x206f), /* Deprecated control characters */ + X2(0x302a, 0x302d), /* IDEOGRAPHIC LEVEL TONE MARK - IDEOGRAPHIC ENTERING TONE MARK */ + X2(0x3099, 0x309a), /* COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK, + COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ + X1(0xa8fa), /* DEVANAGARI CARET */ + X1(0xa8ff), /* DEVANAGARI VOWEL SIGN AY */ + X2(0xfbb2, 0xfbbb), /* ARABIC SYMBOL DOT ABOVE - ARABIC SYMBOL FOUR DOTS BELOW */ + X2(0xfbc0, 0xfbc1), /* ARABIC SYMBOL SMALL TAH ABOVE, ARABIC SYMBOL SMALL TAH BELOW */ + X1(0xfeff), /* ZWNBSP */ + +#undef X1 +#undef X2 +}; + +static inline BOOL is_zero_width_utf16( WORD c ) +{ + size_t i; + for (i = 0; i < ARRAY_SIZE(zw_ranges) && c < zw_ranges[i].start; i++) + if (c - zw_ranges[i].start < zw_ranges[i].count) + return TRUE; + return FALSE; +} + static DWORD get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format, GLYPHMETRICS *gm_ret, ABC *abc_ret, DWORD buflen, void *buf, - const MAT2 *mat ) + const MAT2 *mat, BOOL *zero_width ) { GLYPHMETRICS gm; ABC abc; DWORD ret = 1; UINT index = glyph; BOOL tategaki = (*get_gdi_font_name( font ) == '@'); + UINT orig_format = format;
if (format & GGO_GLYPH_INDEX) { @@ -3863,6 +3920,21 @@ static DWORD get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format,
if (mat && !memcmp( mat, &identity, sizeof(*mat) )) mat = NULL;
+ if (zero_width) + { + *zero_width = FALSE; + if (!(orig_format & GGO_GLYPH_INDEX)) + { + /* Windows seems to have a fallback font that marks these characters as zero-width. + Wine does not have such a font. Since applications are likely to depend on the + dimensions of these characters, explicitly make them zero-width here. */ + if (glyph == 0x0009 || glyph == 0x000A || glyph == 0x000D) + *zero_width = TRUE; + else + *zero_width = is_zero_width_utf16( glyph ); + } + } + if (format == GGO_METRICS && !mat && get_gdi_font_glyph_metrics( font, index, &gm, &abc )) goto done;
@@ -3873,6 +3945,7 @@ static DWORD get_glyph_outline( struct gdi_font *font, UINT glyph, UINT format, set_gdi_font_glyph_metrics( font, index, &gm, &abc );
done: + if (zero_width && (abc.abcA + abc.abcB + abc.abcC) == 0) *zero_width = TRUE; if (gm_ret) *gm_ret = gm; if (abc_ret) *abc_ret = abc; return ret; @@ -3915,7 +3988,7 @@ static BOOL font_GetCharABCWidths( PHYSDEV dev, UINT first, UINT count, WCHAR *c for (i = 0; i < count; i++) { c = chars ? chars[i] : first + i; - get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &buffer[i], 0, NULL, NULL ); + get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &buffer[i], 0, NULL, NULL, NULL ); } pthread_mutex_unlock( &font_lock ); return TRUE; @@ -3941,7 +4014,7 @@ static BOOL font_GetCharABCWidthsI( PHYSDEV dev, UINT first, UINT count, WORD *g pthread_mutex_lock( &font_lock ); for (c = 0; c < count; c++, buffer++) get_glyph_outline( physdev->font, gi ? gi[c] : first + c, GGO_METRICS | GGO_GLYPH_INDEX, - NULL, buffer, 0, NULL, NULL ); + NULL, buffer, 0, NULL, NULL, NULL ); pthread_mutex_unlock( &font_lock ); return TRUE; } @@ -3968,7 +4041,7 @@ static BOOL font_GetCharWidth( PHYSDEV dev, UINT first, UINT count, const WCHAR for (i = 0; i < count; i++) { c = chars ? chars[i] : i + first; - if (get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &abc, 0, NULL, NULL ) == GDI_ERROR) + if (get_glyph_outline( physdev->font, c, GGO_METRICS, NULL, &abc, 0, NULL, NULL, NULL ) == GDI_ERROR) buffer[i] = 0; else buffer[i] = abc.abcA + abc.abcB + abc.abcC; @@ -4147,7 +4220,7 @@ static DWORD font_GetGlyphOutline( PHYSDEV dev, UINT glyph, UINT format, return dev->funcs->pGetGlyphOutline( dev, glyph, format, gm, buflen, buf, mat ); } pthread_mutex_lock( &font_lock ); - ret = get_glyph_outline( physdev->font, glyph, format, gm, NULL, buflen, buf, mat ); + ret = get_glyph_outline( physdev->font, glyph, format, gm, NULL, buflen, buf, mat, NULL ); pthread_mutex_unlock( &font_lock ); return ret; } @@ -4327,8 +4400,10 @@ static BOOL font_GetTextExtentExPoint( PHYSDEV dev, const WCHAR *str, INT count, pthread_mutex_lock( &font_lock ); for (i = pos = 0; i < count; i++) { - get_glyph_outline( physdev->font, str[i], GGO_METRICS, NULL, &abc, 0, NULL, NULL ); - pos += abc.abcA + abc.abcB + abc.abcC; + BOOL zero_width = FALSE; + get_glyph_outline( physdev->font, str[i], GGO_METRICS, NULL, &abc, 0, NULL, NULL, &zero_width ); + if (!zero_width) + pos += abc.abcA + abc.abcB + abc.abcC; dxs[i] = pos; } pthread_mutex_unlock( &font_lock ); @@ -4356,9 +4431,11 @@ static BOOL font_GetTextExtentExPointI( PHYSDEV dev, const WORD *indices, INT co pthread_mutex_lock( &font_lock ); for (i = pos = 0; i < count; i++) { + BOOL zero_width = FALSE; get_glyph_outline( physdev->font, indices[i], GGO_METRICS | GGO_GLYPH_INDEX, - NULL, &abc, 0, NULL, NULL ); - pos += abc.abcA + abc.abcB + abc.abcC; + NULL, &abc, 0, NULL, NULL, &zero_width ); + if (!zero_width) + pos += abc.abcA + abc.abcB + abc.abcC; dxs[i] = pos; } pthread_mutex_unlock( &font_lock ); @@ -5347,7 +5424,18 @@ BOOL WINAPI NtGdiGetTextExtentExW( HDC hdc, const WCHAR *str, INT count, INT max { unsigned int dx = abs( INTERNAL_XDSTOWS( dc, pos[i] )) + (i + 1) * dc->attr->char_extra; - if (nfit && dx > (unsigned int)max_ext) break; + if (nfit) + { + unsigned int dx2 = dx; + if (i >= 1) + { + unsigned int prev_dx = abs( INTERNAL_XDSTOWS( dc, pos[i - 1] )) + + i * dc->attr->char_extra; + if (dx == prev_dx) + dx2 += 1; + } + if (dx2 > (unsigned int)max_ext) break; + } if (dxs) dxs[i] = dx; } if (nfit) *nfit = i;