1 /*
2  * Copyright 2014 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 #include "src/utils/win/SkDWriteNTDDI_VERSION.h"
8 
9 #include "include/core/SkTypes.h"
10 #if defined(SK_BUILD_FOR_WIN)
11 
12 #include "include/core/SkStream.h"
13 #include "include/core/SkString.h"
14 #include "include/core/SkTypes.h"
15 #include "include/ports/SkRemotableFontMgr.h"
16 #include "include/private/SkMutex.h"
17 #include "include/private/SkTArray.h"
18 #include "src/ports/SkTypeface_win_dw.h"
19 #include "src/utils/SkUTF.h"
20 #include "src/utils/win/SkDWrite.h"
21 #include "src/utils/win/SkDWriteFontFileStream.h"
22 #include "src/utils/win/SkHRESULT.h"
23 #include "src/utils/win/SkObjBase.h"
24 #include "src/utils/win/SkTScopedComPtr.h"
25 
26 #include <dwrite.h>
27 
28 class SK_API SkRemotableFontMgr_DirectWrite : public SkRemotableFontMgr {
29 private:
30     struct DataId {
31         IUnknown* fLoader;  // In COM only IUnknown pointers may be safely used for identity.
32         void* fKey;
33         UINT32 fKeySize;
34 
DataIdSkRemotableFontMgr_DirectWrite::DataId35         DataId() { }
36 
DataIdSkRemotableFontMgr_DirectWrite::DataId37         DataId(DataId&& that) : fLoader(that.fLoader), fKey(that.fKey), fKeySize(that.fKeySize) {
38             that.fLoader = nullptr;
39             that.fKey = nullptr;
40             SkDEBUGCODE(that.fKeySize = 0xFFFFFFFF;)
41         }
42 
~DataIdSkRemotableFontMgr_DirectWrite::DataId43         ~DataId() {
44             if (fLoader) {
45                 fLoader->Release();
46             }
47             sk_free(fKey);
48         }
49     };
50 
51     mutable SkTArray<DataId> fDataIdCache;
52     mutable SkMutex fDataIdCacheMutex;
53 
FindOrAdd(IDWriteFontFileLoader * fontFileLoader,const void * refKey,UINT32 refKeySize) const54     int FindOrAdd(IDWriteFontFileLoader* fontFileLoader,
55                   const void* refKey, UINT32 refKeySize) const
56     {
57         SkTScopedComPtr<IUnknown> fontFileLoaderId;
58         HR_GENERAL(fontFileLoader->QueryInterface(&fontFileLoaderId),
59                    "Failed to re-convert to IDWriteFontFileLoader.",
60                    SkFontIdentity::kInvalidDataId);
61 
62         SkAutoMutexExclusive ama(fDataIdCacheMutex);
63         int count = fDataIdCache.count();
64         int i;
65         for (i = 0; i < count; ++i) {
66             const DataId& current = fDataIdCache[i];
67             if (fontFileLoaderId.get() == current.fLoader &&
68                 refKeySize == current.fKeySize &&
69                 0 == memcmp(refKey, current.fKey, refKeySize))
70             {
71                 return i;
72             }
73         }
74         DataId& added = fDataIdCache.push_back();
75         added.fLoader = fontFileLoaderId.release();  // Ref is passed.
76         added.fKey = sk_malloc_throw(refKeySize);
77         memcpy(added.fKey, refKey, refKeySize);
78         added.fKeySize = refKeySize;
79 
80         return i;
81     }
82 
83 public:
84 
85 
86     /** localeNameLength must include the null terminator. */
SkRemotableFontMgr_DirectWrite(IDWriteFontCollection * fontCollection,WCHAR * localeName,int localeNameLength)87     SkRemotableFontMgr_DirectWrite(IDWriteFontCollection* fontCollection,
88                                    WCHAR* localeName, int localeNameLength)
89         : fFontCollection(SkRefComPtr(fontCollection))
90         , fLocaleName(localeNameLength)
91     {
92         memcpy(fLocaleName.get(), localeName, localeNameLength * sizeof(WCHAR));
93     }
94 
FontToIdentity(IDWriteFont * font,SkFontIdentity * fontId) const95     HRESULT FontToIdentity(IDWriteFont* font, SkFontIdentity* fontId) const {
96         SkTScopedComPtr<IDWriteFontFace> fontFace;
97         HRM(font->CreateFontFace(&fontFace), "Could not create font face.");
98 
99         UINT32 numFiles;
100         HR(fontFace->GetFiles(&numFiles, nullptr));
101         if (numFiles > 1) {
102             return E_FAIL;
103         }
104 
105         // data id
106         SkTScopedComPtr<IDWriteFontFile> fontFile;
107         HR(fontFace->GetFiles(&numFiles, &fontFile));
108 
109         SkTScopedComPtr<IDWriteFontFileLoader> fontFileLoader;
110         HR(fontFile->GetLoader(&fontFileLoader));
111 
112         const void* refKey;
113         UINT32 refKeySize;
114         HR(fontFile->GetReferenceKey(&refKey, &refKeySize));
115 
116         fontId->fDataId = FindOrAdd(fontFileLoader.get(), refKey, refKeySize);
117 
118         // index
119         fontId->fTtcIndex = fontFace->GetIndex();
120 
121         // style
122         fontId->fFontStyle = get_style(font);
123         return S_OK;
124     }
125 
getIndex(int familyIndex) const126     SkRemotableFontIdentitySet* getIndex(int familyIndex) const override {
127         SkTScopedComPtr<IDWriteFontFamily> fontFamily;
128         HRNM(fFontCollection->GetFontFamily(familyIndex, &fontFamily),
129              "Could not get requested family.");
130 
131         int count = fontFamily->GetFontCount();
132         SkFontIdentity* fontIds;
133         sk_sp<SkRemotableFontIdentitySet> fontIdSet(
134             new SkRemotableFontIdentitySet(count, &fontIds));
135         for (int fontIndex = 0; fontIndex < count; ++fontIndex) {
136             SkTScopedComPtr<IDWriteFont> font;
137             HRNM(fontFamily->GetFont(fontIndex, &font), "Could not get font.");
138 
139             HRN(FontToIdentity(font.get(), &fontIds[fontIndex]));
140         }
141         return fontIdSet.release();
142     }
143 
matchIndexStyle(int familyIndex,const SkFontStyle & pattern) const144     virtual SkFontIdentity matchIndexStyle(int familyIndex,
145                                            const SkFontStyle& pattern) const override
146     {
147         SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
148 
149         SkTScopedComPtr<IDWriteFontFamily> fontFamily;
150         HR_GENERAL(fFontCollection->GetFontFamily(familyIndex, &fontFamily),
151                    "Could not get requested family.",
152                    identity);
153 
154         const DWriteStyle dwStyle(pattern);
155         SkTScopedComPtr<IDWriteFont> font;
156         HR_GENERAL(fontFamily->GetFirstMatchingFont(dwStyle.fWeight, dwStyle.fWidth,
157                                                     dwStyle.fSlant, &font),
158                    "Could not match font in family.",
159                    identity);
160 
161         HR_GENERAL(FontToIdentity(font.get(), &identity), nullptr, identity);
162 
163         return identity;
164     }
165 
getDefaultFontFamilyName(SkSMallocWCHAR * name)166     static HRESULT getDefaultFontFamilyName(SkSMallocWCHAR* name) {
167         NONCLIENTMETRICSW metrics;
168         metrics.cbSize = sizeof(metrics);
169         if (0 == SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
170                                        sizeof(metrics),
171                                        &metrics,
172                                        0)) {
173             return E_UNEXPECTED;
174         }
175 
176         size_t len = wcsnlen_s(metrics.lfMessageFont.lfFaceName, LF_FACESIZE) + 1;
177         if (0 != wcsncpy_s(name->reset(len), len, metrics.lfMessageFont.lfFaceName, _TRUNCATE)) {
178             return E_UNEXPECTED;
179         }
180 
181         return S_OK;
182     }
183 
matchName(const char familyName[]) const184     SkRemotableFontIdentitySet* matchName(const char familyName[]) const override {
185         SkSMallocWCHAR dwFamilyName;
186         if (nullptr == familyName) {
187             HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName),
188                        nullptr, SkRemotableFontIdentitySet::NewEmpty());
189         } else {
190             HR_GENERAL(sk_cstring_to_wchar(familyName, &dwFamilyName),
191                        nullptr, SkRemotableFontIdentitySet::NewEmpty());
192         }
193 
194         UINT32 index;
195         BOOL exists;
196         HR_GENERAL(fFontCollection->FindFamilyName(dwFamilyName.get(), &index, &exists),
197                    "Failed while finding family by name.",
198                    SkRemotableFontIdentitySet::NewEmpty());
199         if (!exists) {
200             return SkRemotableFontIdentitySet::NewEmpty();
201         }
202 
203         return this->getIndex(index);
204     }
205 
matchNameStyle(const char familyName[],const SkFontStyle & style) const206     virtual SkFontIdentity matchNameStyle(const char familyName[],
207                                           const SkFontStyle& style) const override
208     {
209         SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
210 
211         SkSMallocWCHAR dwFamilyName;
212         if (nullptr == familyName) {
213             HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName), nullptr, identity);
214         } else {
215             HR_GENERAL(sk_cstring_to_wchar(familyName, &dwFamilyName), nullptr, identity);
216         }
217 
218         UINT32 index;
219         BOOL exists;
220         HR_GENERAL(fFontCollection->FindFamilyName(dwFamilyName.get(), &index, &exists),
221                    "Failed while finding family by name.",
222                    identity);
223         if (!exists) {
224             return identity;
225         }
226 
227         return this->matchIndexStyle(index, style);
228     }
229 
230     class FontFallbackRenderer : public IDWriteTextRenderer {
231     public:
FontFallbackRenderer(const SkRemotableFontMgr_DirectWrite * outer,UINT32 character)232         FontFallbackRenderer(const SkRemotableFontMgr_DirectWrite* outer, UINT32 character)
233             : fRefCount(1), fOuter(SkSafeRef(outer)), fCharacter(character) {
234           fIdentity.fDataId = SkFontIdentity::kInvalidDataId;
235         }
236 
~FontFallbackRenderer()237         virtual ~FontFallbackRenderer() { }
238 
239         // IDWriteTextRenderer methods
DrawGlyphRun(void * clientDrawingContext,FLOAT baselineOriginX,FLOAT baselineOriginY,DWRITE_MEASURING_MODE measuringMode,DWRITE_GLYPH_RUN const * glyphRun,DWRITE_GLYPH_RUN_DESCRIPTION const * glyphRunDescription,IUnknown * clientDrawingEffect)240         SK_STDMETHODIMP DrawGlyphRun(
241             void* clientDrawingContext,
242             FLOAT baselineOriginX,
243             FLOAT baselineOriginY,
244             DWRITE_MEASURING_MODE measuringMode,
245             DWRITE_GLYPH_RUN const* glyphRun,
246             DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
247             IUnknown* clientDrawingEffect) override
248         {
249             SkTScopedComPtr<IDWriteFont> font;
250             HRM(fOuter->fFontCollection->GetFontFromFontFace(glyphRun->fontFace, &font),
251                 "Could not get font from font face.");
252 
253             // It is possible that the font passed does not actually have the requested character,
254             // due to no font being found and getting the fallback font.
255             // Check that the font actually contains the requested character.
256             BOOL exists;
257             HRM(font->HasCharacter(fCharacter, &exists), "Could not find character.");
258 
259             if (exists) {
260                 HR(fOuter->FontToIdentity(font.get(), &fIdentity));
261             }
262 
263             return S_OK;
264         }
265 
DrawUnderline(void * clientDrawingContext,FLOAT baselineOriginX,FLOAT baselineOriginY,DWRITE_UNDERLINE const * underline,IUnknown * clientDrawingEffect)266         SK_STDMETHODIMP DrawUnderline(
267             void* clientDrawingContext,
268             FLOAT baselineOriginX,
269             FLOAT baselineOriginY,
270             DWRITE_UNDERLINE const* underline,
271             IUnknown* clientDrawingEffect) override
272         { return E_NOTIMPL; }
273 
DrawStrikethrough(void * clientDrawingContext,FLOAT baselineOriginX,FLOAT baselineOriginY,DWRITE_STRIKETHROUGH const * strikethrough,IUnknown * clientDrawingEffect)274         SK_STDMETHODIMP DrawStrikethrough(
275             void* clientDrawingContext,
276             FLOAT baselineOriginX,
277             FLOAT baselineOriginY,
278             DWRITE_STRIKETHROUGH const* strikethrough,
279             IUnknown* clientDrawingEffect) override
280         { return E_NOTIMPL; }
281 
DrawInlineObject(void * clientDrawingContext,FLOAT originX,FLOAT originY,IDWriteInlineObject * inlineObject,BOOL isSideways,BOOL isRightToLeft,IUnknown * clientDrawingEffect)282         SK_STDMETHODIMP DrawInlineObject(
283             void* clientDrawingContext,
284             FLOAT originX,
285             FLOAT originY,
286             IDWriteInlineObject* inlineObject,
287             BOOL isSideways,
288             BOOL isRightToLeft,
289             IUnknown* clientDrawingEffect) override
290         { return E_NOTIMPL; }
291 
292         // IDWritePixelSnapping methods
IsPixelSnappingDisabled(void * clientDrawingContext,BOOL * isDisabled)293         SK_STDMETHODIMP IsPixelSnappingDisabled(
294             void* clientDrawingContext,
295             BOOL* isDisabled) override
296         {
297             *isDisabled = FALSE;
298             return S_OK;
299         }
300 
GetCurrentTransform(void * clientDrawingContext,DWRITE_MATRIX * transform)301         SK_STDMETHODIMP GetCurrentTransform(
302             void* clientDrawingContext,
303             DWRITE_MATRIX* transform) override
304         {
305             const DWRITE_MATRIX ident = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
306             *transform = ident;
307             return S_OK;
308         }
309 
GetPixelsPerDip(void * clientDrawingContext,FLOAT * pixelsPerDip)310         SK_STDMETHODIMP GetPixelsPerDip(
311             void* clientDrawingContext,
312             FLOAT* pixelsPerDip) override
313         {
314             *pixelsPerDip = 1.0f;
315             return S_OK;
316         }
317 
318         // IUnknown methods
AddRef()319         SK_STDMETHODIMP_(ULONG) AddRef() override {
320             return InterlockedIncrement(&fRefCount);
321         }
322 
Release()323         SK_STDMETHODIMP_(ULONG) Release() override {
324             ULONG newCount = InterlockedDecrement(&fRefCount);
325             if (0 == newCount) {
326                 delete this;
327             }
328             return newCount;
329         }
330 
QueryInterface(IID const & riid,void ** ppvObject)331         SK_STDMETHODIMP QueryInterface(
332             IID const& riid, void** ppvObject) override
333         {
334             if (__uuidof(IUnknown) == riid ||
335                 __uuidof(IDWritePixelSnapping) == riid ||
336                 __uuidof(IDWriteTextRenderer) == riid)
337             {
338                 *ppvObject = this;
339                 this->AddRef();
340                 return S_OK;
341             }
342             *ppvObject = nullptr;
343             return E_FAIL;
344         }
345 
FallbackIdentity()346         const SkFontIdentity FallbackIdentity() { return fIdentity; }
347 
348     protected:
349         ULONG fRefCount;
350         sk_sp<const SkRemotableFontMgr_DirectWrite> fOuter;
351         UINT32 fCharacter;
352         SkFontIdentity fIdentity;
353     };
354 
matchNameStyleCharacter(const char familyName[],const SkFontStyle & pattern,const char * bcp47[],int bcp47Count,SkUnichar character) const355     virtual SkFontIdentity matchNameStyleCharacter(const char familyName[],
356                                                    const SkFontStyle& pattern,
357                                                    const char* bcp47[], int bcp47Count,
358                                                    SkUnichar character) const override
359     {
360         SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
361 
362         IDWriteFactory* dwFactory = sk_get_dwrite_factory();
363         if (nullptr == dwFactory) {
364             return identity;
365         }
366 
367         // TODO: use IDWriteFactory2::GetSystemFontFallback when available.
368 
369         const DWriteStyle dwStyle(pattern);
370 
371         SkSMallocWCHAR dwFamilyName;
372         if (nullptr == familyName) {
373             HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName), nullptr, identity);
374         } else {
375             HR_GENERAL(sk_cstring_to_wchar(familyName, &dwFamilyName), nullptr, identity);
376         }
377 
378         const SkSMallocWCHAR* dwBcp47;
379         SkSMallocWCHAR dwBcp47Local;
380         if (bcp47Count < 1) {
381             dwBcp47 = &fLocaleName;
382         } else {
383             //TODO: support fallback stack.
384             HR_GENERAL(sk_cstring_to_wchar(bcp47[bcp47Count-1], &dwBcp47Local), nullptr, identity);
385             dwBcp47 = &dwBcp47Local;
386         }
387 
388         SkTScopedComPtr<IDWriteTextFormat> fallbackFormat;
389         HR_GENERAL(dwFactory->CreateTextFormat(dwFamilyName,
390                                                fFontCollection.get(),
391                                                dwStyle.fWeight,
392                                                dwStyle.fSlant,
393                                                dwStyle.fWidth,
394                                                72.0f,
395                                                *dwBcp47,
396                                                &fallbackFormat),
397                    "Could not create text format.",
398                    identity);
399 
400         WCHAR str[16];
401         UINT32 strLen = static_cast<UINT32>(
402             SkUTF::ToUTF16(character, reinterpret_cast<uint16_t*>(str)));
403         SkTScopedComPtr<IDWriteTextLayout> fallbackLayout;
404         HR_GENERAL(dwFactory->CreateTextLayout(str, strLen, fallbackFormat.get(),
405                                                200.0f, 200.0f,
406                                                &fallbackLayout),
407                    "Could not create text layout.",
408                    identity);
409 
410         SkTScopedComPtr<FontFallbackRenderer> fontFallbackRenderer(
411             new FontFallbackRenderer(this, character));
412 
413         HR_GENERAL(fallbackLayout->Draw(nullptr, fontFallbackRenderer.get(), 50.0f, 50.0f),
414                    "Could not draw layout with renderer.",
415                    identity);
416 
417         return fontFallbackRenderer->FallbackIdentity();
418     }
419 
getData(int dataId) const420     SkStreamAsset* getData(int dataId) const override {
421         SkAutoMutexExclusive ama(fDataIdCacheMutex);
422         if (dataId >= fDataIdCache.count()) {
423             return nullptr;
424         }
425         const DataId& id = fDataIdCache[dataId];
426 
427         SkTScopedComPtr<IDWriteFontFileLoader> loader;
428         HRNM(id.fLoader->QueryInterface(&loader), "QuerryInterface IDWriteFontFileLoader failed");
429 
430         SkTScopedComPtr<IDWriteFontFileStream> fontFileStream;
431         HRNM(loader->CreateStreamFromKey(id.fKey, id.fKeySize, &fontFileStream),
432              "Could not create font file stream.");
433 
434         return new SkDWriteFontFileStream(fontFileStream.get());
435     }
436 
437 private:
438     SkTScopedComPtr<IDWriteFontCollection> fFontCollection;
439     SkSMallocWCHAR fLocaleName;
440 
441     using INHERITED = SkRemotableFontMgr;
442 };
443 
SkRemotableFontMgr_New_DirectWrite()444 SkRemotableFontMgr* SkRemotableFontMgr_New_DirectWrite() {
445     IDWriteFactory* factory = sk_get_dwrite_factory();
446     if (nullptr == factory) {
447         return nullptr;
448     }
449 
450     SkTScopedComPtr<IDWriteFontCollection> sysFontCollection;
451     HRNM(factory->GetSystemFontCollection(&sysFontCollection, FALSE),
452          "Could not get system font collection.");
453 
454     WCHAR localeNameStorage[LOCALE_NAME_MAX_LENGTH];
455     WCHAR* localeName = nullptr;
456     int localeNameLen = 0;
457 
458     // Dynamically load GetUserDefaultLocaleName function, as it is not available on XP.
459     SkGetUserDefaultLocaleNameProc getUserDefaultLocaleNameProc = nullptr;
460     HRESULT hr = SkGetGetUserDefaultLocaleNameProc(&getUserDefaultLocaleNameProc);
461     if (nullptr == getUserDefaultLocaleNameProc) {
462         SK_TRACEHR(hr, "Could not get GetUserDefaultLocaleName.");
463     } else {
464         localeNameLen = getUserDefaultLocaleNameProc(localeNameStorage, LOCALE_NAME_MAX_LENGTH);
465         if (localeNameLen) {
466             localeName = localeNameStorage;
467         };
468     }
469 
470     return new SkRemotableFontMgr_DirectWrite(sysFontCollection.get(), localeName, localeNameLen);
471 }
472 #endif//defined(SK_BUILD_FOR_WIN)
473