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