gdi32: Improve calculation of the character code text metrics.

This commit is contained in:
Huw Davies 2009-02-16 12:39:15 +00:00 committed by Alexandre Julliard
parent 9d9880bd34
commit e93d348109
3 changed files with 279 additions and 74 deletions

View File

@ -212,14 +212,16 @@ static void FONT_TextMetricWToA(const TEXTMETRICW *ptmW, LPTEXTMETRICA ptmA )
ptmA->tmFirstChar = min(ptmW->tmFirstChar, 255);
if (ptmW->tmCharSet == SYMBOL_CHARSET)
{
UINT last_char = ptmW->tmLastChar;
if (last_char > 0xf000) last_char -= 0xf000;
ptmA->tmLastChar = min(last_char, 255);
ptmA->tmFirstChar = 0x1e;
ptmA->tmLastChar = 0xff; /* win9x behaviour - we need the OS2 table data to calculate correctly */
}
else
ptmA->tmLastChar = min(ptmW->tmLastChar, 255);
ptmA->tmDefaultChar = min(ptmW->tmDefaultChar, 255);
ptmA->tmBreakChar = min(ptmW->tmBreakChar, 255);
{
ptmA->tmFirstChar = ptmW->tmDefaultChar - 1;
ptmA->tmLastChar = min(ptmW->tmLastChar, 0xff);
}
ptmA->tmDefaultChar = ptmW->tmDefaultChar;
ptmA->tmBreakChar = ptmW->tmBreakChar;
ptmA->tmItalic = ptmW->tmItalic;
ptmA->tmUnderlined = ptmW->tmUnderlined;
ptmA->tmStruckOut = ptmW->tmStruckOut;

View File

@ -5396,18 +5396,26 @@ UINT WineEngGetOutlineTextMetrics(GdiFont *font, UINT cbSize,
/* It appears that for fonts with SYMBOL_CHARSET Windows always sets
* symbol range to 0 - f0ff
*/
if (font->charset == SYMBOL_CHARSET)
if (font->charset == SYMBOL_CHARSET || (pOS2->usFirstCharIndex >= 0xf000 && pOS2->usFirstCharIndex < 0xf100))
{
TM.tmFirstChar = 0;
TM.tmDefaultChar = pOS2->usDefaultChar ? pOS2->usDefaultChar : 0x1f;
TM.tmLastChar = 0xf0ff;
TM.tmBreakChar = 0x20;
TM.tmDefaultChar = 0x1f;
}
else
{
TM.tmFirstChar = pOS2->usFirstCharIndex;
TM.tmDefaultChar = pOS2->usDefaultChar ? pOS2->usDefaultChar : 0xffff;
TM.tmFirstChar = pOS2->usFirstCharIndex; /* Should be the first char in the cmap */
TM.tmLastChar = pOS2->usLastCharIndex; /* Should be min(cmap_last, os2_last) */
if(pOS2->usFirstCharIndex <= 1)
TM.tmBreakChar = pOS2->usFirstCharIndex + 2;
else if (pOS2->usFirstCharIndex > 0xff)
TM.tmBreakChar = 0x20;
else
TM.tmBreakChar = pOS2->usFirstCharIndex;
TM.tmDefaultChar = TM.tmBreakChar - 1;
}
TM.tmLastChar = pOS2->usLastCharIndex;
TM.tmBreakChar = pOS2->usBreakChar ? pOS2->usBreakChar : ' ';
TM.tmItalic = font->fake_italic ? 255 : ((ft_face->style_flags & FT_STYLE_FLAG_ITALIC) ? 255 : 0);
TM.tmUnderlined = font->underline;
TM.tmStruckOut = font->strikeout;

View File

@ -963,7 +963,7 @@ static void test_GetGlyphIndices(void)
testtext[0] = textm.tmDefaultChar;
charcount = pGetGlyphIndicesW(hdc, testtext, (sizeof(testtext)/2)-1, glyphs, flags);
ok(charcount == 5, "GetGlyphIndicesW count of glyphs should = 5 not %d\n", charcount);
todo_wine ok(glyphs[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
ok(glyphs[0] == 0, "GetGlyphIndicesW for tmDefaultChar should be 0 not %04x\n", glyphs[0]);
ok(glyphs[4] == 0, "GetGlyphIndicesW should have returned 0 not %04x\n", glyphs[4]);
DeleteObject(SelectObject(hdc, hOldFont));
}
@ -2010,21 +2010,194 @@ typedef struct
#ifdef WORDS_BIGENDIAN
#define GET_BE_WORD(x) (x)
#define GET_BE_DWORD(x) (x)
#else
#define GET_BE_WORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x))
#define GET_BE_DWORD(x) MAKELONG(GET_BE_WORD(HIWORD(x)), GET_BE_WORD(LOWORD(x)));
#endif
#define MS_MAKE_TAG(ch0, ch1, ch2, ch3) \
((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
#define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2')
#define MS_CMAP_TAG MS_MAKE_TAG('c','m','a','p')
typedef struct
{
USHORT version;
USHORT num_tables;
} cmap_header;
typedef struct
{
USHORT plat_id;
USHORT enc_id;
ULONG offset;
} cmap_encoding_record;
typedef struct
{
USHORT format;
USHORT length;
USHORT language;
BYTE glyph_ids[256];
} cmap_format_0;
typedef struct
{
USHORT format;
USHORT length;
USHORT language;
USHORT seg_countx2;
USHORT search_range;
USHORT entry_selector;
USHORT range_shift;
USHORT end_count[1]; /* this is a variable-sized array of length seg_countx2 / 2 */
/* Then follows:
USHORT pad;
USHORT start_count[seg_countx2 / 2];
USHORT id_delta[seg_countx2 / 2];
USHORT id_range_offset[seg_countx2 / 2];
USHORT glyph_ids[];
*/
} cmap_format_4;
typedef struct
{
USHORT end_count;
USHORT start_count;
USHORT id_delta;
USHORT id_range_offset;
} cmap_format_4_seg;
static void expect_ff(const TEXTMETRICA *tmA, const TT_OS2_V2 *os2, WORD family, const char *name)
{
ok((tmA->tmPitchAndFamily & 0xf0) == family, "%s: expected family %02x got %02x. panose %d-%d-%d-%d-...\n",
name, family, tmA->tmPitchAndFamily, os2->panose.bFamilyType, os2->panose.bSerifStyle,
os2->panose.bWeight, os2->panose.bProportion);
}
static BOOL get_first_last_from_cmap0(void *ptr, DWORD *first, DWORD *last)
{
int i;
cmap_format_0 *cmap = (cmap_format_0*)ptr;
*first = 256;
for(i = 0; i < 256; i++)
{
if(cmap->glyph_ids[i] == 0) continue;
*last = i;
if(*first == 256) *first = i;
}
if(*first == 256) return FALSE;
return TRUE;
}
static void get_seg4(cmap_format_4 *cmap, USHORT seg_num, cmap_format_4_seg *seg)
{
USHORT segs = GET_BE_WORD(cmap->seg_countx2) / 2;
seg->end_count = GET_BE_WORD(cmap->end_count[seg_num]);
seg->start_count = GET_BE_WORD(cmap->end_count[segs + 1 + seg_num]);
seg->id_delta = GET_BE_WORD(cmap->end_count[2 * segs + 1 + seg_num]);
seg->id_range_offset = GET_BE_WORD(cmap->end_count[3 * segs + 1 + seg_num]);
}
static BOOL get_first_last_from_cmap4(void *ptr, DWORD *first, DWORD *last)
{
int i;
cmap_format_4 *cmap = (cmap_format_4*)ptr;
USHORT seg_count = GET_BE_WORD(cmap->seg_countx2) / 2;
USHORT const *glyph_ids = cmap->end_count + 4 * seg_count + 1;
*first = 0x10000;
for(i = 0; i < seg_count; i++)
{
DWORD code, index;
cmap_format_4_seg seg;
get_seg4(cmap, i, &seg);
for(code = seg.start_count; code <= seg.end_count; code++)
{
if(seg.id_range_offset == 0)
index = (seg.id_delta + code) & 0xffff;
else
{
index = seg.id_range_offset / 2
+ code - seg.start_count
+ i - seg_count;
index = GET_BE_WORD(glyph_ids[index]);
if(index) index += seg.id_delta;
}
if(index)
{
*last = code;
if(*first == 0x10000) *first = code;
}
}
}
if(*first == 0x10000) return FALSE;
return TRUE;
}
static void *get_cmap(cmap_header *header, USHORT plat_id, USHORT enc_id)
{
USHORT i;
cmap_encoding_record *record = (cmap_encoding_record *)(header + 1);
for(i = 0; i < GET_BE_WORD(header->num_tables); i++)
{
if(GET_BE_WORD(record->plat_id) == plat_id && GET_BE_WORD(record->enc_id) == enc_id)
return (BYTE *)header + GET_BE_DWORD(record->offset);
record++;
}
return NULL;
}
static BOOL get_first_last_from_cmap(HDC hdc, DWORD *first, DWORD *last)
{
LONG size, ret;
cmap_header *header;
void *cmap;
BOOL r = FALSE;
WORD format;
size = GetFontData(hdc, MS_CMAP_TAG, 0, NULL, 0);
ok(size != GDI_ERROR, "no cmap table found\n");
if(size == GDI_ERROR) return FALSE;
header = HeapAlloc(GetProcessHeap(), 0, size);
ret = GetFontData(hdc, MS_CMAP_TAG, 0, header, size);
ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
ok(GET_BE_WORD(header->version) == 0, "got cmap version %d\n", GET_BE_WORD(header->version));
cmap = get_cmap(header, 3, 1);
if(!cmap) cmap = get_cmap(header, 3, 0);
if(!cmap) goto end;
format = GET_BE_WORD(*(WORD *)cmap);
switch(format)
{
case 0:
r = get_first_last_from_cmap0(cmap, first, last);
break;
case 4:
r = get_first_last_from_cmap4(cmap, first, last);
break;
default:
trace("unhandled cmap format %d\n", format);
break;
}
end:
HeapFree(GetProcessHeap(), 0, header);
return r;
}
static void test_text_metrics(const LOGFONTA *lf)
@ -2032,13 +2205,10 @@ static void test_text_metrics(const LOGFONTA *lf)
HDC hdc;
HFONT hfont, hfont_old;
TEXTMETRICA tmA;
TEXTMETRICW tmW;
UINT first_unicode_char, last_unicode_char, default_char, break_char;
INT test_char;
TT_OS2_V2 tt_os2;
USHORT version;
LONG size, ret;
const char *font_name = lf->lfFaceName;
DWORD cmap_first = 0, cmap_last = 0;
hdc = GetDC(0);
@ -2064,78 +2234,103 @@ static void test_text_metrics(const LOGFONTA *lf)
ret = GetFontData(hdc, MS_OS2_TAG, 0, &tt_os2, size);
ok(ret == size, "GetFontData should return %u not %u\n", size, ret);
version = GET_BE_WORD(tt_os2.version);
first_unicode_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
last_unicode_char = GET_BE_WORD(tt_os2.usLastCharIndex);
default_char = GET_BE_WORD(tt_os2.usDefaultChar);
break_char = GET_BE_WORD(tt_os2.usBreakChar);
trace("font %s charset %u: %x-%x default %x break %x OS/2 version %u vendor %4.4s\n",
font_name, lf->lfCharSet, first_unicode_char, last_unicode_char, default_char, break_char,
version, (LPCSTR)&tt_os2.achVendID);
SetLastError(0xdeadbeef);
ret = GetTextMetricsA(hdc, &tmA);
ok(ret, "GetTextMetricsA error %u\n", GetLastError());
#if 0 /* FIXME: This doesn't appear to be what Windows does */
test_char = min(first_unicode_char - 1, 255);
ok(tmA.tmFirstChar == test_char, "A: tmFirstChar for %s %02x != %02x\n",
font_name, tmA.tmFirstChar, test_char);
#endif
if (lf->lfCharSet == SYMBOL_CHARSET)
if(!get_first_last_from_cmap(hdc, &cmap_first, &cmap_last))
{
test_char = min(last_unicode_char - 0xf000, 255);
ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
font_name, tmA.tmLastChar, test_char);
skip("Unable to retrieve first and last glyphs from cmap\n");
}
else
{
test_char = min(last_unicode_char, 255);
ok(tmA.tmLastChar == test_char, "A: tmLastChar for %s %02x != %02x\n",
font_name, tmA.tmLastChar, test_char);
}
USHORT expect_first_A, expect_last_A, expect_break_A, expect_default_A;
USHORT expect_first_W, expect_last_W, expect_break_W, expect_default_W;
UINT os2_first_char, os2_last_char, default_char, break_char;
USHORT version;
TEXTMETRICW tmW;
SetLastError(0xdeadbeef);
ret = GetTextMetricsW(hdc, &tmW);
ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
"GetTextMetricsW error %u\n", GetLastError());
if (ret)
{
trace("%04x-%04x (%02x-%02x) default %x (%x) break %x (%x)\n",
tmW.tmFirstChar, tmW.tmLastChar, tmA.tmFirstChar, tmA.tmLastChar,
tmW.tmDefaultChar, tmA.tmDefaultChar, tmW.tmBreakChar, tmA.tmBreakChar);
version = GET_BE_WORD(tt_os2.version);
if (lf->lfCharSet == SYMBOL_CHARSET)
os2_first_char = GET_BE_WORD(tt_os2.usFirstCharIndex);
os2_last_char = GET_BE_WORD(tt_os2.usLastCharIndex);
default_char = GET_BE_WORD(tt_os2.usDefaultChar);
break_char = GET_BE_WORD(tt_os2.usBreakChar);
trace("font %s charset %u: %x-%x (%x-%x) default %x break %x OS/2 version %u vendor %4.4s\n",
font_name, lf->lfCharSet, os2_first_char, os2_last_char, cmap_first, cmap_last,
default_char, break_char, version, (LPCSTR)&tt_os2.achVendID);
if (lf->lfCharSet == SYMBOL_CHARSET || (cmap_first >= 0xf000 && cmap_first < 0xf100))
{
/* It appears that for fonts with SYMBOL_CHARSET Windows always
* sets symbol range to 0 - f0ff
*/
ok(tmW.tmFirstChar == 0, "W: tmFirstChar for %s %02x != 0\n",
font_name, tmW.tmFirstChar);
/* FIXME: Windows returns f0ff here, while Wine f0xx */
ok(tmW.tmLastChar >= 0xf000, "W: tmLastChar for %s %02x < 0xf000\n",
font_name, tmW.tmLastChar);
ok(tmW.tmDefaultChar == 0x1f, "W: tmDefaultChar for %s %02x != 0x1f\n",
font_name, tmW.tmDefaultChar);
ok(tmW.tmBreakChar == 0x20, "W: tmBreakChar for %s %02x != 0x20\n",
font_name, tmW.tmBreakChar);
expect_first_W = 0;
expect_last_W = 0xf0ff;
expect_break_W = 0x20;
expect_default_W = expect_break_W - 1;
expect_first_A = 0x1e;
expect_last_A = min(os2_last_char - os2_first_char + 0x20, 0xff);
}
else
{
ok(tmW.tmFirstChar == first_unicode_char, "W: tmFirstChar for %s %02x != %02x\n",
font_name, tmW.tmFirstChar, first_unicode_char);
ok(tmW.tmLastChar == last_unicode_char, "W: tmLastChar for %s %02x != %02x\n",
font_name, tmW.tmLastChar, last_unicode_char);
expect_first_W = cmap_first;
expect_last_W = min(cmap_last, os2_last_char);
if(os2_first_char <= 1)
expect_break_W = os2_first_char + 2;
else if(os2_first_char > 0xff)
expect_break_W = 0x20;
else
expect_break_W = os2_first_char;
expect_default_W = expect_break_W - 1;
expect_first_A = expect_default_W - 1;
expect_last_A = min(expect_last_W, 0xff);
}
expect_break_A = expect_break_W;
expect_default_A = expect_default_W;
ok(tmA.tmFirstChar == expect_first_A ||
tmA.tmFirstChar == expect_first_A + 1 /* win9x */,
"A: tmFirstChar for %s got %02x expected %02x\n", font_name, tmA.tmFirstChar, expect_first_A);
ok(tmA.tmLastChar == expect_last_A ||
tmA.tmLastChar == 0xff /* win9x */,
"A: tmLastChar for %s got %02x expected %02x\n", font_name, tmA.tmLastChar, expect_last_A);
ok(tmA.tmBreakChar == expect_break_A, "A: tmBreakChar for %s got %02x expected %02x\n",
font_name, tmA.tmBreakChar, expect_break_A);
ok(tmA.tmDefaultChar == expect_default_A, "A: tmDefaultChar for %s got %02x expected %02x\n",
font_name, tmA.tmDefaultChar, expect_default_A);
SetLastError(0xdeadbeef);
ret = GetTextMetricsW(hdc, &tmW);
ok(ret || GetLastError() == ERROR_CALL_NOT_IMPLEMENTED,
"GetTextMetricsW error %u\n", GetLastError());
if (ret)
{
if(cmap_first != os2_first_char && tmW.tmCharSet != SYMBOL_CHARSET)
todo_wine ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
font_name, tmW.tmFirstChar, expect_first_W);
else
ok(tmW.tmFirstChar == expect_first_W, "W: tmFirstChar for %s got %02x expected %02x\n",
font_name, tmW.tmFirstChar, expect_first_W);
if(expect_last_W != os2_last_char && tmW.tmCharSet != SYMBOL_CHARSET)
todo_wine ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
font_name, tmW.tmLastChar, expect_last_W);
else
ok(tmW.tmLastChar == expect_last_W, "W: tmLastChar for %s got %02x expected %02x\n",
font_name, tmW.tmLastChar, expect_last_W);
ok(tmW.tmBreakChar == expect_break_W, "W: tmBreakChar for %s got %02x expected %02x\n",
font_name, tmW.tmBreakChar, expect_break_W);
ok(tmW.tmDefaultChar == expect_default_W, "W: tmDefaultChar for %s got %02x expected %02x\n",
font_name, tmW.tmDefaultChar, expect_default_W);
/* Test the aspect ratio while we have tmW */
ret = GetDeviceCaps(hdc, LOGPIXELSX);
ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
tmW.tmDigitizedAspectX, ret);
ret = GetDeviceCaps(hdc, LOGPIXELSY);
ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
tmW.tmDigitizedAspectX, ret);
}
ret = GetDeviceCaps(hdc, LOGPIXELSX);
ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectX %u != %u\n",
tmW.tmDigitizedAspectX, ret);
ret = GetDeviceCaps(hdc, LOGPIXELSY);
ok(tmW.tmDigitizedAspectX == ret, "W: tmDigitizedAspectY %u != %u\n",
tmW.tmDigitizedAspectX, ret);
}
/* test FF_ values */