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 
8 #include "SkDataTable.h"
9 #include "SkDWrite.h"
10 #include "SkDWriteFontFileStream.h"
11 #include "SkHRESULT.h"
12 #include "SkRemotableFontMgr.h"
13 #include "SkStream.h"
14 #include "SkString.h"
15 #include "SkTArray.h"
16 #include "SkThread.h"
17 #include "SkTScopedComPtr.h"
18 #include "SkTypeface_win.h"
19 #include "SkTypes.h"
20 #include "SkUtils.h"
21 
22 #include <dwrite.h>
23 
24 class SK_API SkRemotableFontMgr_DirectWrite : public SkRemotableFontMgr {
25 private:
26     struct DataId {
27         IUnknown* fLoader;  // In COM only IUnknown pointers may be safely used for identity.
28         void* fKey;
29         UINT32 fKeySize;
30 
DataIdSkRemotableFontMgr_DirectWrite::DataId31         DataId() { }
32 
33         // This is actually a move!!!
DataIdSkRemotableFontMgr_DirectWrite::DataId34         explicit DataId(DataId& that)
35             : fLoader(that.fLoader), fKey(that.fKey), fKeySize(that.fKeySize)
36         {
37             that.fLoader = NULL;
38             that.fKey = NULL;
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     SK_DECLARE_INST_COUNT(SkRemotableFontMgr_DirectWrite)
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 
getFamilyNames() const94     SkDataTable* getFamilyNames() const override {
95         int count = fFontCollection->GetFontFamilyCount();
96 
97         SkDataTableBuilder names(1024);
98         for (int index = 0; index < count; ++index) {
99             SkTScopedComPtr<IDWriteFontFamily> fontFamily;
100             HRNM(fFontCollection->GetFontFamily(index, &fontFamily),
101                  "Could not get requested family.");
102 
103             SkTScopedComPtr<IDWriteLocalizedStrings> familyNames;
104             HRNM(fontFamily->GetFamilyNames(&familyNames), "Could not get family names.");
105 
106             SkString familyName;
107             sk_get_locale_string(familyNames.get(), fLocaleName.get(), &familyName);
108 
109             names.appendString(familyName);
110         }
111         return names.detachDataTable();
112     }
113 
FontToIdentity(IDWriteFont * font,SkFontIdentity * fontId) const114     HRESULT FontToIdentity(IDWriteFont* font, SkFontIdentity* fontId) const {
115         SkTScopedComPtr<IDWriteFontFace> fontFace;
116         HRM(font->CreateFontFace(&fontFace), "Could not create font face.");
117 
118         UINT32 numFiles;
119         HR(fontFace->GetFiles(&numFiles, NULL));
120         if (numFiles > 1) {
121             return E_FAIL;
122         }
123 
124         // data id
125         SkTScopedComPtr<IDWriteFontFile> fontFile;
126         HR(fontFace->GetFiles(&numFiles, &fontFile));
127 
128         SkTScopedComPtr<IDWriteFontFileLoader> fontFileLoader;
129         HR(fontFile->GetLoader(&fontFileLoader));
130 
131         const void* refKey;
132         UINT32 refKeySize;
133         HR(fontFile->GetReferenceKey(&refKey, &refKeySize));
134 
135         fontId->fDataId = FindOrAdd(fontFileLoader.get(), refKey, refKeySize);
136 
137         // index
138         fontId->fTtcIndex = fontFace->GetIndex();
139 
140         // style
141         SkFontStyle::Slant slant;
142         switch (font->GetStyle()) {
143         case DWRITE_FONT_STYLE_NORMAL:
144             slant = SkFontStyle::kUpright_Slant;
145             break;
146         case DWRITE_FONT_STYLE_OBLIQUE:
147         case DWRITE_FONT_STYLE_ITALIC:
148             slant = SkFontStyle::kItalic_Slant;
149             break;
150         default:
151             SkASSERT(false);
152         }
153 
154         int weight = font->GetWeight();
155         int width = font->GetStretch();
156 
157         fontId->fFontStyle = SkFontStyle(weight, width, slant);
158         return S_OK;
159     }
160 
getIndex(int familyIndex) const161     SkRemotableFontIdentitySet* getIndex(int familyIndex) const override {
162         SkTScopedComPtr<IDWriteFontFamily> fontFamily;
163         HRNM(fFontCollection->GetFontFamily(familyIndex, &fontFamily),
164              "Could not get requested family.");
165 
166         int count = fontFamily->GetFontCount();
167         SkFontIdentity* fontIds;
168         SkAutoTUnref<SkRemotableFontIdentitySet> fontIdSet(
169             new SkRemotableFontIdentitySet(count, &fontIds));
170         for (int fontIndex = 0; fontIndex < count; ++fontIndex) {
171             SkTScopedComPtr<IDWriteFont> font;
172             HRNM(fontFamily->GetFont(fontIndex, &font), "Could not get font.");
173 
174             HRN(FontToIdentity(font.get(), &fontIds[fontIndex]));
175         }
176         return fontIdSet.detach();
177     }
178 
matchIndexStyle(int familyIndex,const SkFontStyle & pattern) const179     virtual SkFontIdentity matchIndexStyle(int familyIndex,
180                                            const SkFontStyle& pattern) const override
181     {
182         SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
183 
184         SkTScopedComPtr<IDWriteFontFamily> fontFamily;
185         HR_GENERAL(fFontCollection->GetFontFamily(familyIndex, &fontFamily),
186                    "Could not get requested family.",
187                    identity);
188 
189         const DWriteStyle dwStyle(pattern);
190         SkTScopedComPtr<IDWriteFont> font;
191         HR_GENERAL(fontFamily->GetFirstMatchingFont(dwStyle.fWeight, dwStyle.fWidth,
192                                                     dwStyle.fSlant, &font),
193                    "Could not match font in family.",
194                    identity);
195 
196         HR_GENERAL(FontToIdentity(font.get(), &identity), NULL, identity);
197 
198         return identity;
199     }
200 
getDefaultFontFamilyName(SkSMallocWCHAR * name)201     static HRESULT getDefaultFontFamilyName(SkSMallocWCHAR* name) {
202         NONCLIENTMETRICSW metrics;
203         metrics.cbSize = sizeof(metrics);
204         if (0 == SystemParametersInfoW(SPI_GETNONCLIENTMETRICS,
205                                        sizeof(metrics),
206                                        &metrics,
207                                        0)) {
208             return E_UNEXPECTED;
209         }
210 
211         size_t len = wcsnlen_s(metrics.lfMessageFont.lfFaceName, LF_FACESIZE) + 1;
212         if (0 != wcsncpy_s(name->reset(len), len, metrics.lfMessageFont.lfFaceName, _TRUNCATE)) {
213             return E_UNEXPECTED;
214         }
215 
216         return S_OK;
217     }
218 
matchName(const char familyName[]) const219     SkRemotableFontIdentitySet* matchName(const char familyName[]) const override {
220         SkSMallocWCHAR dwFamilyName;
221         if (NULL == familyName) {
222             HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName),
223                        NULL, SkRemotableFontIdentitySet::NewEmpty());
224         } else {
225             HR_GENERAL(sk_cstring_to_wchar(familyName, &dwFamilyName),
226                        NULL, SkRemotableFontIdentitySet::NewEmpty());
227         }
228 
229         UINT32 index;
230         BOOL exists;
231         HR_GENERAL(fFontCollection->FindFamilyName(dwFamilyName.get(), &index, &exists),
232                    "Failed while finding family by name.",
233                    SkRemotableFontIdentitySet::NewEmpty());
234         if (!exists) {
235             return SkRemotableFontIdentitySet::NewEmpty();
236         }
237 
238         return this->getIndex(index);
239     }
240 
matchNameStyle(const char familyName[],const SkFontStyle & style) const241     virtual SkFontIdentity matchNameStyle(const char familyName[],
242                                           const SkFontStyle& style) const override
243     {
244         SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
245 
246         SkSMallocWCHAR dwFamilyName;
247         if (NULL == familyName) {
248             HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName), NULL, identity);
249         } else {
250             HR_GENERAL(sk_cstring_to_wchar(familyName, &dwFamilyName), NULL, identity);
251         }
252 
253         UINT32 index;
254         BOOL exists;
255         HR_GENERAL(fFontCollection->FindFamilyName(dwFamilyName.get(), &index, &exists),
256                    "Failed while finding family by name.",
257                    identity);
258         if (!exists) {
259             return identity;
260         }
261 
262         return this->matchIndexStyle(index, style);
263     }
264 
265     class FontFallbackRenderer : public IDWriteTextRenderer {
266     public:
FontFallbackRenderer(const SkRemotableFontMgr_DirectWrite * outer,UINT32 character)267         FontFallbackRenderer(const SkRemotableFontMgr_DirectWrite* outer, UINT32 character)
268             : fRefCount(1), fOuter(SkSafeRef(outer)), fCharacter(character) {
269           fIdentity.fDataId = SkFontIdentity::kInvalidDataId;
270         }
271 
~FontFallbackRenderer()272         virtual ~FontFallbackRenderer() { }
273 
274         // 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)275         virtual HRESULT STDMETHODCALLTYPE DrawGlyphRun(
276             void* clientDrawingContext,
277             FLOAT baselineOriginX,
278             FLOAT baselineOriginY,
279             DWRITE_MEASURING_MODE measuringMode,
280             DWRITE_GLYPH_RUN const* glyphRun,
281             DWRITE_GLYPH_RUN_DESCRIPTION const* glyphRunDescription,
282             IUnknown* clientDrawingEffect) override
283         {
284             SkTScopedComPtr<IDWriteFont> font;
285             HRM(fOuter->fFontCollection->GetFontFromFontFace(glyphRun->fontFace, &font),
286                 "Could not get font from font face.");
287 
288             // It is possible that the font passed does not actually have the requested character,
289             // due to no font being found and getting the fallback font.
290             // Check that the font actually contains the requested character.
291             BOOL exists;
292             HRM(font->HasCharacter(fCharacter, &exists), "Could not find character.");
293 
294             if (exists) {
295                 HR(fOuter->FontToIdentity(font.get(), &fIdentity));
296             }
297 
298             return S_OK;
299         }
300 
DrawUnderline(void * clientDrawingContext,FLOAT baselineOriginX,FLOAT baselineOriginY,DWRITE_UNDERLINE const * underline,IUnknown * clientDrawingEffect)301         virtual HRESULT STDMETHODCALLTYPE DrawUnderline(
302             void* clientDrawingContext,
303             FLOAT baselineOriginX,
304             FLOAT baselineOriginY,
305             DWRITE_UNDERLINE const* underline,
306             IUnknown* clientDrawingEffect) override
307         { return E_NOTIMPL; }
308 
DrawStrikethrough(void * clientDrawingContext,FLOAT baselineOriginX,FLOAT baselineOriginY,DWRITE_STRIKETHROUGH const * strikethrough,IUnknown * clientDrawingEffect)309         virtual HRESULT STDMETHODCALLTYPE DrawStrikethrough(
310             void* clientDrawingContext,
311             FLOAT baselineOriginX,
312             FLOAT baselineOriginY,
313             DWRITE_STRIKETHROUGH const* strikethrough,
314             IUnknown* clientDrawingEffect) override
315         { return E_NOTIMPL; }
316 
DrawInlineObject(void * clientDrawingContext,FLOAT originX,FLOAT originY,IDWriteInlineObject * inlineObject,BOOL isSideways,BOOL isRightToLeft,IUnknown * clientDrawingEffect)317         virtual HRESULT STDMETHODCALLTYPE DrawInlineObject(
318             void* clientDrawingContext,
319             FLOAT originX,
320             FLOAT originY,
321             IDWriteInlineObject* inlineObject,
322             BOOL isSideways,
323             BOOL isRightToLeft,
324             IUnknown* clientDrawingEffect) override
325         { return E_NOTIMPL; }
326 
327         // IDWritePixelSnapping methods
IsPixelSnappingDisabled(void * clientDrawingContext,BOOL * isDisabled)328         virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(
329             void* clientDrawingContext,
330             BOOL* isDisabled) override
331         {
332             *isDisabled = FALSE;
333             return S_OK;
334         }
335 
GetCurrentTransform(void * clientDrawingContext,DWRITE_MATRIX * transform)336         virtual HRESULT STDMETHODCALLTYPE GetCurrentTransform(
337             void* clientDrawingContext,
338             DWRITE_MATRIX* transform) override
339         {
340             const DWRITE_MATRIX ident = {1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
341             *transform = ident;
342             return S_OK;
343         }
344 
GetPixelsPerDip(void * clientDrawingContext,FLOAT * pixelsPerDip)345         virtual HRESULT STDMETHODCALLTYPE GetPixelsPerDip(
346             void* clientDrawingContext,
347             FLOAT* pixelsPerDip) override
348         {
349             *pixelsPerDip = 1.0f;
350             return S_OK;
351         }
352 
353         // IUnknown methods
AddRef()354         ULONG STDMETHODCALLTYPE AddRef() override {
355             return InterlockedIncrement(&fRefCount);
356         }
357 
Release()358         ULONG STDMETHODCALLTYPE Release() override {
359             ULONG newCount = InterlockedDecrement(&fRefCount);
360             if (0 == newCount) {
361                 delete this;
362             }
363             return newCount;
364         }
365 
QueryInterface(IID const & riid,void ** ppvObject)366         virtual HRESULT STDMETHODCALLTYPE QueryInterface(
367             IID const& riid, void** ppvObject) override
368         {
369             if (__uuidof(IUnknown) == riid ||
370                 __uuidof(IDWritePixelSnapping) == riid ||
371                 __uuidof(IDWriteTextRenderer) == riid)
372             {
373                 *ppvObject = this;
374                 this->AddRef();
375                 return S_OK;
376             }
377             *ppvObject = NULL;
378             return E_FAIL;
379         }
380 
FallbackIdentity()381         const SkFontIdentity FallbackIdentity() { return fIdentity; }
382 
383     protected:
384         ULONG fRefCount;
385         SkAutoTUnref<const SkRemotableFontMgr_DirectWrite> fOuter;
386         UINT32 fCharacter;
387         SkFontIdentity fIdentity;
388     };
389 
matchNameStyleCharacter(const char familyName[],const SkFontStyle & pattern,const char * bcp47[],int bcp47Count,SkUnichar character) const390     virtual SkFontIdentity matchNameStyleCharacter(const char familyName[],
391                                                    const SkFontStyle& pattern,
392                                                    const char* bcp47[], int bcp47Count,
393                                                    SkUnichar character) const override
394     {
395         SkFontIdentity identity = { SkFontIdentity::kInvalidDataId };
396 
397         IDWriteFactory* dwFactory = sk_get_dwrite_factory();
398         if (NULL == dwFactory) {
399             return identity;
400         }
401 
402         // TODO: use IDWriteFactory2::GetSystemFontFallback when available.
403 
404         const DWriteStyle dwStyle(pattern);
405 
406         SkSMallocWCHAR dwFamilyName;
407         if (NULL == familyName) {
408             HR_GENERAL(getDefaultFontFamilyName(&dwFamilyName), NULL, identity);
409         } else {
410             HR_GENERAL(sk_cstring_to_wchar(familyName, &dwFamilyName), NULL, identity);
411         }
412 
413         const SkSMallocWCHAR* dwBcp47;
414         SkSMallocWCHAR dwBcp47Local;
415         if (bcp47Count < 1) {
416             dwBcp47 = &fLocaleName;
417         } else {
418             //TODO: support fallback stack.
419             HR_GENERAL(sk_cstring_to_wchar(bcp47[bcp47Count-1], &dwBcp47Local), NULL, identity);
420             dwBcp47 = &dwBcp47Local;
421         }
422 
423         SkTScopedComPtr<IDWriteTextFormat> fallbackFormat;
424         HR_GENERAL(dwFactory->CreateTextFormat(dwFamilyName,
425                                                fFontCollection.get(),
426                                                dwStyle.fWeight,
427                                                dwStyle.fSlant,
428                                                dwStyle.fWidth,
429                                                72.0f,
430                                                *dwBcp47,
431                                                &fallbackFormat),
432                    "Could not create text format.",
433                    identity);
434 
435         WCHAR str[16];
436         UINT32 strLen = static_cast<UINT32>(
437             SkUTF16_FromUnichar(character, reinterpret_cast<uint16_t*>(str)));
438         SkTScopedComPtr<IDWriteTextLayout> fallbackLayout;
439         HR_GENERAL(dwFactory->CreateTextLayout(str, strLen, fallbackFormat.get(),
440                                                200.0f, 200.0f,
441                                                &fallbackLayout),
442                    "Could not create text layout.",
443                    identity);
444 
445         SkTScopedComPtr<FontFallbackRenderer> fontFallbackRenderer(
446             new FontFallbackRenderer(this, character));
447 
448         HR_GENERAL(fallbackLayout->Draw(NULL, fontFallbackRenderer.get(), 50.0f, 50.0f),
449                    "Could not draw layout with renderer.",
450                    identity);
451 
452         return fontFallbackRenderer->FallbackIdentity();
453     }
454 
getData(int dataId) const455     SkStreamAsset* getData(int dataId) const override {
456         SkAutoMutexAcquire ama(fDataIdCacheMutex);
457         if (dataId >= fDataIdCache.count()) {
458             return NULL;
459         }
460         const DataId& id = fDataIdCache[dataId];
461 
462         SkTScopedComPtr<IDWriteFontFileLoader> loader;
463         HRNM(id.fLoader->QueryInterface(&loader), "QuerryInterface IDWriteFontFileLoader failed");
464 
465         SkTScopedComPtr<IDWriteFontFileStream> fontFileStream;
466         HRNM(loader->CreateStreamFromKey(id.fKey, id.fKeySize, &fontFileStream),
467              "Could not create font file stream.");
468 
469         return SkNEW_ARGS(SkDWriteFontFileStream, (fontFileStream.get()));
470     }
471 
472 private:
473     SkTScopedComPtr<IDWriteFontCollection> fFontCollection;
474     SkSMallocWCHAR fLocaleName;
475 
476     typedef SkRemotableFontMgr INHERITED;
477 };
478 
SkRemotableFontMgr_New_DirectWrite()479 SkRemotableFontMgr* SkRemotableFontMgr_New_DirectWrite() {
480     IDWriteFactory* factory = sk_get_dwrite_factory();
481     if (NULL == factory) {
482         return NULL;
483     }
484 
485     SkTScopedComPtr<IDWriteFontCollection> sysFontCollection;
486     HRNM(factory->GetSystemFontCollection(&sysFontCollection, FALSE),
487          "Could not get system font collection.");
488 
489     WCHAR localeNameStorage[LOCALE_NAME_MAX_LENGTH];
490     WCHAR* localeName = NULL;
491     int localeNameLen = 0;
492 
493     // Dynamically load GetUserDefaultLocaleName function, as it is not available on XP.
494     SkGetUserDefaultLocaleNameProc getUserDefaultLocaleNameProc = NULL;
495     HRESULT hr = SkGetGetUserDefaultLocaleNameProc(&getUserDefaultLocaleNameProc);
496     if (NULL == getUserDefaultLocaleNameProc) {
497         SK_TRACEHR(hr, "Could not get GetUserDefaultLocaleName.");
498     } else {
499         localeNameLen = getUserDefaultLocaleNameProc(localeNameStorage, LOCALE_NAME_MAX_LENGTH);
500         if (localeNameLen) {
501             localeName = localeNameStorage;
502         };
503     }
504 
505     return SkNEW_ARGS(SkRemotableFontMgr_DirectWrite, (sysFontCollection.get(),
506                                                        localeName, localeNameLen));
507 }
508