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
24class SK_API SkRemotableFontMgr_DirectWrite : public SkRemotableFontMgr {
25private:
26    struct DataId {
27        IUnknown* fLoader;  // In COM only IUnknown pointers may be safely used for identity.
28        void* fKey;
29        UINT32 fKeySize;
30
31        DataId() { }
32
33        // This is actually a move!!!
34        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
42        ~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
53    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
82public:
83    SK_DECLARE_INST_COUNT(SkRemotableFontMgr_DirectWrite)
84
85    /** localeNameLength must include the null terminator. */
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
94    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
114    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
161    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
179    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
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
219    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
241    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:
267        FontFallbackRenderer(const SkRemotableFontMgr_DirectWrite* outer, UINT32 character)
268            : fRefCount(1), fOuter(SkSafeRef(outer)), fCharacter(character) {
269          fIdentity.fDataId = SkFontIdentity::kInvalidDataId;
270        }
271
272        virtual ~FontFallbackRenderer() { }
273
274        // IDWriteTextRenderer methods
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
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
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
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
328        virtual HRESULT STDMETHODCALLTYPE IsPixelSnappingDisabled(
329            void* clientDrawingContext,
330            BOOL* isDisabled) override
331        {
332            *isDisabled = FALSE;
333            return S_OK;
334        }
335
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
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
354        ULONG STDMETHODCALLTYPE AddRef() override {
355            return InterlockedIncrement(&fRefCount);
356        }
357
358        ULONG STDMETHODCALLTYPE Release() override {
359            ULONG newCount = InterlockedDecrement(&fRefCount);
360            if (0 == newCount) {
361                delete this;
362            }
363            return newCount;
364        }
365
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
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
390    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
455    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
472private:
473    SkTScopedComPtr<IDWriteFontCollection> fFontCollection;
474    SkSMallocWCHAR fLocaleName;
475
476    typedef SkRemotableFontMgr INHERITED;
477};
478
479SkRemotableFontMgr* 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