[PATCH] dwrite: Add support for memory font resources in CreateFontFaceFromHdc().

Nikolay Sivov nsivov at codeweavers.com
Thu Nov 22 05:29:14 CST 2018


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 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 5501f377c4..2e4a949286 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 2ac81573de..86efb8cc05 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());
 
-- 
2.19.1




More information about the wine-devel mailing list