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