From: Aric Stewart Subject: [3/3]dwrite: Implement Custom Font Collections and supporting functions Message-Id: <540E459A.5040308@codeweavers.com> Date: Mon, 08 Sep 2014 19:11:06 -0500 read strings from OpenType 'name' table Correct string index count --- dlls/dwrite/dwrite_private.h | 2 + dlls/dwrite/font.c | 298 +++++++++++++++++++++++++++++++++++++- dlls/dwrite/main.c | 59 +++++++- dlls/dwrite/opentype.c | 112 +++++++++++++++ dlls/dwrite/tests/font.c | 335 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 802 insertions(+), 4 deletions(-) diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 77834cd..e4af2b2 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -97,9 +97,11 @@ extern HRESULT get_textanalyzer(IDWriteTextAnalyzer**) DECLSPEC_HIDDEN; extern HRESULT create_font_file(IDWriteFontFileLoader *loader, const void *reference_key, UINT32 key_size, IDWriteFontFile **font_file) DECLSPEC_HIDDEN; extern HRESULT create_localfontfileloader(IDWriteLocalFontFileLoader** iface) DECLSPEC_HIDDEN; extern HRESULT font_create_fontface(IDWriteFactory *iface, DWRITE_FONT_FACE_TYPE facetype, UINT32 files_number, IDWriteFontFile* const* font_files, UINT32 index, DWRITE_FONT_SIMULATIONS sim_flags, IDWriteFontFace **font_face) DECLSPEC_HIDDEN; +extern HRESULT create_font_collection(IDWriteFactory *factory, IDWriteFontFileEnumerator* enumerator, IDWriteFontCollectionLoader *loader, IDWriteFontCollection **collection) DECLSPEC_HIDDEN; /* 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; extern VOID get_font_properties(LPCVOID os2, LPCVOID head, LPCVOID post, DWRITE_FONT_METRICS *metrics, DWRITE_FONT_STRETCH *stretch, DWRITE_FONT_WEIGHT *weight, DWRITE_FONT_STYLE *style) DECLSPEC_HIDDEN; +extern HRESULT get_name_font_table_data(const void* table_data, UINT32 table_size, USHORT name_id, IDWriteLocalizedStrings** string) DECLSPEC_HIDDEN; diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c index d6dbf6d..504f792 100644 --- a/dlls/dwrite/font.c +++ b/dlls/dwrite/font.c @@ -33,6 +33,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dwrite); #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') +#define MS_NAME_TAG MS_MAKE_TAG('n','a','m','e') struct dwrite_fontface_data { LONG ref; @@ -53,6 +54,8 @@ struct dwrite_font_data { DWRITE_FONT_SIMULATIONS simulations; DWRITE_FONT_METRICS metrics; + IDWriteLocalizedStrings *info_strings[DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT+1]; + struct dwrite_fontface_data *face_data; WCHAR *facename; @@ -137,6 +140,7 @@ static HRESULT create_fontfamily(IDWriteLocalizedStrings *familyname, IDWriteFon static HRESULT create_font_base(IDWriteFont **font); static HRESULT create_font_from_data(struct dwrite_font_data *data, IDWriteFont **font); static HRESULT create_fontfamily_from_data(struct dwrite_fontfamily_data *data, IDWriteFontCollection *collection, IDWriteFontFamily **family); +static HRESULT create_font_from_fontface(IDWriteFontFace *face, IDWriteFont **font); static inline struct dwrite_fontface *impl_from_IDWriteFontFace(IDWriteFontFace *iface) { @@ -205,6 +209,9 @@ static VOID _free_font_data(struct dwrite_font_data *data) if (i > 0) return; _free_fontface_data(data->face_data); + for (i = 0; i < DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT+1; i++) + if (data->info_strings[i]) + IDWriteLocalizedStrings_Release(data->info_strings[i]); heap_free(data->facename); heap_free(data); } @@ -655,8 +662,16 @@ static HRESULT WINAPI dwritefont_GetInformationalStrings(IDWriteFont *iface, DWRITE_INFORMATIONAL_STRING_ID stringid, IDWriteLocalizedStrings **strings, BOOL *exists) { struct dwrite_font *This = impl_from_IDWriteFont(iface); - FIXME("(%p)->(%d %p %p): stub\n", This, stringid, strings, exists); - return E_NOTIMPL; + TRACE("(%p)->(%d %p %p)\n", This, stringid, strings, exists); + if (stringid <= DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT && + This->data->info_strings[stringid]) + { + *exists = TRUE; + return clone_localizedstring(This->data->info_strings[stringid], strings); + } + + *exists = FALSE; + return S_OK; } static DWRITE_FONT_SIMULATIONS WINAPI dwritefont_GetSimulations(IDWriteFont *iface) @@ -889,6 +904,28 @@ static const IDWriteFontFamilyVtbl fontfamilyvtbl = { dwritefontfamily_GetMatchingFonts }; +static HRESULT fontfamily_AddFont(struct dwrite_fontfamily *family, IDWriteFont *font) +{ + struct dwrite_font *font_data = impl_from_IDWriteFont(font); + + if (family->data->font_count + 1 >= family->data->alloc) + { + struct dwrite_font_data **new_list; + UINT32 new_alloc; + new_alloc = family->data->alloc * 2; + new_list = heap_realloc(family->data->fonts, sizeof(*family->data->fonts) * new_alloc); + if (!new_list) + return E_OUTOFMEMORY; + family->data->fonts = new_list; + family->data->alloc = new_alloc; + } + + family->data->fonts[family->data->font_count] = font_data->data; + InterlockedIncrement(&font_data->data->ref); + family->data->font_count++; + return S_OK; +} + static HRESULT WINAPI dwritefontcollection_QueryInterface(IDWriteFontCollection *iface, REFIID riid, void **obj) { struct dwrite_fontcollection *This = impl_from_IDWriteFontCollection(iface); @@ -1046,6 +1083,31 @@ static const IDWriteFontCollectionVtbl fontcollectionvtbl = { dwritefontcollection_GetFontFromFontFace }; +static HRESULT add_family_to_collection(struct dwrite_fontcollection *collection, + IDWriteFontFamily *family) +{ + struct dwrite_fontfamily *d_family = impl_from_IDWriteFontFamily(family); + + if (collection->family_alloc < collection->family_count + 1) + { + struct dwrite_fontfamily_data **new_list; + DWORD new_alloc; + new_alloc = collection->family_alloc * 2; + new_list = heap_realloc(collection->font_families, sizeof(*new_list) * new_alloc); + if (!new_list) + return E_OUTOFMEMORY; + + collection->family_alloc = new_alloc; + collection->font_families = new_list; + } + + collection->font_families[collection->family_count] = d_family->data; + InterlockedIncrement(&d_family->data->ref); + collection->family_count ++; + + return S_OK; +} + static HRESULT add_family_syscollection(struct dwrite_fontcollection *collection, const WCHAR *family) { /* check for duplicate family name */ @@ -1105,6 +1167,139 @@ HRESULT get_system_fontcollection(IDWriteFontCollection **collection) return S_OK; } +HRESULT create_font_collection(IDWriteFactory* factory, IDWriteFontFileEnumerator* enumerator, IDWriteFontCollectionLoader *loader, IDWriteFontCollection **collection) +{ + struct dwrite_fontcollection *This; + HRESULT hr; + BOOL current = FALSE; + + *collection = NULL; + + This = heap_alloc(sizeof(struct dwrite_fontcollection)); + if (!This) return E_OUTOFMEMORY; + + This->IDWriteFontCollection_iface.lpVtbl = &fontcollectionvtbl; + This->ref = 1; + This->family_count = 0; + This->family_alloc = 2; + This->font_families = heap_alloc(sizeof(*This->font_families)*2); + This->count = 0; + This->alloc = 0; + This->families = NULL; + + *collection = &This->IDWriteFontCollection_iface; + + TRACE("building font collection:\n"); + do + { + hr = IDWriteFontFileEnumerator_MoveNext(enumerator, ¤t); + TRACE("%x, %i\n",hr, current); + if (SUCCEEDED(hr) && current) + { + IDWriteFontFile *font_file; + DWRITE_FONT_FILE_TYPE type; + DWRITE_FONT_FACE_TYPE face_type; + BOOL supported; + UINT32 face_count; + int i; + + hr = IDWriteFontFileEnumerator_GetCurrentFontFile(enumerator, &font_file); + if (FAILED(hr)) + { + TRACE("No more font files\n"); + break; + } + + hr = IDWriteFontFile_Analyze(font_file, &supported, &type, &face_type, &face_count); + if (FAILED(hr) || !supported || face_count == 0) + { + TRACE("Unsupported font (%08x, %i, %i)\n", hr, supported, face_count); + IDWriteFontFile_Release(font_file); + continue; + } + + for (i = 0; i < face_count; i++) + { + IDWriteFontFace* font_face; + IDWriteLocalizedStrings *family_name; + WCHAR buffer[255]; + BOOL exists = FALSE; + UINT32 index; + IDWriteFont *font; + + + hr = IDWriteFactory_CreateFontFace(factory, face_type, 1, &font_file, i, DWRITE_FONT_SIMULATIONS_NONE, &font_face); + if (FAILED(hr)) + { + WARN("Unable to create font face\n"); + continue; + } + + hr = create_font_from_fontface(font_face, &font); + if (FAILED(hr)) + { + WARN("Unable to create font\n"); + IDWriteFontFace_Release(font_face); + continue; + } + + hr = IDWriteFont_GetInformationalStrings(font, DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES, &family_name, &exists); + + if (FAILED(hr) || !exists) + { + WARN("Unable to get family name from font\n"); + IDWriteFont_Release(font); + IDWriteFontFace_Release(font_face); + continue; + } + + IDWriteLocalizedStrings_GetString(family_name, 0, buffer, 255); + IDWriteFontCollection_FindFamilyName(*collection, buffer, &index, &exists); + if (exists) + { + IDWriteFontFamily *font_family; + IDWriteLocalizedStrings_Release(family_name); + hr = IDWriteFontCollection_GetFontFamily(*collection, index, &font_family); + if (SUCCEEDED(hr)) + { + struct dwrite_fontfamily *family = impl_from_IDWriteFontFamily(font_family); + fontfamily_AddFont(family, font); + IDWriteFontFamily_Release(font_family); + } + else + WARN("Unable to get font family\n"); + } + else + { + IDWriteFontFamily *new_family; + + hr = create_fontfamily(family_name, &new_family); + if (SUCCEEDED(hr)) + { + struct dwrite_fontfamily *family = impl_from_IDWriteFontFamily(new_family); + fontfamily_AddFont(family, font); + hr = add_family_to_collection(This, new_family); + if (FAILED(hr)) + WARN("Failed to add new family to collection\n"); + IDWriteFontFamily_Release(new_family); + } + else + WARN("Failed to create new family\n"); + } + + IDWriteFont_Release(font); + IDWriteFontFace_Release(font_face); + } + + IDWriteFontFile_Release(font_file); + } + else + current = FALSE; + } while (current); + + return S_OK; +} + static HRESULT create_fontfamily_from_data(struct dwrite_fontfamily_data *data, IDWriteFontCollection *collection, IDWriteFontFamily **family) { HRESULT hr; @@ -1158,6 +1353,104 @@ static HRESULT create_fontfamily(IDWriteLocalizedStrings *familyname, IDWriteFon return S_OK; } +static HRESULT create_font_from_fontface(IDWriteFontFace *face, IDWriteFont **font) +{ + HRESULT hr; + struct dwrite_font *This; + + hr = create_font_base(font); + if (FAILED(hr)) + return hr; + + This = impl_from_IDWriteFont(*font); + + if (face) + { + const VOID* os2_data = NULL; + VOID* os2_context = NULL; + const VOID* head_data = NULL; + VOID* head_context = NULL; + const VOID* post_data = NULL; + VOID* post_context = NULL; + const VOID* name_data = NULL; + VOID* name_context = NULL; + UINT32 size; + BOOL os2_exists; + BOOL head_exists; + BOOL post_exists; + BOOL name_exists; + struct dwrite_fontface *fontface = impl_from_IDWriteFontFace(face); + int i; + + This->data->face_data = fontface->data; + InterlockedIncrement(&fontface->data->ref); + + IDWriteFontFace_TryGetFontTable(face, MS_OS2_TAG, &os2_data, &size, &os2_context, &os2_exists); + IDWriteFontFace_TryGetFontTable(face, MS_HEAD_TAG, &head_data, &size, &head_context, &head_exists); + IDWriteFontFace_TryGetFontTable(face, MS_POST_TAG, &post_data, &size, &post_context, &post_exists); + + get_font_properties(os2_data, head_data, post_data, &This->data->metrics, &This->data->stretch, &This->data->weight, &This->data->style); + + if (os2_exists) IDWriteFontFace_ReleaseFontTable(face, os2_context); + if (head_exists) IDWriteFontFace_ReleaseFontTable(face, head_context); + if (post_exists) IDWriteFontFace_ReleaseFontTable(face, post_context); + + IDWriteFontFace_TryGetFontTable(face, MS_NAME_TAG, &name_data, &size, &name_context, &name_exists); + if (name_exists) + { + for (i = 1; i <= DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT; i++) + { + int otindex = -1; + switch (i) + { + case DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE: + otindex = 0; break; + case DWRITE_INFORMATIONAL_STRING_VERSION_STRINGS: + otindex = 5; break; + case DWRITE_INFORMATIONAL_STRING_TRADEMARK: + otindex = 7; break; + case DWRITE_INFORMATIONAL_STRING_MANUFACTURER: + otindex = 8; break; + case DWRITE_INFORMATIONAL_STRING_DESIGNER: + otindex = 9; break; + case DWRITE_INFORMATIONAL_STRING_DESIGNER_URL: + otindex = 12; break; + case DWRITE_INFORMATIONAL_STRING_DESCRIPTION: + otindex = 10; break; + case DWRITE_INFORMATIONAL_STRING_FONT_VENDOR_URL: + otindex = 11; break; + case DWRITE_INFORMATIONAL_STRING_LICENSE_DESCRIPTION: + otindex = 13; break; + case DWRITE_INFORMATIONAL_STRING_LICENSE_INFO_URL: + otindex = 14; break; + case DWRITE_INFORMATIONAL_STRING_WIN32_FAMILY_NAMES: + otindex = 1; break; + case DWRITE_INFORMATIONAL_STRING_WIN32_SUBFAMILY_NAMES: + otindex = 2; break; + case DWRITE_INFORMATIONAL_STRING_PREFERRED_FAMILY_NAMES: + otindex = 16; break; + case DWRITE_INFORMATIONAL_STRING_PREFERRED_SUBFAMILY_NAMES: + otindex = 17; break; + case DWRITE_INFORMATIONAL_STRING_SAMPLE_TEXT: + otindex = 19; break; + default: + FIXME("Unknown info string id %i\n",i); + } + if (otindex == -1) + continue; + + hr = get_name_font_table_data(name_data, size, otindex, &This->data->info_strings[i]); + if (SUCCEEDED(hr) && IDWriteLocalizedStrings_GetCount(This->data->info_strings[i]) == 0) + { + IDWriteLocalizedStrings_Release(This->data->info_strings[i]); + This->data->info_strings[i] = NULL; + } + } + } + } + return S_OK; +} + static HRESULT create_font_from_data(struct dwrite_font_data *data, IDWriteFont **font) { struct dwrite_font *This; @@ -1190,6 +1483,7 @@ static HRESULT create_font_base(IDWriteFont **font) data->ref = 0; data->face_data = NULL; + memset(data->info_strings, 0, sizeof(data->info_strings)); ret = create_font_from_data( data, font ); if (FAILED(ret)) heap_free( data ); diff --git a/dlls/dwrite/main.c b/dlls/dwrite/main.c index bcddbba..2a0501d 100644 --- a/dlls/dwrite/main.c +++ b/dlls/dwrite/main.c @@ -397,6 +397,9 @@ struct dwritefactory{ LONG loader_count; IDWriteFontFileLoader **file_loaders; LONG file_loader_count; + + IDWriteFontCollection **collections; + LONG collection_count; }; static inline struct dwritefactory *impl_from_IDWriteFactory(IDWriteFactory *iface) @@ -452,6 +455,10 @@ static ULONG WINAPI dwritefactory_Release(IDWriteFactory *iface) heap_free(This->file_loaders); if (This->system_collection) IDWriteFontCollection_Release(This->system_collection); + for (i = 0; i < This->collection_count; i++) + if (This->collections[i]) + IDWriteFontCollection_Release(This->collections[i]); + heap_free(This->collections); heap_free(This); } @@ -482,9 +489,55 @@ static HRESULT WINAPI dwritefactory_GetSystemFontCollection(IDWriteFactory *ifac static HRESULT WINAPI dwritefactory_CreateCustomFontCollection(IDWriteFactory *iface, IDWriteFontCollectionLoader *loader, void const *key, UINT32 key_size, IDWriteFontCollection **collection) { + int i; + HRESULT hr; + IDWriteFontFileEnumerator *enumerator; struct dwritefactory *This = impl_from_IDWriteFactory(iface); - FIXME("(%p)->(%p %p %u %p): stub\n", This, loader, key, key_size, collection); - return E_NOTIMPL; + TRACE("(%p)->(%p %p %u %p)\n", This, loader, key, key_size, collection); + if (loader == NULL) + return E_INVALIDARG; + for (i = 0; i < This->loader_count; i++) + if (This->loaders[i] == loader) break; + if (i == This->loader_count) + return E_INVALIDARG; + + IDWriteFontCollectionLoader_AddRef(This->loaders[i]); + hr = IDWriteFontCollectionLoader_CreateEnumeratorFromKey(This->loaders[i], iface, key, key_size, &enumerator); + if (SUCCEEDED(hr)) + { + hr = create_font_collection(iface, enumerator, loader, collection); + if (SUCCEEDED(hr)) + { + int j; + for (j = 0; j < This->collection_count; j++) + if (This->collections[j] == NULL) + break; + + if (i == This->collection_count) + { + IDWriteFontCollection **new_list = NULL; + int new_count = 0; + + new_count = This->collection_count * 2; + new_list = heap_realloc_zero(This->collections, new_count * sizeof(*This->collections)); + + if (!new_list) + { + return E_OUTOFMEMORY; + } + else + { + This->collection_count = new_count; + This->collections = new_list; + } + } + IDWriteFontCollection_AddRef(*collection); + This->collections[i] = *collection; + } + IDWriteFontFileEnumerator_Release(enumerator); + } + IDWriteFontCollectionLoader_Release(This->loaders[i]); + return hr; } static HRESULT WINAPI dwritefactory_RegisterFontCollectionLoader(IDWriteFactory *iface, @@ -808,6 +861,8 @@ HRESULT WINAPI DWriteCreateFactory(DWRITE_FACTORY_TYPE type, REFIID riid, IUnkno This->file_loader_count = 2; This->file_loaders = heap_alloc_zero(sizeof(*This->file_loaders) * 2); This->system_collection = NULL; + This->collection_count = 2; + This->collections = heap_alloc_zero(sizeof(*This->collections) * 2); *factory = (IUnknown*)&This->IDWriteFactory_iface; diff --git a/dlls/dwrite/opentype.c b/dlls/dwrite/opentype.c index 532c126..f146da7 100644 --- a/dlls/dwrite/opentype.c +++ b/dlls/dwrite/opentype.c @@ -184,6 +184,22 @@ typedef struct } TT_OS2_V2; #include "poppack.h" +typedef struct { + WORD platformID; + WORD encodingID; + WORD languageID; + WORD nameID; + WORD length; + WORD offset; +}TT_NameRecord; + +typedef struct { + WORD format; + WORD count; + WORD stringOffset; + TT_NameRecord nameRecord[1]; +} TT_NAME_V0; + 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 */ @@ -445,3 +461,99 @@ VOID get_font_properties(LPCVOID os2, LPCVOID head, LPCVOID post, DWRITE_FONT_ME metrics->underlineThickness = GET_BE_WORD(tt_post->underlineThickness); } } + +HRESULT get_name_font_table_data(const void* table_data, UINT32 table_size, USHORT name_id, IDWriteLocalizedStrings** string) +{ + const TT_NAME_V0 *header = table_data; + BYTE *storage_area = 0; + USHORT count = 0; + int i; + HRESULT hr; + + hr = create_localizedstrings(string); + if (FAILED(hr)) return hr; + + storage_area = (LPBYTE)table_data + GET_BE_WORD(header->stringOffset); + count = GET_BE_WORD(header->count); + + for (i = 0; i < count; i++) + { + const TT_NameRecord *record = &header->nameRecord[i]; + USHORT lang_id = 0; + USHORT length = 0; + USHORT offset = 0; + USHORT encoding = 0; + USHORT platform; + if (GET_BE_WORD(record->nameID) != name_id) + continue; + platform = GET_BE_WORD(record->platformID); + /* Right now only accept unicode and windows encoded fonts */ + if (platform != 0 && platform != 3) + { + FIXME("Skip platform %i\n",platform); + continue; + } + lang_id = GET_BE_WORD(record->languageID); + length = GET_BE_WORD(record->length); + offset = GET_BE_WORD(record->offset); + encoding = GET_BE_WORD(record->encodingID); + if (lang_id < 0x8000) + { + WCHAR *name_string; + WCHAR locale[255] = {'e','n','-','u','s',0}; + DWORD len; + UINT codepage = 0; + if (platform == 3) + { + switch (encoding) + { + case 0: /* Symbol == Unicode */ + case 1: /*Unicode*/ + break; + case 2: /*SHIFT-JIS*/ + codepage = 932; + break; + case 3: /*GB2312*/ + codepage = 936; + break; + case 4: /*BIG5*/ + codepage = 950; + break; + case 5: /*Wansung*/ + codepage = 20949; + break; + case 6: /*Johab*/ + codepage = 1361; + break; + } + } + + LCIDToLocaleName(lang_id, locale, 255, 0); + + if (codepage) + { + len = MultiByteToWideChar(codepage, 0, (LPSTR)(storage_area + offset), length, NULL, 0); + name_string = heap_alloc(sizeof(WCHAR) * len); + MultiByteToWideChar(codepage, 0, (LPSTR)(storage_area + offset), length, name_string, len); + } + else + { + int i; + name_string = heap_strdupnW((LPWSTR)(storage_area + offset), (length/2)); + for (i = 0; i < (length/2); i++) + name_string[i] = GET_BE_WORD(name_string[i]); + } + + TRACE("string %s locale %s found\n", debugstr_w(name_string), debugstr_w(locale)); + add_localizedstring(*string, locale, name_string); + heap_free(name_string); + } + else + { + FIXME("Handle NAME format 1"); + continue; + } + } + + return hr; +} diff --git a/dlls/dwrite/tests/font.c b/dlls/dwrite/tests/font.c index 3a17064..910b75d 100644 --- a/dlls/dwrite/tests/font.c +++ b/dlls/dwrite/tests/font.c @@ -36,6 +36,27 @@ static void _expect_ref(IUnknown* obj, ULONG ref, int line) ok_(__FILE__,line)(rc-1 == ref, "expected refcount %d, got %d\n", ref, rc-1); } +static INT stream_access_block = 0; + +#define STREAM_ACCESS_INVALID _stream_access_invalid(__LINE__) +static void _stream_access_invalid(int line) +{ + stream_access_block = line; +} + +#define STREAM_ACCESS_VALID _stream_access_valid() +static void _stream_access_valid(void) +{ + stream_access_block = 0; +} + +#define CHECK_STREAM_ACCESS _check_stream_access() +static void _check_stream_access(void) +{ + ok_(__FILE__,stream_access_block)(stream_access_block == 0, "Unexpected file stream access\n"); +} + + static IDWriteFactory *factory; static const WCHAR tahomaW[] = {'T','a','h','o','m','a',0}; @@ -88,6 +109,7 @@ static HRESULT WINAPI fontdatastream_ReadFileFragment(IDWriteFontFileStream *ifa { struct test_fontdatastream *This = impl_from_IDWriteFontFileStream(iface); *fragment_context = NULL; + CHECK_STREAM_ACCESS; if (offset+fragment_size > This->size) { *fragment_start = NULL; @@ -188,6 +210,152 @@ static const struct IDWriteFontFileLoaderVtbl resourcefontfileloadervtbl = { resourcefontfileloader_CreateStreamFromKey }; +struct test_fontenumerator +{ + IDWriteFontFileEnumerator IDWriteFontFileEnumerator_iface; + LONG ref; + + DWORD index; + IDWriteFontFile *font_file; +}; + +static inline struct test_fontenumerator *impl_from_IDWriteFontFileEnumerator(IDWriteFontFileEnumerator* iface) +{ + return CONTAINING_RECORD(iface, struct test_fontenumerator, IDWriteFontFileEnumerator_iface); +} + + +static HRESULT WINAPI singlefontfileenumerator_QueryInterface(IDWriteFontFileEnumerator *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDWriteFontFileEnumerator)) + { + *obj = iface; + IDWriteFontFileEnumerator_AddRef(iface); + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI singlefontfileenumerator_AddRef(IDWriteFontFileEnumerator *iface) +{ + struct test_fontenumerator *This = impl_from_IDWriteFontFileEnumerator(iface); + ULONG ref = InterlockedIncrement(&This->ref); + return ref; +} + +static ULONG WINAPI singlefontfileenumerator_Release(IDWriteFontFileEnumerator *iface) +{ + struct test_fontenumerator *This = impl_from_IDWriteFontFileEnumerator(iface); + ULONG ref = InterlockedDecrement(&This->ref); + if (ref == 0) + { + IDWriteFontFile_Release(This->font_file); + HeapFree(GetProcessHeap(), 0, This); + } + return ref; +} + +static HRESULT WINAPI singlefontfileenumerator_GetCurrentFontFile(IDWriteFontFileEnumerator *iface, IDWriteFontFile ** fontFile) +{ + struct test_fontenumerator *This = impl_from_IDWriteFontFileEnumerator(iface); + IDWriteFontFile_AddRef(This->font_file); + *fontFile = This->font_file; + return S_OK; +} + +static HRESULT WINAPI singlefontfileenumerator_MoveNext(IDWriteFontFileEnumerator *iface, BOOL * hasCurrentFile) +{ + struct test_fontenumerator *This = impl_from_IDWriteFontFileEnumerator(iface); + if (This->index > 1) + { + *hasCurrentFile = FALSE; + return S_OK; + } + This->index ++; + *hasCurrentFile = TRUE; + return S_OK; +} + +static const struct IDWriteFontFileEnumeratorVtbl singlefontfileenumeratorvtbl = +{ + singlefontfileenumerator_QueryInterface, + singlefontfileenumerator_AddRef, + singlefontfileenumerator_Release, + singlefontfileenumerator_MoveNext, + singlefontfileenumerator_GetCurrentFontFile, +}; + +static HRESULT create_enumerator(IDWriteFontFile *font_file, IDWriteFontFileEnumerator **enumer) +{ + struct test_fontenumerator *This; + + This = HeapAlloc(GetProcessHeap(), 0 , sizeof(struct test_fontenumerator)); + if (!This) + return E_OUTOFMEMORY; + This->IDWriteFontFileEnumerator_iface.lpVtbl = &singlefontfileenumeratorvtbl; + This->ref = 1; + This->index = 0; + This->font_file = font_file; + IDWriteFontFile_AddRef(font_file); + + *enumer = &This->IDWriteFontFileEnumerator_iface; + return S_OK; +} + +struct test_fontcollectionloader +{ + IDWriteFontCollectionLoader IDWriteFontFileCollectionLoader_iface; + + IDWriteFontFileLoader *loader; +}; + +static inline struct test_fontcollectionloader *impl_from_IDWriteFontFileCollectionLoader(IDWriteFontCollectionLoader* iface) +{ + return CONTAINING_RECORD(iface, struct test_fontcollectionloader, IDWriteFontFileCollectionLoader_iface); +} + +static HRESULT WINAPI resourcecollectionloader_QueryInterface(IDWriteFontCollectionLoader *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDWriteFontCollectionLoader)) + { + *obj = iface; + IDWriteFontCollectionLoader_AddRef(iface); + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI resourcecollectionloader_AddRef(IDWriteFontCollectionLoader *iface) +{ + return 2; +} + +static ULONG WINAPI resourcecollectionloader_Release(IDWriteFontCollectionLoader *iface) +{ + return 1; +} + +static HRESULT WINAPI resourcecollectionloader_CreateEnumeratorFromKey(IDWriteFontCollectionLoader *iface, IDWriteFactory * factory, const void * collectionKey, UINT32 collectionKeySize, IDWriteFontFileEnumerator ** fontFileEnumerator) +{ + HRESULT hr; + IDWriteFontFile *font_file; + struct test_fontcollectionloader *This = impl_from_IDWriteFontFileCollectionLoader(iface); + + IDWriteFactory_CreateCustomFontFileReference(factory, collectionKey, collectionKeySize, This->loader, &font_file); + + hr = create_enumerator(font_file, fontFileEnumerator); + ok(hr == S_OK, "got 0x%08x\n", hr); + + IDWriteFontFile_Release(font_file); + return hr; +} + +static const struct IDWriteFontCollectionLoaderVtbl resourcecollectionloadervtbl = { + resourcecollectionloader_QueryInterface, + resourcecollectionloader_AddRef, + resourcecollectionloader_Release, + resourcecollectionloader_CreateEnumeratorFromKey +}; static void test_CreateFontFromLOGFONT(void) { @@ -821,6 +989,50 @@ if (0) /* crashes on native */ IDWriteFontFace_Release(fontface); } +static HRESULT WINAPI fontfileenumerator_QueryInterface(IDWriteFontFileEnumerator *iface, REFIID riid, void **obj) +{ + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IDWriteFontFileEnumerator)) + { + *obj = iface; + IDWriteFontFileEnumerator_AddRef(iface); + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI fontfileenumerator_AddRef(IDWriteFontFileEnumerator *iface) +{ + return 2; +} + +static ULONG WINAPI fontfileenumerator_Release(IDWriteFontFileEnumerator *iface) +{ + return 1; +} + +static HRESULT WINAPI fontfileenumerator_GetCurrentFontFile(IDWriteFontFileEnumerator *iface, IDWriteFontFile ** fontFile) +{ + *fontFile = NULL; + return E_FAIL; +} + +static HRESULT WINAPI fontfileenumerator_MoveNext(IDWriteFontFileEnumerator *iface, BOOL * hasCurrentFile) +{ + *hasCurrentFile = FALSE; + return S_OK; +} + + +static const struct IDWriteFontFileEnumeratorVtbl dwritefontfileenumeratorvtbl = +{ + fontfileenumerator_QueryInterface, + fontfileenumerator_AddRef, + fontfileenumerator_Release, + fontfileenumerator_MoveNext, + fontfileenumerator_GetCurrentFontFile, +}; + + static HRESULT WINAPI fontcollectionloader_QueryInterface(IDWriteFontCollectionLoader *iface, REFIID riid, void **obj) { *obj = iface; @@ -839,6 +1051,8 @@ static ULONG WINAPI fontcollectionloader_Release(IDWriteFontCollectionLoader *if static HRESULT WINAPI fontcollectionloader_CreateEnumeratorFromKey(IDWriteFontCollectionLoader *iface, IDWriteFactory * factory, const void * collectionKey, UINT32 collectionKeySize, IDWriteFontFileEnumerator ** fontFileEnumerator) { + static IDWriteFontFileEnumerator enumer = {&dwritefontfileenumeratorvtbl}; + *fontFileEnumerator = &enumer; return S_OK; } @@ -853,7 +1067,12 @@ static void test_CustomFontCollection(void) { IDWriteFontCollectionLoader collection = { &dwritefontcollectionloadervtbl }; IDWriteFontCollectionLoader collection2 = { &dwritefontcollectionloadervtbl }; + IDWriteFontCollectionLoader collection3 = { &dwritefontcollectionloadervtbl }; + IDWriteFontCollection *font_collection = NULL; + static IDWriteFontFileLoader rloader = { &resourcefontfileloadervtbl }; + struct test_fontcollectionloader resource_collection = {{&resourcecollectionloadervtbl}, &rloader}; HRESULT hr; + HRSRC font; hr = IDWriteFactory_RegisterFontCollectionLoader(factory, &collection); ok(hr == S_OK, "got 0x%08x\n", hr); @@ -861,6 +1080,118 @@ static void test_CustomFontCollection(void) ok(hr == S_OK, "got 0x%08x\n", hr); hr = IDWriteFactory_RegisterFontCollectionLoader(factory, &collection); ok(hr == DWRITE_E_ALREADYREGISTERED, "got 0x%08x\n", hr); + hr = IDWriteFactory_RegisterFontFileLoader(factory, &rloader); + ok(hr == S_OK, "got 0x%08x\n", hr); + hr = IDWriteFactory_RegisterFontCollectionLoader(factory, &resource_collection.IDWriteFontFileCollectionLoader_iface); + ok(hr == S_OK, "got 0x%08x\n", hr); + + hr = IDWriteFactory_CreateCustomFontCollection(factory, &collection3, "Billy", 6, &font_collection); + ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); + + hr = IDWriteFactory_CreateCustomFontCollection(factory, &collection, "Billy", 6, &font_collection); + ok(hr == S_OK, "got 0x%08x\n", hr); + IDWriteFontCollection_Release(font_collection); + + hr = IDWriteFactory_CreateCustomFontCollection(factory, &collection2, "Billy", 6, &font_collection); + ok(hr == S_OK, "got 0x%08x\n", hr); + IDWriteFontCollection_Release(font_collection); + + hr = IDWriteFactory_CreateCustomFontCollection(factory, (IDWriteFontCollectionLoader*)0xdeadbeef, "Billy", 6, &font_collection); + ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); + + font = FindResourceA(GetModuleHandleA(NULL), (LPCSTR)MAKEINTRESOURCE(1), (LPCSTR)RT_RCDATA); + ok(font != NULL, "Failed to find font resource\n"); + if (font) + { + IDWriteFontFamily *family, *family2, *family3; + IDWriteFont *idfont, *idfont2; + IDWriteFontFace *idfontface, *idfontface2; + IDWriteFontFile *fontfile, *fontfile2; + IDWriteLocalizedStrings *string; + WCHAR szFontName[] = {'w','i','n','e','_','t','e','s','t',0}; + UINT32 index; + BOOL exists; + + hr = IDWriteFactory_CreateCustomFontCollection(factory, &resource_collection.IDWriteFontFileCollectionLoader_iface, &font, sizeof(HRSRC), &font_collection); + + ok(hr == S_OK, "got 0x%08x\n",hr); + hr = IDWriteFontCollection_FindFamilyName(font_collection, szFontName, &index, &exists); + ok(hr == S_OK, "got 0x%08x\n",hr); + ok(index == 0, "got index %i\n",index); + ok(exists, "got exists %i\n",exists); + + hr = IDWriteFontCollection_GetFontFamily(font_collection, 0, &family); + ok(hr == S_OK, "got 0x%08x\n", hr); + EXPECT_REF(family, 1); + + hr = IDWriteFontCollection_GetFontFamily(font_collection, 0, &family2); + ok(hr == S_OK, "got 0x%08x\n", hr); + EXPECT_REF(family2, 1); + ok(family != family2, "Family instances should not match\n"); + + hr = IDWriteFontFamily_GetFont(family, 0, &idfont); + ok(hr == S_OK, "got 0x%08x\n", hr); + EXPECT_REF(idfont, 1); + EXPECT_REF(family, 2); + hr = IDWriteFontFamily_GetFont(family, 0, &idfont2); + ok(hr == S_OK, "got 0x%08x\n", hr); + EXPECT_REF(idfont2, 1); + EXPECT_REF(family, 3); + ok(idfont != idfont2, "Font instances should not match\n"); + IDWriteFont_Release(idfont2); + + STREAM_ACCESS_INVALID; + + IDWriteFont_GetWeight(idfont); + IDWriteFont_GetStretch(idfont); + IDWriteFont_GetStyle(idfont); + IDWriteFont_GetSimulations(idfont); + IDWriteFont_HasCharacter(idfont, 'A', &exists); + IDWriteFont_GetInformationalStrings(idfont, DWRITE_INFORMATIONAL_STRING_COPYRIGHT_NOTICE, &string, &exists); + ok(exists, "Could not find informational string\n"); + EXPECT_REF(string, 1); + + STREAM_ACCESS_VALID; + + IDWriteFont_GetFontFamily(idfont, &family3); + EXPECT_REF(family, 3); + ok(family == family3, "wrong family returned\n"); + IDWriteFontFamily_Release(family3); + + hr = IDWriteFont_CreateFontFace(idfont, &idfontface); + ok(hr == S_OK, "got 0x%08x\n", hr); + EXPECT_REF(idfont, 1); + + hr = IDWriteFontFamily_GetFont(family2, 0, &idfont2); + ok(hr == S_OK, "got 0x%08x\n", hr); + EXPECT_REF(idfont2, 1); + EXPECT_REF(idfont, 1); + ok(idfont2 != idfont, "Font instances shoudl not match\n"); + + hr = IDWriteFont_CreateFontFace(idfont2, &idfontface2); + ok(hr == S_OK, "got 0x%08x\n", hr); + todo_wine ok(idfontface2 == idfontface, "fontfaces should match\n"); + + index = 1; + hr = IDWriteFontFace_GetFiles(idfontface, &index, &fontfile); + ok(hr == S_OK, "got 0x%08x\n", hr); + todo_wine EXPECT_REF(fontfile, 2); + index = 1; + hr = IDWriteFontFace_GetFiles(idfontface2, &index, &fontfile2); + ok(hr == S_OK, "got 0x%08x\n", hr); + todo_wine EXPECT_REF(fontfile2, 3); + ok(fontfile == fontfile2, "fontfiles should match\n"); + + IDWriteFontFile_Release(fontfile); + IDWriteFontFile_Release(fontfile2); + IDWriteFontFace_Release(idfontface); + IDWriteFont_Release(idfont); + IDWriteFontFace_Release(idfontface2); + IDWriteFont_Release(idfont2); + IDWriteFontFamily_Release(family2); + IDWriteFontFamily_Release(family); + IDWriteFontCollection_Release(font_collection); + } hr = IDWriteFactory_UnregisterFontCollectionLoader(factory, &collection); ok(hr == S_OK, "got 0x%08x\n", hr); @@ -868,6 +1199,10 @@ static void test_CustomFontCollection(void) ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = IDWriteFactory_UnregisterFontCollectionLoader(factory, &collection2); ok(hr == S_OK, "got 0x%08x\n", hr); + hr = IDWriteFactory_UnregisterFontCollectionLoader(factory, &resource_collection.IDWriteFontFileCollectionLoader_iface); + ok(hr == S_OK, "got 0x%08x\n", hr); + hr = IDWriteFactory_UnregisterFontFileLoader(factory, &rloader); + ok(hr == S_OK, "got 0x%08x\n", hr); } static HRESULT WINAPI fontfileloader_QueryInterface(IDWriteFontFileLoader *iface, REFIID riid, void **obj)