From fb8226f639093a3fef41f7273fa0089246d1dfa4 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Fri, 6 Oct 2017 12:45:20 +0300 Subject: [PATCH] dwrite: Implement IDWriteInMemoryFontFileLoader. Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/dwrite/dwrite_private.h | 1 + dlls/dwrite/font.c | 317 +++++++++++++++++++++++++++++++++++++++++++ dlls/dwrite/main.c | 4 +- dlls/dwrite/tests/font.c | 116 ++++++++++++++-- 4 files changed, 421 insertions(+), 17 deletions(-) diff --git a/dlls/dwrite/dwrite_private.h b/dlls/dwrite/dwrite_private.h index 90014202908..38cf11a1e0e 100644 --- a/dlls/dwrite/dwrite_private.h +++ b/dlls/dwrite/dwrite_private.h @@ -210,6 +210,7 @@ extern HRESULT create_gdiinterop(IDWriteFactory5*,IDWriteGdiInterop1**) DECLSPEC extern void fontface_detach_from_cache(IDWriteFontFace4*) DECLSPEC_HIDDEN; extern void factory_lock(IDWriteFactory5*) DECLSPEC_HIDDEN; extern void factory_unlock(IDWriteFactory5*) DECLSPEC_HIDDEN; +extern HRESULT create_inmemory_fileloader(IDWriteFontFileLoader**) DECLSPEC_HIDDEN; /* Opentype font table functions */ struct dwrite_font_props { diff --git a/dlls/dwrite/font.c b/dlls/dwrite/font.c index 81d16245090..1916bf22fc9 100644 --- a/dlls/dwrite/font.c +++ b/dlls/dwrite/font.c @@ -4480,6 +4480,32 @@ struct dwrite_localfontfileloader { static struct dwrite_localfontfileloader local_fontfile_loader; +struct dwrite_inmemory_stream_data +{ + LONG ref; + IUnknown *owner; + void *data; + UINT32 size; +}; + +struct dwrite_inmemory_filestream +{ + IDWriteFontFileStream IDWriteFontFileStream_iface; + LONG ref; + + struct dwrite_inmemory_stream_data *data; +}; + +struct dwrite_inmemory_fileloader +{ + IDWriteInMemoryFontFileLoader IDWriteInMemoryFontFileLoader_iface; + LONG ref; + + struct dwrite_inmemory_stream_data **streams; + UINT32 filecount; + UINT32 capacity; +}; + static inline struct dwrite_localfontfileloader *impl_from_IDWriteLocalFontFileLoader(IDWriteLocalFontFileLoader *iface) { return CONTAINING_RECORD(iface, struct dwrite_localfontfileloader, IDWriteLocalFontFileLoader_iface); @@ -4490,6 +4516,27 @@ static inline struct dwrite_localfontfilestream *impl_from_IDWriteFontFileStream return CONTAINING_RECORD(iface, struct dwrite_localfontfilestream, IDWriteFontFileStream_iface); } +static inline struct dwrite_inmemory_fileloader *impl_from_IDWriteInMemoryFontFileLoader(IDWriteInMemoryFontFileLoader *iface) +{ + return CONTAINING_RECORD(iface, struct dwrite_inmemory_fileloader, IDWriteInMemoryFontFileLoader_iface); +} + +static inline struct dwrite_inmemory_filestream *inmemory_impl_from_IDWriteFontFileStream(IDWriteFontFileStream *iface) +{ + return CONTAINING_RECORD(iface, struct dwrite_inmemory_filestream, IDWriteFontFileStream_iface); +} + +static void release_inmemory_stream(struct dwrite_inmemory_stream_data *stream) +{ + if (InterlockedDecrement(&stream->ref) == 0) { + if (stream->owner) + IUnknown_Release(stream->owner); + else + heap_free(stream->data); + heap_free(stream); + } +} + static HRESULT WINAPI localfontfilestream_QueryInterface(IDWriteFontFileStream *iface, REFIID riid, void **obj) { struct dwrite_localfontfilestream *This = impl_from_IDWriteFontFileStream(iface); @@ -5967,3 +6014,273 @@ HRESULT create_fontfacereference(IDWriteFactory5 *factory, IDWriteFontFile *file return S_OK; } + +static HRESULT WINAPI inmemoryfilestream_QueryInterface(IDWriteFontFileStream *iface, REFIID riid, void **obj) +{ + struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); + + TRACE_(dwrite_file)("(%p)->(%s, %p)\n", stream, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IDWriteFontFileStream) || IsEqualIID(riid, &IID_IUnknown)) { + *obj = iface; + IDWriteFontFileStream_AddRef(iface); + return S_OK; + } + + *obj = NULL; + + WARN("%s not implemented.\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI inmemoryfilestream_AddRef(IDWriteFontFileStream *iface) +{ + struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); + ULONG ref = InterlockedIncrement(&stream->ref); + TRACE_(dwrite_file)("(%p)->(%u)\n", stream, ref); + return ref; +} + +static ULONG WINAPI inmemoryfilestream_Release(IDWriteFontFileStream *iface) +{ + struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); + ULONG ref = InterlockedDecrement(&stream->ref); + + TRACE_(dwrite_file)("(%p)->(%u)\n", stream, ref); + + if (!ref) { + release_inmemory_stream(stream->data); + heap_free(stream); + } + + return ref; +} + +static HRESULT WINAPI inmemoryfilestream_ReadFileFragment(IDWriteFontFileStream *iface, void const **fragment_start, + UINT64 offset, UINT64 fragment_size, void **fragment_context) +{ + struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); + + TRACE_(dwrite_file)("(%p)->(%p, 0x%s, 0x%s, %p)\n", stream, fragment_start, + wine_dbgstr_longlong(offset), wine_dbgstr_longlong(fragment_size), fragment_context); + + *fragment_context = NULL; + + if ((offset >= stream->data->size - 1) || (fragment_size > stream->data->size - offset)) { + *fragment_start = NULL; + return E_FAIL; + } + + *fragment_start = (char *)stream->data->data + offset; + return S_OK; +} + +static void WINAPI inmemoryfilestream_ReleaseFileFragment(IDWriteFontFileStream *iface, void *fragment_context) +{ + struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); + + TRACE_(dwrite_file)("(%p)->(%p)\n", stream, fragment_context); +} + +static HRESULT WINAPI inmemoryfilestream_GetFileSize(IDWriteFontFileStream *iface, UINT64 *size) +{ + struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); + + TRACE_(dwrite_file)("(%p)->(%p)\n", stream, size); + + *size = stream->data->size; + + return S_OK; +} + +static HRESULT WINAPI inmemoryfilestream_GetLastWriteTime(IDWriteFontFileStream *iface, UINT64 *last_writetime) +{ + struct dwrite_inmemory_filestream *stream = inmemory_impl_from_IDWriteFontFileStream(iface); + + TRACE_(dwrite_file)("(%p)->(%p)\n", stream, last_writetime); + + *last_writetime = 0; + + return E_NOTIMPL; +} + +static const IDWriteFontFileStreamVtbl inmemoryfilestreamvtbl = { + inmemoryfilestream_QueryInterface, + inmemoryfilestream_AddRef, + inmemoryfilestream_Release, + inmemoryfilestream_ReadFileFragment, + inmemoryfilestream_ReleaseFileFragment, + inmemoryfilestream_GetFileSize, + inmemoryfilestream_GetLastWriteTime, +}; + +static HRESULT WINAPI inmemoryfontfileloader_QueryInterface(IDWriteInMemoryFontFileLoader *iface, + REFIID riid, void **obj) +{ + struct dwrite_inmemory_fileloader *loader = impl_from_IDWriteInMemoryFontFileLoader(iface); + + TRACE("(%p)->(%s, %p)\n", loader, debugstr_guid(riid), obj); + + if (IsEqualIID(riid, &IID_IDWriteInMemoryFontFileLoader) || + IsEqualIID(riid, &IID_IDWriteFontFileLoader) || + IsEqualIID(riid, &IID_IUnknown)) + { + *obj = iface; + IDWriteInMemoryFontFileLoader_AddRef(iface); + return S_OK; + } + + WARN("%s not implemented.\n", debugstr_guid(riid)); + + *obj = NULL; + + return E_NOINTERFACE; +} + +static ULONG WINAPI inmemoryfontfileloader_AddRef(IDWriteInMemoryFontFileLoader *iface) +{ + struct dwrite_inmemory_fileloader *loader = impl_from_IDWriteInMemoryFontFileLoader(iface); + ULONG ref = InterlockedIncrement(&loader->ref); + TRACE("(%p)->(%u)\n", loader, ref); + return ref; +} + +static ULONG WINAPI inmemoryfontfileloader_Release(IDWriteInMemoryFontFileLoader *iface) +{ + struct dwrite_inmemory_fileloader *loader = impl_from_IDWriteInMemoryFontFileLoader(iface); + ULONG ref = InterlockedDecrement(&loader->ref); + + TRACE("(%p)->(%u)\n", loader, ref); + + if (!ref) { + UINT32 i; + + for (i = 0; i < loader->filecount; i++) + release_inmemory_stream(loader->streams[i]); + heap_free(loader->streams); + heap_free(loader); + } + + return ref; +} + +static HRESULT WINAPI inmemoryfontfileloader_CreateStreamFromKey(IDWriteInMemoryFontFileLoader *iface, + void const *key, UINT32 key_size, IDWriteFontFileStream **ret) +{ + struct dwrite_inmemory_fileloader *loader = impl_from_IDWriteInMemoryFontFileLoader(iface); + struct dwrite_inmemory_filestream *stream; + DWORD index; + + TRACE("(%p)->(%p, %u, %p)\n", loader, key, key_size, ret); + + *ret = NULL; + + if (key_size != sizeof(DWORD)) + return E_INVALIDARG; + + index = *(DWORD *)key; + + if (index >= loader->filecount) + return E_INVALIDARG; + + if (!(stream = heap_alloc(sizeof(*stream)))) + return E_OUTOFMEMORY; + + stream->IDWriteFontFileStream_iface.lpVtbl = &inmemoryfilestreamvtbl; + stream->ref = 1; + stream->data = loader->streams[index]; + InterlockedIncrement(&stream->data->ref); + + *ret = &stream->IDWriteFontFileStream_iface; + + return S_OK; +} + +static HRESULT WINAPI inmemoryfontfileloader_CreateInMemoryFontFileReference(IDWriteInMemoryFontFileLoader *iface, + IDWriteFactory *factory, void const *data, UINT32 data_size, IUnknown *owner, IDWriteFontFile **fontfile) +{ + struct dwrite_inmemory_fileloader *loader = impl_from_IDWriteInMemoryFontFileLoader(iface); + struct dwrite_inmemory_stream_data *stream; + DWORD key; + + TRACE("(%p)->(%p, %p, %u, %p, %p)\n", loader, factory, data, data_size, owner, fontfile); + + *fontfile = NULL; + + if (loader->filecount == loader->capacity) { + if (loader->streams) { + struct dwrite_inmemory_stream_data **ptr; + + if (!(ptr = heap_realloc(loader->streams, 2 * loader->capacity * sizeof(*loader->streams)))) + return E_OUTOFMEMORY; + + loader->streams = ptr; + loader->capacity *= 2; + } + else { + loader->capacity = 16; + loader->streams = heap_alloc(loader->capacity * sizeof(*loader->streams)); + } + } + + if (!(stream = heap_alloc(sizeof(*stream)))) + return E_OUTOFMEMORY; + + stream->ref = 1; + stream->size = data_size; + stream->owner = owner; + if (stream->owner) { + IUnknown_AddRef(stream->owner); + stream->data = (void *)data; + } + else { + if (!(stream->data = heap_alloc(data_size))) { + heap_free(stream); + return E_OUTOFMEMORY; + } + memcpy(stream->data, data, data_size); + } + + key = loader->filecount; + loader->streams[loader->filecount++] = stream; + + return IDWriteFactory_CreateCustomFontFileReference(factory, &key, sizeof(key), + (IDWriteFontFileLoader *)&loader->IDWriteInMemoryFontFileLoader_iface, fontfile); +} + +static UINT32 WINAPI inmemoryfontfileloader_GetFileCount(IDWriteInMemoryFontFileLoader *iface) +{ + struct dwrite_inmemory_fileloader *loader = impl_from_IDWriteInMemoryFontFileLoader(iface); + + TRACE("(%p)\n", loader); + + return loader->filecount; +} + +static const IDWriteInMemoryFontFileLoaderVtbl inmemoryfontfileloadervtbl = +{ + inmemoryfontfileloader_QueryInterface, + inmemoryfontfileloader_AddRef, + inmemoryfontfileloader_Release, + inmemoryfontfileloader_CreateStreamFromKey, + inmemoryfontfileloader_CreateInMemoryFontFileReference, + inmemoryfontfileloader_GetFileCount, +}; + +HRESULT create_inmemory_fileloader(IDWriteFontFileLoader **ret) +{ + struct dwrite_inmemory_fileloader *loader; + + *ret = NULL; + + loader = heap_alloc_zero(sizeof(*loader)); + if (!loader) + return E_OUTOFMEMORY; + + loader->IDWriteInMemoryFontFileLoader_iface.lpVtbl = &inmemoryfontfileloadervtbl; + loader->ref = 1; + + *ret = (IDWriteFontFileLoader *)&loader->IDWriteInMemoryFontFileLoader_iface; + + return S_OK; +} diff --git a/dlls/dwrite/main.c b/dlls/dwrite/main.c index 6bf5b7310c2..1cdcc7edb53 100644 --- a/dlls/dwrite/main.c +++ b/dlls/dwrite/main.c @@ -1617,9 +1617,9 @@ static HRESULT WINAPI dwritefactory5_CreateInMemoryFontFileLoader(IDWriteFactory { struct dwritefactory *This = impl_from_IDWriteFactory5(iface); - FIXME("(%p)->(%p): stub\n", This, loader); + TRACE("(%p)->(%p)\n", This, loader); - return E_NOTIMPL; + return create_inmemory_fileloader(loader); } static HRESULT WINAPI dwritefactory5_CreateHttpFontFileLoader(IDWriteFactory5 *iface, WCHAR const *referrer_url, WCHAR const *extra_headers, diff --git a/dlls/dwrite/tests/font.c b/dlls/dwrite/tests/font.c index a57806aeed8..e97c4d133b1 100644 --- a/dlls/dwrite/tests/font.c +++ b/dlls/dwrite/tests/font.c @@ -7644,14 +7644,15 @@ static void test_inmemory_file_loader(void) IDWriteFontFileLoader *loader, *loader2; IDWriteInMemoryFontFileLoader *inmemory; struct testowner_object ownerobject; + const void *key, *data, *frag_start; + UINT64 file_size, size, writetime; IDWriteFontFile *file, *file2; IDWriteFontFace *fontface; IDWriteFactory5 *factory5; + void *context, *context2; IDWriteFactory *factory; - const void *key, *data; UINT32 count, key_size; - UINT64 file_size; - void *context; + DWORD ref_key; HRESULT hr; ULONG ref; @@ -7666,15 +7667,9 @@ static void test_inmemory_file_loader(void) EXPECT_REF(factory5, 1); hr = IDWriteFactory5_CreateInMemoryFontFileLoader(factory5, &loader); -todo_wine ok(hr == S_OK, "got %#x\n", hr); EXPECT_REF(factory5, 1); - if (FAILED(hr)) { - IDWriteFactory5_Release(factory5); - return; - } - testowner_init(&ownerobject); fontface = create_fontface((IDWriteFactory *)factory5); @@ -7688,6 +7683,9 @@ todo_wine IDWriteFontFileLoader_Release(loader); EXPECT_REF(inmemory, 1); + count = IDWriteInMemoryFontFileLoader_GetFileCount(inmemory); + ok(!count, "Unexpected file count %u.\n", count); + /* Use whole font blob to construct in-memory file. */ count = 1; hr = IDWriteFontFace_GetFiles(fontface, &count, &file); @@ -7715,6 +7713,9 @@ todo_wine file_size, NULL, &file); ok(hr == E_INVALIDARG, "got %#x\n", hr); + count = IDWriteInMemoryFontFileLoader_GetFileCount(inmemory); + ok(count == 1, "Unexpected file count %u.\n", count); + hr = IDWriteFactory5_RegisterFontFileLoader(factory5, (IDWriteFontFileLoader *)inmemory); ok(hr == S_OK, "got %#x\n", hr); EXPECT_REF(inmemory, 2); @@ -7726,6 +7727,9 @@ todo_wine EXPECT_REF(&ownerobject.IUnknown_iface, 2); EXPECT_REF(inmemory, 3); + count = IDWriteInMemoryFontFileLoader_GetFileCount(inmemory); + ok(count == 2, "Unexpected file count %u.\n", count); + hr = IDWriteInMemoryFontFileLoader_CreateInMemoryFontFileReference(inmemory, (IDWriteFactory *)factory5, data, file_size, &ownerobject.IUnknown_iface, &file2); ok(hr == S_OK, "got %#x\n", hr); @@ -7733,6 +7737,9 @@ todo_wine EXPECT_REF(&ownerobject.IUnknown_iface, 3); EXPECT_REF(inmemory, 4); + count = IDWriteInMemoryFontFileLoader_GetFileCount(inmemory); + ok(count == 3, "Unexpected file count %u.\n", count); + /* Check in-memory reference key format. */ hr = IDWriteFontFile_GetReferenceKey(file, &key, &key_size); ok(hr == S_OK, "got %#x\n", hr); @@ -7762,26 +7769,105 @@ todo_wine /* Release file at index 1, create new one to see if index is reused. */ EXPECT_REF(&ownerobject.IUnknown_iface, 3); - IDWriteFontFile_Release(file); + ref = IDWriteFontFile_Release(file); + ok(ref == 0, "File object not released, %u.\n", ref); EXPECT_REF(&ownerobject.IUnknown_iface, 3); + count = IDWriteInMemoryFontFileLoader_GetFileCount(inmemory); + ok(count == 3, "Unexpected file count %u.\n", count); + EXPECT_REF(&ownerobject.IUnknown_iface, 3); - IDWriteFontFile_Release(file2); + ref = IDWriteFontFile_Release(file2); + ok(ref == 0, "File object not released, %u.\n", ref); EXPECT_REF(&ownerobject.IUnknown_iface, 3); + count = IDWriteInMemoryFontFileLoader_GetFileCount(inmemory); + ok(count == 3, "Unexpected file count %u.\n", count); + hr = IDWriteFactory5_UnregisterFontFileLoader(factory5, (IDWriteFontFileLoader *)inmemory); ok(hr == S_OK, "got %#x\n", hr); EXPECT_REF(&ownerobject.IUnknown_iface, 3); - IDWriteFontFileStream_ReleaseFileFragment(stream, context); - IDWriteFontFileStream_Release(stream); - IDWriteFontFace_Release(fontface); - EXPECT_REF(&ownerobject.IUnknown_iface, 3); ref = IDWriteInMemoryFontFileLoader_Release(inmemory); ok(ref == 0, "loader not released, %u.\n", ref); EXPECT_REF(&ownerobject.IUnknown_iface, 1); + /* Test reference key for first added file. */ + hr = IDWriteFactory5_CreateInMemoryFontFileLoader(factory5, &loader); + ok(hr == S_OK, "Failed to create loader, hr %#x.\n", hr); + + hr = IDWriteFontFileLoader_QueryInterface(loader, &IID_IDWriteInMemoryFontFileLoader, (void **)&inmemory); + ok(hr == S_OK, "Failed to get in-memory interface, hr %#x.\n", hr); + IDWriteFontFileLoader_Release(loader); + + hr = IDWriteFactory5_RegisterFontFileLoader(factory5, (IDWriteFontFileLoader *)inmemory); + ok(hr == S_OK, "Failed to register loader, hr %#x.\n", hr); + + ref_key = 0; + hr = IDWriteInMemoryFontFileLoader_CreateStreamFromKey(inmemory, &ref_key, sizeof(ref_key), &stream2); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + hr = IDWriteInMemoryFontFileLoader_CreateInMemoryFontFileReference(inmemory, (IDWriteFactory *)factory5, data, + file_size, &ownerobject.IUnknown_iface, &file); + ok(hr == S_OK, "Failed to create in-memory file reference, hr %#x.\n", hr); + + ref_key = 0; + hr = IDWriteInMemoryFontFileLoader_CreateStreamFromKey(inmemory, &ref_key, sizeof(ref_key), &stream2); + ok(hr == S_OK, "Failed to create a stream, hr %#x.\n", hr); + + context2 = (void *)0xdeadbeef; + hr = IDWriteFontFileStream_ReadFileFragment(stream2, &frag_start, 0, file_size, &context2); + ok(hr == S_OK, "Failed to read a fragment, hr %#x.\n", hr); + ok(context == NULL, "Unexpected context %p.\n", context2); + ok(frag_start == data, "Unexpected fragment pointer %p.\n", frag_start); + + hr = IDWriteFontFileStream_GetFileSize(stream2, &size); + ok(hr == S_OK, "Failed to get file size, hr %#x.\n", hr); + ok(size == file_size, "Unexpected file size.\n"); + + IDWriteFontFileStream_ReleaseFileFragment(stream2, context2); + + writetime = 1; + hr = IDWriteFontFileStream_GetLastWriteTime(stream2, &writetime); + ok(hr == E_NOTIMPL, "Unexpected hr %#x.\n", hr); + ok(writetime == 0, "Unexpected writetime.\n"); + + IDWriteFontFileStream_Release(stream2); + + ref_key = 0; + hr = IDWriteInMemoryFontFileLoader_CreateStreamFromKey(inmemory, NULL, sizeof(ref_key) - 1, &stream2); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + ref_key = 0; + hr = IDWriteInMemoryFontFileLoader_CreateStreamFromKey(inmemory, &ref_key, sizeof(ref_key) - 1, &stream2); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + ref_key = 0; + hr = IDWriteInMemoryFontFileLoader_CreateStreamFromKey(inmemory, &ref_key, sizeof(ref_key) + 1, &stream2); + ok(hr == E_INVALIDARG, "Unexpected hr %#x.\n", hr); + + count = IDWriteInMemoryFontFileLoader_GetFileCount(inmemory); + ok(count == 1, "Unexpected file count %u.\n", count); + + hr = IDWriteFontFile_GetReferenceKey(file, &key, &key_size); + ok(hr == S_OK, "Failed to get reference key, hr %#x.\n", hr); + + ok(key && *(DWORD*)key == 0, "Unexpected reference key.\n"); + ok(key_size == 4, "Unexpected key size %u.\n", key_size); + + IDWriteFontFile_Release(file); + + count = IDWriteInMemoryFontFileLoader_GetFileCount(inmemory); + ok(count == 1, "Unexpected file count %u.\n", count); + + hr = IDWriteFactory5_UnregisterFontFileLoader(factory5, (IDWriteFontFileLoader *)inmemory); + ok(hr == S_OK, "Failed to unregister loader, hr %#x.\n", hr); + + IDWriteFontFileStream_ReleaseFileFragment(stream, context); + IDWriteFontFileStream_Release(stream); + IDWriteFontFace_Release(fontface); + ref = IDWriteFactory5_Release(factory5); ok(ref == 0, "factory not released, %u\n", ref); } -- 2.11.4.GIT