From a0c8f066d402f163c95ee38b940e2533371530a4 Mon Sep 17 00:00:00 2001 From: Nikolay Sivov Date: Thu, 22 Nov 2018 14:29:14 +0300 Subject: [PATCH] dwrite: Add support for memory font resources in CreateFontFaceFromHdc(). Signed-off-by: Nikolay Sivov Signed-off-by: Alexandre Julliard --- dlls/dwrite/gdiinterop.c | 200 ++++++++++++++++++++++++++++++++++++++- dlls/dwrite/tests/font.c | 71 ++++++++++++-- 2 files changed, 261 insertions(+), 10 deletions(-) diff --git a/dlls/dwrite/gdiinterop.c b/dlls/dwrite/gdiinterop.c index 5501f377c46..2e4a9492862 100644 --- a/dlls/dwrite/gdiinterop.c +++ b/dlls/dwrite/gdiinterop.c @@ -2,7 +2,7 @@ * GDI Interop * * Copyright 2011 Huw Davies - * Copyright 2012, 2014-2016 Nikolay Sivov for CodeWeavers + * Copyright 2012, 2014-2018 Nikolay Sivov for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -54,10 +54,17 @@ struct rendertarget { struct gdiinterop { IDWriteGdiInterop1 IDWriteGdiInterop1_iface; + IDWriteFontFileLoader IDWriteFontFileLoader_iface; LONG ref; IDWriteFactory5 *factory; }; +struct memresource_stream { + IDWriteFontFileStream IDWriteFontFileStream_iface; + LONG ref; + DWORD key; +}; + static inline int get_dib_stride(int width, int bpp) { return ((width * bpp + 31) >> 3) & ~3; @@ -111,6 +118,16 @@ static inline struct gdiinterop *impl_from_IDWriteGdiInterop1(IDWriteGdiInterop1 return CONTAINING_RECORD(iface, struct gdiinterop, IDWriteGdiInterop1_iface); } +static inline struct gdiinterop *impl_from_IDWriteFontFileLoader(IDWriteFontFileLoader *iface) +{ + return CONTAINING_RECORD(iface, struct gdiinterop, IDWriteFontFileLoader_iface); +} + +static inline struct memresource_stream *impl_from_IDWriteFontFileStream(IDWriteFontFileStream *iface) +{ + return CONTAINING_RECORD(iface, struct memresource_stream, IDWriteFontFileStream_iface); +} + static HRESULT WINAPI rendertarget_sink_QueryInterface(ID2D1SimplifiedGeometrySink *iface, REFIID riid, void **obj) { if (IsEqualIID(riid, &IID_ID2D1SimplifiedGeometrySink) || @@ -637,6 +654,7 @@ static ULONG WINAPI gdiinterop_Release(IDWriteGdiInterop1 *iface) TRACE("(%p)->(%d)\n", This, ref); if (!ref) { + IDWriteFactory5_UnregisterFontFileLoader(This->factory, &This->IDWriteFontFileLoader_iface); factory_detach_gdiinterop(This->factory, iface); heap_free(This); } @@ -728,6 +746,7 @@ struct font_fileinfo { /* Undocumented gdi32 exports, used to access actually selected font information */ extern BOOL WINAPI GetFontRealizationInfo(HDC hdc, struct font_realization_info *info); extern BOOL WINAPI GetFontFileInfo(DWORD instance_id, DWORD unknown, struct font_fileinfo *info, SIZE_T size, SIZE_T *needed); +extern BOOL WINAPI GetFontFileData(DWORD instance_id, DWORD unknown, UINT64 offset, void *buff, DWORD buff_size); static HRESULT WINAPI gdiinterop_CreateFontFaceFromHdc(IDWriteGdiInterop1 *iface, HDC hdc, IDWriteFontFace **fontface) @@ -773,8 +792,12 @@ static HRESULT WINAPI gdiinterop_CreateFontFaceFromHdc(IDWriteGdiInterop1 *iface return E_FAIL; } - hr = IDWriteFactory5_CreateFontFileReference(This->factory, fileinfo->path, &fileinfo->writetime, - &file); + if (*fileinfo->path) + hr = IDWriteFactory5_CreateFontFileReference(This->factory, fileinfo->path, &fileinfo->writetime, &file); + else + hr = IDWriteFactory5_CreateCustomFontFileReference(This->factory, &info.instance_id, sizeof(info.instance_id), + &This->IDWriteFontFileLoader_iface, &file); + heap_free(fileinfo); if (FAILED(hr)) return hr; @@ -897,6 +920,175 @@ static const struct IDWriteGdiInterop1Vtbl gdiinteropvtbl = { gdiinterop1_GetMatchingFontsByLOGFONT }; +static HRESULT WINAPI memresourcestream_QueryInterface(IDWriteFontFileStream *iface, REFIID riid, void **out) +{ + struct memresource_stream *This = impl_from_IDWriteFontFileStream(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), out); + + if (IsEqualIID(&IID_IDWriteFontFileStream, riid) || IsEqualIID(&IID_IUnknown, riid)) { + *out = iface; + IDWriteFontFileStream_AddRef(iface); + return S_OK; + } + + *out = NULL; + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI memresourcestream_AddRef(IDWriteFontFileStream *iface) +{ + struct memresource_stream *This = impl_from_IDWriteFontFileStream(iface); + ULONG ref = InterlockedIncrement(&This->ref); + TRACE("(%p)->(%d)\n", This, ref); + return ref; +} + +static ULONG WINAPI memresourcestream_Release(IDWriteFontFileStream *iface) +{ + struct memresource_stream *This = impl_from_IDWriteFontFileStream(iface); + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p)->(%d)\n", This, ref); + + if (!ref) + heap_free(This); + + return ref; +} + +static HRESULT WINAPI memresourcestream_ReadFileFragment(IDWriteFontFileStream *iface, void const **fragment_start, + UINT64 offset, UINT64 fragment_size, void **fragment_context) +{ + struct memresource_stream *This = impl_from_IDWriteFontFileStream(iface); + struct font_fileinfo fileinfo; + void *fragment; + + TRACE("(%p)->(%p %s %s %p)\n", This, fragment_start, wine_dbgstr_longlong(offset), + wine_dbgstr_longlong(fragment_size), fragment_context); + + *fragment_context = NULL; + *fragment_start = NULL; + + if (!GetFontFileInfo(This->key, 0, &fileinfo, sizeof(fileinfo), NULL)) + return E_INVALIDARG; + + if ((offset >= fileinfo.size.QuadPart - 1) || (fragment_size > fileinfo.size.QuadPart - offset)) + return E_INVALIDARG; + + if (!(fragment = heap_alloc(fragment_size))) + return E_OUTOFMEMORY; + + if (!GetFontFileData(This->key, 0, offset, fragment, fragment_size)) + return E_FAIL; + + *fragment_start = *fragment_context = fragment; + return S_OK; +} + +static void WINAPI memresourcestream_ReleaseFileFragment(IDWriteFontFileStream *iface, void *fragment_context) +{ + struct memresource_stream *This = impl_from_IDWriteFontFileStream(iface); + + TRACE("(%p)->(%p)\n", This, fragment_context); + + heap_free(fragment_context); +} + +static HRESULT WINAPI memresourcestream_GetFileSize(IDWriteFontFileStream *iface, UINT64 *size) +{ + struct memresource_stream *This = impl_from_IDWriteFontFileStream(iface); + struct font_fileinfo fileinfo; + + TRACE("(%p)->(%p)\n", This, size); + + if (!GetFontFileInfo(This->key, 0, &fileinfo, sizeof(fileinfo), NULL)) + return E_INVALIDARG; + + *size = fileinfo.size.QuadPart; + + return S_OK; +} + +static HRESULT WINAPI memresourcestream_GetLastWriteTime(IDWriteFontFileStream *iface, UINT64 *last_writetime) +{ + struct memresource_stream *This = impl_from_IDWriteFontFileStream(iface); + + TRACE("(%p)->(%p)\n", This, last_writetime); + + return E_NOTIMPL; +} + +static const struct IDWriteFontFileStreamVtbl memresourcestreamvtbl = { + memresourcestream_QueryInterface, + memresourcestream_AddRef, + memresourcestream_Release, + memresourcestream_ReadFileFragment, + memresourcestream_ReleaseFileFragment, + memresourcestream_GetFileSize, + memresourcestream_GetLastWriteTime, +}; + +static HRESULT WINAPI memresourceloader_QueryInterface(IDWriteFontFileLoader *iface, REFIID riid, void **out) +{ + struct gdiinterop *This = impl_from_IDWriteFontFileLoader(iface); + + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), out); + + if (IsEqualIID(&IID_IDWriteFontFileLoader, riid) || IsEqualIID(&IID_IUnknown, riid)) { + *out = iface; + IDWriteFontFileLoader_AddRef(iface); + return S_OK; + } + + *out = NULL; + WARN("Unsupported interface %s.\n", debugstr_guid(riid)); + return E_NOINTERFACE; +} + +static ULONG WINAPI memresourceloader_AddRef(IDWriteFontFileLoader *iface) +{ + return 2; +} + +static ULONG WINAPI memresourceloader_Release(IDWriteFontFileLoader *iface) +{ + return 1; +} + +static HRESULT WINAPI memresourceloader_CreateStreamFromKey(IDWriteFontFileLoader *iface, void const *key, + UINT32 key_size, IDWriteFontFileStream **ret) +{ + struct gdiinterop *This = impl_from_IDWriteFontFileLoader(iface); + struct memresource_stream *stream; + + TRACE("(%p)->(%p %u %p)\n", This, key, key_size, ret); + + *ret = NULL; + + if (!key || key_size != sizeof(DWORD)) + return E_INVALIDARG; + + if (!(stream = heap_alloc(sizeof(*stream)))) + return E_OUTOFMEMORY; + + stream->IDWriteFontFileStream_iface.lpVtbl = &memresourcestreamvtbl; + stream->ref = 1; + memcpy(&stream->key, key, sizeof(stream->key)); + + *ret = &stream->IDWriteFontFileStream_iface; + + return S_OK; +} + +static const struct IDWriteFontFileLoaderVtbl memresourceloadervtbl = { + memresourceloader_QueryInterface, + memresourceloader_AddRef, + memresourceloader_Release, + memresourceloader_CreateStreamFromKey, +}; + HRESULT create_gdiinterop(IDWriteFactory5 *factory, IDWriteGdiInterop1 **ret) { struct gdiinterop *interop; @@ -907,8 +1099,10 @@ HRESULT create_gdiinterop(IDWriteFactory5 *factory, IDWriteGdiInterop1 **ret) return E_OUTOFMEMORY; interop->IDWriteGdiInterop1_iface.lpVtbl = &gdiinteropvtbl; + interop->IDWriteFontFileLoader_iface.lpVtbl = &memresourceloadervtbl; interop->ref = 1; IDWriteFactory5_AddRef(interop->factory = factory); + IDWriteFactory5_RegisterFontFileLoader(factory, &interop->IDWriteFontFileLoader_iface); *ret = &interop->IDWriteGdiInterop1_iface; return S_OK; diff --git a/dlls/dwrite/tests/font.c b/dlls/dwrite/tests/font.c index 2ac81573de4..86efb8cc05e 100644 --- a/dlls/dwrite/tests/font.c +++ b/dlls/dwrite/tests/font.c @@ -1,7 +1,7 @@ /* * Font related tests * - * Copyright 2012, 2014-2017 Nikolay Sivov for CodeWeavers + * Copyright 2012, 2014-2018 Nikolay Sivov for CodeWeavers * Copyright 2014 Aric Stewart for CodeWeavers * * This library is free software; you can redistribute it and/or @@ -122,6 +122,8 @@ static void _expect_ref_broken(IUnknown* obj, ULONG ref, ULONG brokenref, int li ok_(__FILE__,line)(rc == ref || broken(rc == brokenref), "expected refcount %d, got %d\n", ref, rc); } +static BOOL (WINAPI *pGetFontRealizationInfo)(HDC hdc, void *); + static const WCHAR test_fontfile[] = {'w','i','n','e','_','t','e','s','t','_','f','o','n','t','.','t','t','f',0}; static const WCHAR tahomaW[] = {'T','a','h','o','m','a',0}; static const WCHAR arialW[] = {'A','r','i','a','l',0}; @@ -3735,10 +3737,22 @@ static void *map_font_file(const WCHAR *filename, DWORD *file_size) return ptr; } +struct font_realization_info +{ + DWORD size; + DWORD flags; + DWORD cache_num; + DWORD instance_id; + DWORD unk; + WORD face_index; + WORD simulations; +}; + static void test_CreateFontFaceFromHdc(void) { IDWriteFontFileStream *stream, *stream2; void *font_data, *fragment_context; + struct font_realization_info info; const void *refkey, *fragment; IDWriteFontFileLoader *loader; DWORD data_size, num_fonts; @@ -3748,10 +3762,10 @@ static void test_CreateFontFaceFromHdc(void) UINT64 size, writetime; IDWriteFontFile *file; HFONT hfont, oldhfont; + UINT32 count, dummy; LOGFONTW logfont; HANDLE resource; IUnknown *unk; - UINT32 count; LOGFONTA lf; WCHAR *path; HRESULT hr; @@ -3761,6 +3775,8 @@ static void test_CreateFontFaceFromHdc(void) factory = create_factory(); + pGetFontRealizationInfo = (void *)GetProcAddress(GetModuleHandleA("gdi32"), "GetFontRealizationInfo"); + interop = NULL; hr = IDWriteFactory_GetGdiInterop(factory, &interop); ok(hr == S_OK, "got 0x%08x\n", hr); @@ -3789,7 +3805,23 @@ static void test_CreateFontFaceFromHdc(void) fontface = NULL; hr = IDWriteGdiInterop_CreateFontFaceFromHdc(interop, hdc, &fontface); - ok(hr == S_OK, "got 0x%08x\n", hr); + ok(hr == S_OK, "Failed to create font face, hr %#x.\n", hr); + + count = 1; + hr = IDWriteFontFace_GetFiles(fontface, &count, &file); + ok(hr == S_OK, "Failed to get font files, hr %#x.\n", hr); + + hr = IDWriteFontFile_GetLoader(file, &loader); + ok(hr == S_OK, "Failed to get file loader, hr %#x.\n", hr); + + hr = IDWriteFontFileLoader_QueryInterface(loader, &IID_IDWriteLocalFontFileLoader, (void **)&unk); + ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* Vista */, "Expected local loader, hr %#x.\n", hr); + if (unk) + IUnknown_Release(unk); + + IDWriteFontFileLoader_Release(loader); + IDWriteFontFile_Release(file); + IDWriteFontFace_Release(fontface); DeleteObject(SelectObject(hdc, oldhfont)); @@ -3828,11 +3860,8 @@ static void test_CreateFontFaceFromHdc(void) oldhfont = SelectObject(hdc, hfont); hr = IDWriteGdiInterop_CreateFontFaceFromHdc(interop, hdc, &fontface); -todo_wine ok(hr == S_OK, "Failed to create fontface, hr %#x.\n", hr); -if (fontface) -{ count = 1; hr = IDWriteFontFace_GetFiles(fontface, &count, &file); ok(hr == S_OK, "Failed to get font files, hr %#x.\n", hr); @@ -3854,6 +3883,17 @@ if (fontface) ok(hr == S_OK, "Failed to get ref key, hr %#x.\n", hr); ok(count > 0, "Unexpected key length %u.\n", count); + if (pGetFontRealizationInfo) + { + info.size = sizeof(info); + ret = pGetFontRealizationInfo(hdc, &info); + ok(ret, "Failed to get realization info.\n"); + ok(count == sizeof(info.instance_id), "Unexpected key size.\n"); + ok(*(DWORD *)refkey == info.instance_id, "Unexpected stream key.\n"); + } + else + win_skip("GetFontRealizationInfo() is not available.\n"); + hr = IDWriteFontFileLoader_CreateStreamFromKey(loader, refkey, count, &stream); ok(hr == S_OK, "Failed to create file stream, hr %#x.\n", hr); @@ -3862,6 +3902,17 @@ if (fontface) ok(stream2 != stream, "Unexpected stream instance.\n"); IDWriteFontFileStream_Release(stream2); + dummy = 1; + hr = IDWriteFontFileLoader_CreateStreamFromKey(loader, &dummy, count, &stream2); + ok(hr == S_OK, "Failed to create file stream, hr %#x.\n", hr); + + writetime = 1; + hr = IDWriteFontFileStream_GetLastWriteTime(stream2, &writetime); + ok(hr == E_NOTIMPL, "Unexpected hr %#x.\n", hr); + ok(writetime == 1, "Unexpected write time.\n"); + + IDWriteFontFileStream_Release(stream2); + hr = IDWriteFontFileStream_GetFileSize(stream, &size); ok(hr == S_OK, "Failed to get stream size, hr %#x.\n", hr); ok(size == data_size, "Unexpected stream size.\n"); @@ -3876,13 +3927,19 @@ if (fontface) ok(fragment == fragment_context, "Unexpected data pointer %p, context %p.\n", fragment, fragment_context); IDWriteFontFileStream_ReleaseFileFragment(stream, fragment_context); + hr = IDWriteFontFileStream_ReadFileFragment(stream, &fragment, 0, size + 1, &fragment_context); + ok(FAILED(hr), "Unexpected hr %#x.\n", hr); + + hr = IDWriteFontFileStream_ReadFileFragment(stream, &fragment, size - 1, size / 2, &fragment_context); + ok(FAILED(hr), "Unexpected hr %#x.\n", hr); + IDWriteFontFileStream_Release(stream); IDWriteFontFileLoader_Release(loader); IDWriteFontFile_Release(file); IDWriteFontFace_Release(fontface); -} + ret = RemoveFontMemResourceEx(resource); ok(ret, "Failed to remove memory resource font, %d.\n", GetLastError());