diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index e78f9bf3d34..08bd40fee2e 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -100,3 +100,4 @@ extern HRESULT font_create_fontface(IDWriteFactory *iface, DWRITE_FONT_FACE_TYPE /* Opentype font table functions */ extern HRESULT analyze_opentype_font(const void* font_data, UINT32* font_count, DWRITE_FONT_FILE_TYPE *file_type, DWRITE_FONT_FACE_TYPE *face_type, BOOL *supported) DECLSPEC_HIDDEN; extern HRESULT find_font_table(IDWriteFontFileStream *stream, UINT32 font_index, UINT32 tag, const void** table_data, void** table_context, UINT32 *table_size, BOOL* found) DECLSPEC_HIDDEN; +extern VOID OpenType_CMAP_GetGlyphIndex(LPVOID data, DWORD utf32c, LPWORD pgi, DWORD flags) DECLSPEC_HIDDEN; diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c index 07a8b869e3c..e78210419e6 100644 --- a/dlls/dwrite/font.c +++ b/dlls/dwrite/font.c @@ -124,6 +124,7 @@ typedef struct #define MS_HEAD_TAG MS_MAKE_TAG('h','e','a','d') #define MS_OS2_TAG MS_MAKE_TAG('O','S','/','2') #define MS_POST_TAG MS_MAKE_TAG('p','o','s','t') +#define MS_CMAP_TAG MS_MAKE_TAG('c','m','a','p') struct dwrite_fontface_data { LONG ref; @@ -188,6 +189,10 @@ struct dwrite_fontface { struct dwrite_fontface_data *data; + LPVOID CMAP_table; + LPVOID CMAP_context; + DWORD CMAP_size; + BOOL is_system; LOGFONTW logfont; }; @@ -296,6 +301,8 @@ static ULONG WINAPI dwritefontface_Release(IDWriteFontFace *iface) if (!ref) { + if (This->CMAP_context) + IDWriteFontFace_ReleaseFontTable(iface, This->CMAP_context); _free_fontface_data(This->data); heap_free(This); } @@ -379,12 +386,13 @@ static HRESULT WINAPI dwritefontface_GetGlyphIndices(IDWriteFontFace *iface, UIN UINT32 count, UINT16 *glyph_indices) { struct dwrite_fontface *This = impl_from_IDWriteFontFace(iface); + unsigned int i; + if (This->is_system) { HFONT hfont; WCHAR *str; HDC hdc; - unsigned int i; TRACE("(%p)->(%p %u %p)\n", This, codepoints, count, glyph_indices); @@ -408,8 +416,24 @@ static HRESULT WINAPI dwritefontface_GetGlyphIndices(IDWriteFontFace *iface, UIN } else { - FIXME("(%p)->(%p %u %p): Stub\n", This, codepoints, count, glyph_indices); - return E_NOTIMPL; + HRESULT hr; + TRACE("(%p)->(%p %u %p)\n", This, codepoints, count, glyph_indices); + if (!This->CMAP_table) + { + BOOL exists = FALSE; + hr = IDWriteFontFace_TryGetFontTable(iface, MS_CMAP_TAG, (const void**)&This->CMAP_table, &This->CMAP_size, &This->CMAP_context, &exists); + if (FAILED(hr) || !exists) + { + ERR("Font does not have a CMAP table\n"); + return E_FAIL; + } + } + + for (i = 0; i < count; i++) + { + OpenType_CMAP_GetGlyphIndex(This->CMAP_table, codepoints[i], &glyph_indices[i], 0); + } + return S_OK; } } @@ -557,6 +581,9 @@ static HRESULT create_system_fontface(struct dwrite_font *font, IDWriteFontFace This->data->files = NULL; This->data->index = 0; This->data->simulations = DWRITE_FONT_SIMULATIONS_NONE; + This->CMAP_table = NULL; + This->CMAP_context = NULL; + This->CMAP_size = 0; This->is_system = TRUE; memset(&This->logfont, 0, sizeof(This->logfont)); @@ -1318,6 +1345,9 @@ HRESULT font_create_fontface(IDWriteFactory *iface, DWRITE_FONT_FACE_TYPE facety This->data->type = facetype; This->data->file_count = files_number; This->data->files = heap_alloc(sizeof(*This->data->files) * files_number); + This->CMAP_table = NULL; + This->CMAP_context = NULL; + This->CMAP_size = 0; /* Verify font file streams */ for (i = 0; i < This->data->file_count && SUCCEEDED(hr); i++) { diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 6c90a961b87..9e7a716dcf6 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -62,6 +62,44 @@ typedef struct { DWORD length; } TT_TableRecord; +typedef struct { + WORD platformID; + WORD encodingID; + DWORD offset; +} CMAP_EncodingRecord; + +typedef struct { + WORD version; + WORD numTables; + CMAP_EncodingRecord tables[1]; +} CMAP_Header; + +typedef struct { + DWORD startCharCode; + DWORD endCharCode; + DWORD startGlyphID; +} CMAP_SegmentedCoverage_group; + +typedef struct { + WORD format; + WORD reserved; + DWORD length; + DWORD language; + DWORD nGroups; + CMAP_SegmentedCoverage_group groups[1]; +} CMAP_SegmentedCoverage; + +typedef struct { + WORD format; + WORD length; + WORD language; + WORD segCountX2; + WORD searchRange; + WORD entrySelector; + WORD rangeShift; + WORD endCode[1]; +} CMAP_SegmentMapping_0; + HRESULT analyze_opentype_font(const void* font_data, UINT32* font_count, DWRITE_FONT_FILE_TYPE *file_type, DWRITE_FONT_FACE_TYPE *face_type, BOOL *supported) { /* TODO: Do font validation */ @@ -169,3 +207,110 @@ HRESULT find_font_table(IDWriteFontFileStream *stream, UINT32 font_index, UINT32 return hr; } + +/********** + * CMAP + **********/ + +static int compare_group(const void *a, const void* b) +{ + const DWORD *chr = a; + const CMAP_SegmentedCoverage_group *group = b; + + if (*chr < GET_BE_DWORD(group->startCharCode)) + return -1; + if (*chr > GET_BE_DWORD(group->endCharCode)) + return 1; + return 0; +} + +static void CMAP4_GetGlyphIndex(CMAP_SegmentMapping_0* format, DWORD utf32c, LPWORD pgi) +{ + WORD *startCode; + SHORT *idDelta; + WORD *idRangeOffset; + int segment; + + int segment_count = GET_BE_WORD(format->segCountX2)/2; + /* This is correct because of the padding before startCode */ + startCode = (WORD*)((BYTE*)format + sizeof(CMAP_SegmentMapping_0) + (sizeof(WORD) * segment_count)); + idDelta = (SHORT*)(((BYTE*)startCode) + (sizeof(WORD) * segment_count)); + idRangeOffset = (WORD*)(((BYTE*)idDelta) + (sizeof(WORD) * segment_count)); + + segment = 0; + while(GET_BE_WORD(format->endCode[segment]) < 0xffff) + { + if (utf32c <= GET_BE_WORD(format->endCode[segment])) + break; + segment++; + } + if (segment >= segment_count) + return; + TRACE("Segment %i of %i\n",segment, segment_count); + if (GET_BE_WORD(startCode[segment]) > utf32c) + return; + TRACE("In range %i -> %i\n", GET_BE_WORD(startCode[segment]), GET_BE_WORD(format->endCode[segment])); + if (GET_BE_WORD(idRangeOffset[segment]) == 0) + { + *pgi = (SHORT)(GET_BE_WORD(idDelta[segment])) + utf32c; + } + else + { + WORD ro = GET_BE_WORD(idRangeOffset[segment])/2; + WORD co = (utf32c - GET_BE_WORD(startCode[segment])); + WORD *index = (WORD*)((BYTE*)&idRangeOffset[segment] + (ro + co)); + *pgi = GET_BE_WORD(*index); + } +} + +static void CMAP12_GetGlyphIndex(CMAP_SegmentedCoverage* format, DWORD utf32c, LPWORD pgi) +{ + CMAP_SegmentedCoverage_group *group = NULL; + + group = bsearch(&utf32c, format->groups, GET_BE_DWORD(format->nGroups), + sizeof(CMAP_SegmentedCoverage_group), compare_group); + + if (group) + { + DWORD offset = utf32c - GET_BE_DWORD(group->startCharCode); + *pgi = GET_BE_DWORD(group->startGlyphID) + offset; + } +} + +VOID OpenType_CMAP_GetGlyphIndex(LPVOID data, DWORD utf32c, LPWORD pgi, DWORD flags) +{ + int i; + CMAP_Header *CMAP_Table = NULL; + + if (flags & GGI_MARK_NONEXISTING_GLYPHS) + *pgi = 0xffff; + else + *pgi = 0; + + CMAP_Table = data; + + for (i = 0; i < GET_BE_WORD(CMAP_Table->numTables); i++) + { + WORD type; + WORD *table; + + if (GET_BE_WORD(CMAP_Table->tables[i].platformID) != 3) + continue; + + table = (WORD*)(((BYTE*)CMAP_Table) + GET_BE_DWORD(CMAP_Table->tables[i].offset)); + type = GET_BE_WORD(*table); + TRACE("Type %i\n", type); + /* Break when we find a handled type */ + switch(type) + { + case 4: + CMAP4_GetGlyphIndex((CMAP_SegmentMapping_0*) table, utf32c, pgi); + break; + case 12: + CMAP12_GetGlyphIndex((CMAP_SegmentedCoverage*) table, utf32c, pgi); + break; + default: + TRACE("Type %i unhandled.\n", type); + } + } +} diff --git a/dlls/dwrite/tests/font.c b/dlls/dwrite/tests/font.c index 7a90c58ff76..3a17064999d 100644 --- a/dlls/dwrite/tests/font.c +++ b/dlls/dwrite/tests/font.c @@ -919,6 +919,8 @@ static void test_FontLoader(void) IDWriteFontFace *fface = NULL; HRESULT hr; HRSRC font; + UINT32 codePoints[1] = {0xa8}; + UINT16 indices[1]; hr = IDWriteFactory_RegisterFontFileLoader(factory, NULL); ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); @@ -975,6 +977,9 @@ static void test_FontLoader(void) hr = IDWriteFactory_CreateFontFace(factory, face, 1, &ffile, 0, 0, &fface); ok(hr == S_OK, "got 0x%08x\n",hr); + hr = IDWriteFontFace_GetGlyphIndices(fface, codePoints, 1, indices); + ok(hr == S_OK, "got0x%08x\n",hr); + ok(indices[0] == 6, "got index %i\n",indices[0]); IDWriteFontFace_Release(fface); IDWriteFontFile_Release(ffile); }