Commit 21dbe1c9 authored by Sam Edwards's avatar Sam Edwards Committed by Alexandre Julliard

gdi32: Clip font glyphs to fit within text metrics.

parent a7dee44c
...@@ -538,6 +538,7 @@ static BOOL use_default_fallback = FALSE; ...@@ -538,6 +538,7 @@ static BOOL use_default_fallback = FALSE;
static BOOL get_glyph_index_linked(GdiFont *font, UINT c, GdiFont **linked_font, FT_UInt *glyph); static BOOL get_glyph_index_linked(GdiFont *font, UINT c, GdiFont **linked_font, FT_UInt *glyph);
static BOOL get_outline_text_metrics(GdiFont *font); static BOOL get_outline_text_metrics(GdiFont *font);
static BOOL get_bitmap_text_metrics(GdiFont *font);
static BOOL get_text_metrics(GdiFont *font, LPTEXTMETRICW ptm); static BOOL get_text_metrics(GdiFont *font, LPTEXTMETRICW ptm);
static void remove_face_from_cache( Face *face ); static void remove_face_from_cache( Face *face );
...@@ -5868,6 +5869,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format, ...@@ -5868,6 +5869,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)}; static const FT_Matrix identityMat = {(1 << 16), 0, 0, (1 << 16)};
FT_Face ft_face = incoming_font->ft_face; FT_Face ft_face = incoming_font->ft_face;
GdiFont *font = incoming_font; GdiFont *font = incoming_font;
FT_Glyph_Metrics metrics;
FT_UInt glyph_index; FT_UInt glyph_index;
DWORD width, height, pitch, needed = 0; DWORD width, height, pitch, needed = 0;
FT_Bitmap ft_bitmap; FT_Bitmap ft_bitmap;
...@@ -6019,6 +6021,22 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format, ...@@ -6019,6 +6021,22 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
return GDI_ERROR; return GDI_ERROR;
} }
/* Some poorly-created fonts contain glyphs that exceed the boundaries set
* by the text metrics. The proper behavior is to clip the glyph metrics to
* fit within the maximums specified in the text metrics. */
metrics = ft_face->glyph->metrics;
if(incoming_font->potm || get_outline_text_metrics(incoming_font) ||
get_bitmap_text_metrics(incoming_font)) {
TEXTMETRICW *ptm = &incoming_font->potm->otmTextMetrics;
top = min( metrics.horiBearingY, ptm->tmAscent << 6 );
bottom = max( metrics.horiBearingY - metrics.height, -(ptm->tmDescent << 6) );
metrics.horiBearingY = top;
metrics.height = top - bottom;
/* TODO: Are we supposed to clip the width as well...? */
/* metrics.width = min( metrics.width, ptm->tmMaxCharWidth << 6 ); */
}
if(FT_IS_SCALABLE(incoming_font->ft_face)) { if(FT_IS_SCALABLE(incoming_font->ft_face)) {
TEXTMETRICW tm; TEXTMETRICW tm;
if (get_text_metrics(incoming_font, &tm) && if (get_text_metrics(incoming_font, &tm) &&
...@@ -6026,7 +6044,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format, ...@@ -6026,7 +6044,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
em_scale = MulDiv(incoming_font->ppem, 1 << 16, incoming_font->ft_face->units_per_EM); em_scale = MulDiv(incoming_font->ppem, 1 << 16, incoming_font->ft_face->units_per_EM);
avgAdvance = pFT_MulFix(incoming_font->ntmAvgWidth, em_scale); avgAdvance = pFT_MulFix(incoming_font->ntmAvgWidth, em_scale);
if (avgAdvance && if (avgAdvance &&
(ft_face->glyph->metrics.horiAdvance+63) >> 6 == pFT_MulFix(incoming_font->ntmAvgWidth*2, em_scale)) (metrics.horiAdvance+63) >> 6 == pFT_MulFix(incoming_font->ntmAvgWidth*2, em_scale))
TRACE("Fixed-pitch full-width character detected\n"); TRACE("Fixed-pitch full-width character detected\n");
else else
avgAdvance = 0; /* cancel this feature */ avgAdvance = 0; /* cancel this feature */
...@@ -6034,16 +6052,15 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format, ...@@ -6034,16 +6052,15 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
} }
if(!needsTransform) { if(!needsTransform) {
left = (INT)(ft_face->glyph->metrics.horiBearingX) & -64; left = (INT)(metrics.horiBearingX) & -64;
right = (INT)((ft_face->glyph->metrics.horiBearingX + ft_face->glyph->metrics.width) + 63) & -64; right = (INT)((metrics.horiBearingX + metrics.width) + 63) & -64;
if (!avgAdvance) if (!avgAdvance)
adv = (INT)(ft_face->glyph->metrics.horiAdvance + 63) >> 6; adv = (INT)(metrics.horiAdvance + 63) >> 6;
else else
adv = (INT)avgAdvance * 2; adv = (INT)avgAdvance * 2;
top = (ft_face->glyph->metrics.horiBearingY + 63) & -64; top = (metrics.horiBearingY + 63) & -64;
bottom = (ft_face->glyph->metrics.horiBearingY - bottom = (metrics.horiBearingY - metrics.height) & -64;
ft_face->glyph->metrics.height) & -64;
lpgm->gmCellIncX = adv; lpgm->gmCellIncX = adv;
lpgm->gmCellIncY = 0; lpgm->gmCellIncY = 0;
} else { } else {
...@@ -6054,10 +6071,8 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format, ...@@ -6054,10 +6071,8 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
for(xc = 0; xc < 2; xc++) { for(xc = 0; xc < 2; xc++) {
for(yc = 0; yc < 2; yc++) { for(yc = 0; yc < 2; yc++) {
vec.x = (ft_face->glyph->metrics.horiBearingX + vec.x = metrics.horiBearingX + xc * metrics.width;
xc * ft_face->glyph->metrics.width); vec.y = metrics.horiBearingY - yc * metrics.height;
vec.y = ft_face->glyph->metrics.horiBearingY -
yc * ft_face->glyph->metrics.height;
TRACE("Vec %ld,%ld\n", vec.x, vec.y); TRACE("Vec %ld,%ld\n", vec.x, vec.y);
pFT_Vector_Transform(&vec, &transMat); pFT_Vector_Transform(&vec, &transMat);
if(xc == 0 && yc == 0) { if(xc == 0 && yc == 0) {
...@@ -6077,7 +6092,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format, ...@@ -6077,7 +6092,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
top = (top + 63) & -64; top = (top + 63) & -64;
TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom); TRACE("transformed box: (%d,%d - %d,%d)\n", left, top, right, bottom);
vec.x = ft_face->glyph->metrics.horiAdvance; vec.x = metrics.horiAdvance;
vec.y = 0; vec.y = 0;
pFT_Vector_Transform(&vec, &transMat); pFT_Vector_Transform(&vec, &transMat);
lpgm->gmCellIncY = -((vec.y+63) >> 6); lpgm->gmCellIncY = -((vec.y+63) >> 6);
...@@ -6090,7 +6105,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format, ...@@ -6090,7 +6105,7 @@ static DWORD get_glyph_outline(GdiFont *incoming_font, UINT glyph, UINT format,
lpgm->gmCellIncX = pFT_MulFix(vec.x, em_scale) * 2; lpgm->gmCellIncX = pFT_MulFix(vec.x, em_scale) * 2;
} }
vec.x = ft_face->glyph->metrics.horiAdvance; vec.x = metrics.horiAdvance;
vec.y = 0; vec.y = 0;
pFT_Vector_Transform(&vec, &transMatUnrotated); pFT_Vector_Transform(&vec, &transMatUnrotated);
if (!avgAdvance || vec.y) if (!avgAdvance || vec.y)
......
...@@ -4561,6 +4561,46 @@ static void test_GetGlyphOutline_empty_contour(void) ...@@ -4561,6 +4561,46 @@ static void test_GetGlyphOutline_empty_contour(void)
ReleaseDC(NULL, hdc); ReleaseDC(NULL, hdc);
} }
static void test_GetGlyphOutline_metric_clipping(void)
{
HDC hdc;
LOGFONTA lf;
HFONT hfont, hfont_prev;
GLYPHMETRICS gm;
TEXTMETRICA tm;
DWORD ret;
memset(&lf, 0, sizeof(lf));
lf.lfHeight = 72;
lstrcpyA(lf.lfFaceName, "wine_test");
SetLastError(0xdeadbeef);
hfont = CreateFontIndirectA(&lf);
ok(hfont != 0, "CreateFontIndirectA error %u\n", GetLastError());
hdc = GetDC(NULL);
hfont_prev = SelectObject(hdc, hfont);
ok(hfont_prev != NULL, "SelectObject failed\n");
SetLastError(0xdeadbeef);
ret = GetTextMetrics(hdc, &tm);
ok(ret, "GetTextMetrics error %u\n", GetLastError());
GetGlyphOutlineA(hdc, 'A', GGO_METRICS, &gm, 0, NULL, &mat);
ok(gm.gmptGlyphOrigin.y <= tm.tmAscent,
"Glyph top(%d) exceeds ascent(%d)\n",
gm.gmptGlyphOrigin.y, tm.tmAscent);
GetGlyphOutlineA(hdc, 'D', GGO_METRICS, &gm, 0, NULL, &mat);
ok(gm.gmptGlyphOrigin.y - gm.gmBlackBoxY >= -tm.tmDescent,
"Glyph bottom(%d) exceeds descent(%d)\n",
gm.gmptGlyphOrigin.y - gm.gmBlackBoxY, -tm.tmDescent);
SelectObject(hdc, hfont_prev);
DeleteObject(hfont);
ReleaseDC(NULL, hdc);
}
static void test_CreateScalableFontResource(void) static void test_CreateScalableFontResource(void)
{ {
char ttf_name[MAX_PATH]; char ttf_name[MAX_PATH];
...@@ -4643,6 +4683,7 @@ static void test_CreateScalableFontResource(void) ...@@ -4643,6 +4683,7 @@ static void test_CreateScalableFontResource(void)
ok(ret, "font wine_test should be enumerated\n"); ok(ret, "font wine_test should be enumerated\n");
test_GetGlyphOutline_empty_contour(); test_GetGlyphOutline_empty_contour();
test_GetGlyphOutline_metric_clipping();
ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0); ret = pRemoveFontResourceExA(fot_name, FR_PRIVATE, 0);
ok(!ret, "RemoveFontResourceEx() with not matching flags should fail\n"); ok(!ret, "RemoveFontResourceEx() with not matching flags should fail\n");
......
...@@ -20,7 +20,7 @@ OS2Version: 2 ...@@ -20,7 +20,7 @@ OS2Version: 2
OS2_WeightWidthSlopeOnly: 0 OS2_WeightWidthSlopeOnly: 0
OS2_UseTypoMetrics: 1 OS2_UseTypoMetrics: 1
CreationTime: 1288336343 CreationTime: 1288336343
ModificationTime: 1352483620 ModificationTime: 1366465321
PfmFamily: 17 PfmFamily: 17
TTFWeight: 500 TTFWeight: 500
TTFWidth: 5 TTFWidth: 5
...@@ -32,10 +32,10 @@ OS2TypoAOffset: 1 ...@@ -32,10 +32,10 @@ OS2TypoAOffset: 1
OS2TypoDescent: 0 OS2TypoDescent: 0
OS2TypoDOffset: 1 OS2TypoDOffset: 1
OS2TypoLinegap: 184 OS2TypoLinegap: 184
OS2WinAscent: 0 OS2WinAscent: 1638
OS2WinAOffset: 1 OS2WinAOffset: 0
OS2WinDescent: 0 OS2WinDescent: 410
OS2WinDOffset: 1 OS2WinDOffset: 0
HheadAscent: 0 HheadAscent: 0
HheadAOffset: 1 HheadAOffset: 1
HheadDescent: 0 HheadDescent: 0
...@@ -78,15 +78,17 @@ ShortTable: maxp 16 ...@@ -78,15 +78,17 @@ ShortTable: maxp 16
0 0
EndShort EndShort
LangName: 1033 "" "" "" "Wine : wine_test : 4-11-2010" LangName: 1033 "" "" "" "Wine : wine_test : 4-11-2010"
GaspTable: 1 65535 2 GaspTable: 1 65535 2 0
Encoding: UnicodeBmp Encoding: UnicodeBmp
UnicodeInterp: none UnicodeInterp: none
NameList: Adobe Glyph List NameList: Adobe Glyph List
DisplaySize: -24 DisplaySize: -24
AntiAlias: 1 AntiAlias: 1
FitToEm: 1 FitToEm: 1
WinInfo: 65 65 19 WinInfo: 48 16 4
BeginChars: 65539 5 BeginPrivate: 0
EndPrivate
BeginChars: 65539 7
StartChar: .notdef StartChar: .notdef
Encoding: 65536 -1 0 Encoding: 65536 -1 0
...@@ -178,10 +180,10 @@ LayerCount: 2 ...@@ -178,10 +180,10 @@ LayerCount: 2
EndChar EndChar
StartChar: dieresis StartChar: dieresis
Encoding: 168 168 0 Encoding: 168 168 4
Width: 1000 Width: 1000
VWidth: 0 VWidth: 0
Flags: HW Flags: W
LayerCount: 2 LayerCount: 2
Fore Fore
SplineSet SplineSet
...@@ -201,5 +203,43 @@ SplineSet ...@@ -201,5 +203,43 @@ SplineSet
310.707 834.805 310.707 834.805 254.492 773.213 c 1,12,13 310.707 834.805 310.707 834.805 254.492 773.213 c 1,12,13
EndSplineSet EndSplineSet
EndChar EndChar
StartChar: A
Encoding: 65 65 5
Width: 1000
VWidth: 0
Flags: WO
LayerCount: 2
Fore
SplineSet
459 1258 m 29,0,-1
462 1639 l 5,1,-1
389 1638 l 5,2,-1
492 1815 l 5,3,-1
609 1638.5 l 5,4,-1
531 1637.5 l 5,5,-1
523 1258 l 5,6,-1
459 1258 l 29,0,-1
EndSplineSet
EndChar
StartChar: D
Encoding: 68 68 6
Width: 1000
VWidth: 0
Flags: WO
LayerCount: 2
Fore
SplineSet
461 -30.7998 m 29,0,-1
464 -411.8 l 5,1,-1
391 -410.8 l 5,2,-1
494 -587.8 l 5,3,-1
611 -411.3 l 5,4,-1
533 -410.3 l 5,5,-1
525 -30.7998 l 5,6,-1
461 -30.7998 l 29,0,-1
EndSplineSet
EndChar
EndChars EndChars
EndSplineFont EndSplineFont
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment