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