diff -r 000000000000 -r 6474c204b198 gfx/skia/trunk/src/ports/SkFontHost_win_dw.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/skia/trunk/src/ports/SkFontHost_win_dw.cpp Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,1931 @@ +/* + * Copyright 2011 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkTypes.h" +#undef GetGlyphIndices + +#include "SkAdvancedTypefaceMetrics.h" +#include "SkColorFilter.h" +#include "SkDWriteFontFileStream.h" +#include "SkDWriteGeometrySink.h" +#include "SkDescriptor.h" +#include "SkEndian.h" +#include "SkFontDescriptor.h" +#include "SkFontHost.h" +#include "SkFontMgr.h" +#include "SkFontStream.h" +#include "SkGlyph.h" +#include "SkHRESULT.h" +#include "SkMaskGamma.h" +#include "SkOnce.h" +#include "SkOTTable_head.h" +#include "SkOTTable_hhea.h" +#include "SkOTTable_OS_2.h" +#include "SkOTTable_post.h" +#include "SkPath.h" +#include "SkStream.h" +#include "SkString.h" +#include "SkTScopedComPtr.h" +#include "SkThread.h" +#include "SkTypeface_win.h" +#include "SkTypefaceCache.h" +#include "SkUtils.h" + +#include + +static bool isLCD(const SkScalerContext::Rec& rec) { + return SkMask::kLCD16_Format == rec.fMaskFormat || + SkMask::kLCD32_Format == rec.fMaskFormat; +} + +/** Prefer to use this type to prevent template proliferation. */ +typedef SkAutoSTMalloc<16, WCHAR> SkSMallocWCHAR; + +/** Converts a utf8 string to a WCHAR string. */ +static HRESULT cstring_to_wchar(const char* skname, SkSMallocWCHAR* name) { + int wlen = MultiByteToWideChar(CP_UTF8, 0, skname, -1, NULL, 0); + if (0 == wlen) { + HRM(HRESULT_FROM_WIN32(GetLastError()), + "Could not get length for wchar to utf-8 conversion."); + } + name->reset(wlen); + wlen = MultiByteToWideChar(CP_UTF8, 0, skname, -1, name->get(), wlen); + if (0 == wlen) { + HRM(HRESULT_FROM_WIN32(GetLastError()), "Could not convert wchar to utf-8."); + } + return S_OK; +} + +/** Converts a WCHAR string to a utf8 string. */ +static HRESULT wchar_to_skstring(WCHAR* name, SkString* skname) { + int len = WideCharToMultiByte(CP_UTF8, 0, name, -1, NULL, 0, NULL, NULL); + if (0 == len) { + HRM(HRESULT_FROM_WIN32(GetLastError()), + "Could not get length for utf-8 to wchar conversion."); + } + skname->resize(len - 1); + + // TODO: remove after https://code.google.com/p/skia/issues/detail?id=1989 is fixed. + // If we resize to 0 then the skname points to gEmptyRec (the unique empty SkString::Rec). + // gEmptyRec is static const and on Windows this means the value is in a read only page. + // Writing to it in the following call to WideCharToMultiByte will cause an access violation. + if (1 == len) { + return S_OK; + } + + len = WideCharToMultiByte(CP_UTF8, 0, name, -1, skname->writable_str(), len, NULL, NULL); + if (0 == len) { + HRM(HRESULT_FROM_WIN32(GetLastError()), "Could not convert utf-8 to wchar."); + } + return S_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void create_dwrite_factory(IDWriteFactory** factory) { + typedef decltype(DWriteCreateFactory)* DWriteCreateFactoryProc; + DWriteCreateFactoryProc dWriteCreateFactoryProc = reinterpret_cast( + GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory")); + + if (!dWriteCreateFactoryProc) { + HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); + if (!IS_ERROR(hr)) { + hr = ERROR_PROC_NOT_FOUND; + } + HRVM(hr, "Could not get DWriteCreateFactory proc."); + } + + HRVM(dWriteCreateFactoryProc(DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast(factory)), + "Could not create DirectWrite factory."); +} + +static IDWriteFactory* get_dwrite_factory() { + static IDWriteFactory* gDWriteFactory = NULL; + SK_DECLARE_STATIC_ONCE(once); + SkOnce(&once, create_dwrite_factory, &gDWriteFactory); + + return gDWriteFactory; +} + +/////////////////////////////////////////////////////////////////////////////// + +class StreamFontFileLoader; + +class SkFontMgr_DirectWrite : public SkFontMgr { +public: + /** localeNameLength must include the null terminator. */ + SkFontMgr_DirectWrite(IDWriteFontCollection* fontCollection, + WCHAR* localeName, int localeNameLength) + : fFontCollection(SkRefComPtr(fontCollection)) + , fLocaleName(localeNameLength) + { + memcpy(fLocaleName.get(), localeName, localeNameLength * sizeof(WCHAR)); + } + + SkTypeface* createTypefaceFromDWriteFont(IDWriteFontFace* fontFace, + IDWriteFont* font, + IDWriteFontFamily* fontFamily, + StreamFontFileLoader* = NULL, + IDWriteFontCollectionLoader* = NULL) const; + +protected: + virtual int onCountFamilies() const SK_OVERRIDE; + virtual void onGetFamilyName(int index, SkString* familyName) const SK_OVERRIDE; + virtual SkFontStyleSet* onCreateStyleSet(int index) const SK_OVERRIDE; + virtual SkFontStyleSet* onMatchFamily(const char familyName[]) const SK_OVERRIDE; + virtual SkTypeface* onMatchFamilyStyle(const char familyName[], + const SkFontStyle& fontstyle) const SK_OVERRIDE; + virtual SkTypeface* onMatchFaceStyle(const SkTypeface* familyMember, + const SkFontStyle& fontstyle) const SK_OVERRIDE; + virtual SkTypeface* onCreateFromStream(SkStream* stream, int ttcIndex) const SK_OVERRIDE; + virtual SkTypeface* onCreateFromData(SkData* data, int ttcIndex) const SK_OVERRIDE; + virtual SkTypeface* onCreateFromFile(const char path[], int ttcIndex) const SK_OVERRIDE; + virtual SkTypeface* onLegacyCreateTypeface(const char familyName[], + unsigned styleBits) const SK_OVERRIDE; + +private: + HRESULT getByFamilyName(const WCHAR familyName[], IDWriteFontFamily** fontFamily) const; + HRESULT getDefaultFontFamily(IDWriteFontFamily** fontFamily) const; + + void Add(SkTypeface* face, SkTypeface::Style requestedStyle, bool strong) const { + SkAutoMutexAcquire ama(fTFCacheMutex); + fTFCache.add(face, requestedStyle, strong); + } + + SkTypeface* FindByProcAndRef(SkTypefaceCache::FindProc proc, void* ctx) const { + SkAutoMutexAcquire ama(fTFCacheMutex); + SkTypeface* typeface = fTFCache.findByProcAndRef(proc, ctx); + return typeface; + } + + SkTScopedComPtr fFontCollection; + SkSMallocWCHAR fLocaleName; + mutable SkMutex fTFCacheMutex; + mutable SkTypefaceCache fTFCache; + + friend class SkFontStyleSet_DirectWrite; +}; + +class SkFontStyleSet_DirectWrite : public SkFontStyleSet { +public: + SkFontStyleSet_DirectWrite(const SkFontMgr_DirectWrite* fontMgr, + IDWriteFontFamily* fontFamily) + : fFontMgr(SkRef(fontMgr)) + , fFontFamily(SkRefComPtr(fontFamily)) + { } + + virtual int count() SK_OVERRIDE; + virtual void getStyle(int index, SkFontStyle* fs, SkString* styleName) SK_OVERRIDE; + virtual SkTypeface* createTypeface(int index) SK_OVERRIDE; + virtual SkTypeface* matchStyle(const SkFontStyle& pattern) SK_OVERRIDE; + +private: + SkAutoTUnref fFontMgr; + SkTScopedComPtr fFontFamily; +}; + +/////////////////////////////////////////////////////////////////////////////// + +class DWriteOffscreen { +public: + DWriteOffscreen() : fWidth(0), fHeight(0) { + } + + void init(IDWriteFontFace* fontFace, const DWRITE_MATRIX& xform, FLOAT fontSize) { + fFontFace = fontFace; + fFontSize = fontSize; + fXform = xform; + } + + const void* draw(const SkGlyph&, bool isBW); + +private: + uint16_t fWidth; + uint16_t fHeight; + IDWriteFontFace* fFontFace; + FLOAT fFontSize; + DWRITE_MATRIX fXform; + SkTDArray fBits; +}; + +const void* DWriteOffscreen::draw(const SkGlyph& glyph, bool isBW) { + IDWriteFactory* factory = get_dwrite_factory(); + SkASSERT(factory != NULL); + + if (fWidth < glyph.fWidth || fHeight < glyph.fHeight) { + fWidth = SkMax32(fWidth, glyph.fWidth); + fHeight = SkMax32(fHeight, glyph.fHeight); + + if (isBW) { + fBits.setCount(fWidth * fHeight); + } else { + fBits.setCount(fWidth * fHeight * 3); + } + } + + // erase + memset(fBits.begin(), 0, fBits.count()); + + fXform.dx = SkFixedToFloat(glyph.getSubXFixed()); + fXform.dy = SkFixedToFloat(glyph.getSubYFixed()); + + FLOAT advance = 0.0f; + + UINT16 index = glyph.getGlyphID(); + + DWRITE_GLYPH_OFFSET offset; + offset.advanceOffset = 0.0f; + offset.ascenderOffset = 0.0f; + + DWRITE_GLYPH_RUN run; + run.glyphCount = 1; + run.glyphAdvances = &advance; + run.fontFace = fFontFace; + run.fontEmSize = fFontSize; + run.bidiLevel = 0; + run.glyphIndices = &index; + run.isSideways = FALSE; + run.glyphOffsets = &offset; + + DWRITE_RENDERING_MODE renderingMode; + DWRITE_TEXTURE_TYPE textureType; + if (isBW) { + renderingMode = DWRITE_RENDERING_MODE_ALIASED; + textureType = DWRITE_TEXTURE_ALIASED_1x1; + } else { + renderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; + textureType = DWRITE_TEXTURE_CLEARTYPE_3x1; + } + SkTScopedComPtr glyphRunAnalysis; + HRNM(factory->CreateGlyphRunAnalysis(&run, + 1.0f, // pixelsPerDip, + &fXform, + renderingMode, + DWRITE_MEASURING_MODE_NATURAL, + 0.0f, // baselineOriginX, + 0.0f, // baselineOriginY, + &glyphRunAnalysis), + "Could not create glyph run analysis."); + + //NOTE: this assumes that the glyph has already been measured + //with an exact same glyph run analysis. + RECT bbox; + bbox.left = glyph.fLeft; + bbox.top = glyph.fTop; + bbox.right = glyph.fLeft + glyph.fWidth; + bbox.bottom = glyph.fTop + glyph.fHeight; + HRNM(glyphRunAnalysis->CreateAlphaTexture(textureType, + &bbox, + fBits.begin(), + fBits.count()), + "Could not draw mask."); + return fBits.begin(); +} + +/////////////////////////////////////////////////////////////////////////////// + +class StreamFontFileLoader : public IDWriteFontFileLoader { +public: + // IUnknown methods + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(); + virtual ULONG STDMETHODCALLTYPE Release(); + + // IDWriteFontFileLoader methods + virtual HRESULT STDMETHODCALLTYPE CreateStreamFromKey( + void const* fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + IDWriteFontFileStream** fontFileStream); + + static HRESULT Create(SkStream* stream, StreamFontFileLoader** streamFontFileLoader) { + *streamFontFileLoader = new StreamFontFileLoader(stream); + if (NULL == streamFontFileLoader) { + return E_OUTOFMEMORY; + } + return S_OK; + } + + SkAutoTUnref fStream; + +private: + StreamFontFileLoader(SkStream* stream) : fRefCount(1), fStream(SkRef(stream)) { } + + ULONG fRefCount; +}; + +HRESULT StreamFontFileLoader::QueryInterface(REFIID iid, void** ppvObject) { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileLoader)) { + *ppvObject = this; + AddRef(); + return S_OK; + } else { + *ppvObject = NULL; + return E_NOINTERFACE; + } +} + +ULONG StreamFontFileLoader::AddRef() { + return InterlockedIncrement(&fRefCount); +} + +ULONG StreamFontFileLoader::Release() { + ULONG newCount = InterlockedDecrement(&fRefCount); + if (0 == newCount) { + delete this; + } + return newCount; +} + +HRESULT StreamFontFileLoader::CreateStreamFromKey( + void const* fontFileReferenceKey, + UINT32 fontFileReferenceKeySize, + IDWriteFontFileStream** fontFileStream) +{ + SkTScopedComPtr stream; + HR(SkDWriteFontFileStreamWrapper::Create(fStream, &stream)); + *fontFileStream = stream.release(); + return S_OK; +} + +class StreamFontFileEnumerator : public IDWriteFontFileEnumerator { +public: + // IUnknown methods + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(); + virtual ULONG STDMETHODCALLTYPE Release(); + + // IDWriteFontFileEnumerator methods + virtual HRESULT STDMETHODCALLTYPE MoveNext(BOOL* hasCurrentFile); + virtual HRESULT STDMETHODCALLTYPE GetCurrentFontFile(IDWriteFontFile** fontFile); + + static HRESULT Create(IDWriteFactory* factory, IDWriteFontFileLoader* fontFileLoader, + StreamFontFileEnumerator** streamFontFileEnumerator) { + *streamFontFileEnumerator = new StreamFontFileEnumerator(factory, fontFileLoader); + if (NULL == streamFontFileEnumerator) { + return E_OUTOFMEMORY; + } + return S_OK; + } +private: + StreamFontFileEnumerator(IDWriteFactory* factory, IDWriteFontFileLoader* fontFileLoader); + ULONG fRefCount; + + SkTScopedComPtr fFactory; + SkTScopedComPtr fCurrentFile; + SkTScopedComPtr fFontFileLoader; + bool fHasNext; +}; + +StreamFontFileEnumerator::StreamFontFileEnumerator(IDWriteFactory* factory, + IDWriteFontFileLoader* fontFileLoader) + : fRefCount(1) + , fFactory(SkRefComPtr(factory)) + , fCurrentFile() + , fFontFileLoader(SkRefComPtr(fontFileLoader)) + , fHasNext(true) +{ } + +HRESULT StreamFontFileEnumerator::QueryInterface(REFIID iid, void** ppvObject) { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontFileEnumerator)) { + *ppvObject = this; + AddRef(); + return S_OK; + } else { + *ppvObject = NULL; + return E_NOINTERFACE; + } +} + +ULONG StreamFontFileEnumerator::AddRef() { + return InterlockedIncrement(&fRefCount); +} + +ULONG StreamFontFileEnumerator::Release() { + ULONG newCount = InterlockedDecrement(&fRefCount); + if (0 == newCount) { + delete this; + } + return newCount; +} + +HRESULT StreamFontFileEnumerator::MoveNext(BOOL* hasCurrentFile) { + *hasCurrentFile = FALSE; + + if (!fHasNext) { + return S_OK; + } + fHasNext = false; + + UINT32 dummy = 0; + HR(fFactory->CreateCustomFontFileReference( + &dummy, //cannot be NULL + sizeof(dummy), //even if this is 0 + fFontFileLoader.get(), + &fCurrentFile)); + + *hasCurrentFile = TRUE; + return S_OK; +} + +HRESULT StreamFontFileEnumerator::GetCurrentFontFile(IDWriteFontFile** fontFile) { + if (fCurrentFile.get() == NULL) { + *fontFile = NULL; + return E_FAIL; + } + + *fontFile = SkRefComPtr(fCurrentFile.get()); + return S_OK; +} + +class StreamFontCollectionLoader : public IDWriteFontCollectionLoader { +public: + // IUnknown methods + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void** ppvObject); + virtual ULONG STDMETHODCALLTYPE AddRef(); + virtual ULONG STDMETHODCALLTYPE Release(); + + // IDWriteFontCollectionLoader methods + virtual HRESULT STDMETHODCALLTYPE CreateEnumeratorFromKey( + IDWriteFactory* factory, + void const* collectionKey, + UINT32 collectionKeySize, + IDWriteFontFileEnumerator** fontFileEnumerator); + + static HRESULT Create(IDWriteFontFileLoader* fontFileLoader, + StreamFontCollectionLoader** streamFontCollectionLoader) { + *streamFontCollectionLoader = new StreamFontCollectionLoader(fontFileLoader); + if (NULL == streamFontCollectionLoader) { + return E_OUTOFMEMORY; + } + return S_OK; + } +private: + StreamFontCollectionLoader(IDWriteFontFileLoader* fontFileLoader) + : fRefCount(1) + , fFontFileLoader(SkRefComPtr(fontFileLoader)) + { } + + ULONG fRefCount; + SkTScopedComPtr fFontFileLoader; +}; + +HRESULT StreamFontCollectionLoader::QueryInterface(REFIID iid, void** ppvObject) { + if (iid == IID_IUnknown || iid == __uuidof(IDWriteFontCollectionLoader)) { + *ppvObject = this; + AddRef(); + return S_OK; + } else { + *ppvObject = NULL; + return E_NOINTERFACE; + } +} + +ULONG StreamFontCollectionLoader::AddRef() { + return InterlockedIncrement(&fRefCount); +} + +ULONG StreamFontCollectionLoader::Release() { + ULONG newCount = InterlockedDecrement(&fRefCount); + if (0 == newCount) { + delete this; + } + return newCount; +} + +HRESULT StreamFontCollectionLoader::CreateEnumeratorFromKey( + IDWriteFactory* factory, + void const* collectionKey, + UINT32 collectionKeySize, + IDWriteFontFileEnumerator** fontFileEnumerator) +{ + SkTScopedComPtr enumerator; + HR(StreamFontFileEnumerator::Create(factory, fFontFileLoader.get(), &enumerator)); + *fontFileEnumerator = enumerator.release(); + return S_OK; +} + +/////////////////////////////////////////////////////////////////////////////// + +static SkTypeface::Style get_style(IDWriteFont* font) { + int style = SkTypeface::kNormal; + DWRITE_FONT_WEIGHT weight = font->GetWeight(); + if (DWRITE_FONT_WEIGHT_DEMI_BOLD <= weight) { + style |= SkTypeface::kBold; + } + DWRITE_FONT_STYLE angle = font->GetStyle(); + if (DWRITE_FONT_STYLE_OBLIQUE == angle || DWRITE_FONT_STYLE_ITALIC == angle) { + style |= SkTypeface::kItalic; + } + return static_cast(style); +} + +class DWriteFontTypeface : public SkTypeface { +private: + DWriteFontTypeface(SkTypeface::Style style, SkFontID fontID, + IDWriteFontFace* fontFace, + IDWriteFont* font, + IDWriteFontFamily* fontFamily, + StreamFontFileLoader* fontFileLoader = NULL, + IDWriteFontCollectionLoader* fontCollectionLoader = NULL) + : SkTypeface(style, fontID, false) + , fDWriteFontCollectionLoader(SkSafeRefComPtr(fontCollectionLoader)) + , fDWriteFontFileLoader(SkSafeRefComPtr(fontFileLoader)) + , fDWriteFontFamily(SkRefComPtr(fontFamily)) + , fDWriteFont(SkRefComPtr(font)) + , fDWriteFontFace(SkRefComPtr(fontFace)) + { } + +public: + SkTScopedComPtr fDWriteFontCollectionLoader; + SkTScopedComPtr fDWriteFontFileLoader; + SkTScopedComPtr fDWriteFontFamily; + SkTScopedComPtr fDWriteFont; + SkTScopedComPtr fDWriteFontFace; + + static DWriteFontTypeface* Create(IDWriteFontFace* fontFace, + IDWriteFont* font, + IDWriteFontFamily* fontFamily, + StreamFontFileLoader* fontFileLoader = NULL, + IDWriteFontCollectionLoader* fontCollectionLoader = NULL) { + SkTypeface::Style style = get_style(font); + SkFontID fontID = SkTypefaceCache::NewFontID(); + return SkNEW_ARGS(DWriteFontTypeface, (style, fontID, + fontFace, font, fontFamily, + fontFileLoader, fontCollectionLoader)); + } + + ~DWriteFontTypeface() { + if (fDWriteFontCollectionLoader.get() == NULL) return; + + IDWriteFactory* factory = get_dwrite_factory(); + SkASSERT(factory != NULL); + HRV(factory->UnregisterFontCollectionLoader(fDWriteFontCollectionLoader.get())); + HRV(factory->UnregisterFontFileLoader(fDWriteFontFileLoader.get())); + } + +protected: + virtual SkStream* onOpenStream(int* ttcIndex) const SK_OVERRIDE; + virtual SkScalerContext* onCreateScalerContext(const SkDescriptor*) const SK_OVERRIDE; + virtual void onFilterRec(SkScalerContextRec*) const SK_OVERRIDE; + virtual SkAdvancedTypefaceMetrics* onGetAdvancedTypefaceMetrics( + SkAdvancedTypefaceMetrics::PerGlyphInfo, + const uint32_t*, uint32_t) const SK_OVERRIDE; + virtual void onGetFontDescriptor(SkFontDescriptor*, bool*) const SK_OVERRIDE; + virtual int onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const SK_OVERRIDE; + virtual int onCountGlyphs() const SK_OVERRIDE; + virtual int onGetUPEM() const SK_OVERRIDE; + virtual SkTypeface::LocalizedStrings* onCreateFamilyNameIterator() const SK_OVERRIDE; + virtual int onGetTableTags(SkFontTableTag tags[]) const SK_OVERRIDE; + virtual size_t onGetTableData(SkFontTableTag, size_t offset, + size_t length, void* data) const SK_OVERRIDE; +}; + +class SkScalerContext_DW : public SkScalerContext { +public: + SkScalerContext_DW(DWriteFontTypeface*, const SkDescriptor* desc); + virtual ~SkScalerContext_DW(); + +protected: + virtual unsigned generateGlyphCount() SK_OVERRIDE; + virtual uint16_t generateCharToGlyph(SkUnichar uni) SK_OVERRIDE; + virtual void generateAdvance(SkGlyph* glyph) SK_OVERRIDE; + virtual void generateMetrics(SkGlyph* glyph) SK_OVERRIDE; + virtual void generateImage(const SkGlyph& glyph) SK_OVERRIDE; + virtual void generatePath(const SkGlyph& glyph, SkPath* path) SK_OVERRIDE; + virtual void generateFontMetrics(SkPaint::FontMetrics* mX, + SkPaint::FontMetrics* mY) SK_OVERRIDE; + +private: + DWriteOffscreen fOffscreen; + DWRITE_MATRIX fXform; + SkAutoTUnref fTypeface; + int fGlyphCount; +}; + +static bool are_same(IUnknown* a, IUnknown* b) { + SkTScopedComPtr iunkA; + if (FAILED(a->QueryInterface(&iunkA))) { + return false; + } + + SkTScopedComPtr iunkB; + if (FAILED(b->QueryInterface(&iunkB))) { + return false; + } + + return iunkA.get() == iunkB.get(); +} +static bool FindByDWriteFont(SkTypeface* face, SkTypeface::Style requestedStyle, void* ctx) { + //Check to see if the two fonts are identical. + DWriteFontTypeface* dwFace = reinterpret_cast(face); + IDWriteFont* dwFont = reinterpret_cast(ctx); + if (are_same(dwFace->fDWriteFont.get(), dwFont)) { + return true; + } + + //Check if the two fonts share the same loader and have the same key. + SkTScopedComPtr dwFaceFontFace; + SkTScopedComPtr dwFontFace; + HRB(dwFace->fDWriteFont->CreateFontFace(&dwFaceFontFace)); + HRB(dwFont->CreateFontFace(&dwFontFace)); + if (are_same(dwFaceFontFace.get(), dwFontFace.get())) { + return true; + } + + UINT32 dwFaceNumFiles; + UINT32 dwNumFiles; + HRB(dwFaceFontFace->GetFiles(&dwFaceNumFiles, NULL)); + HRB(dwFontFace->GetFiles(&dwNumFiles, NULL)); + if (dwFaceNumFiles != dwNumFiles) { + return false; + } + + SkTScopedComPtr dwFaceFontFile; + SkTScopedComPtr dwFontFile; + HRB(dwFaceFontFace->GetFiles(&dwFaceNumFiles, &dwFaceFontFile)); + HRB(dwFontFace->GetFiles(&dwNumFiles, &dwFontFile)); + + //for (each file) { //we currently only admit fonts from one file. + SkTScopedComPtr dwFaceFontFileLoader; + SkTScopedComPtr dwFontFileLoader; + HRB(dwFaceFontFile->GetLoader(&dwFaceFontFileLoader)); + HRB(dwFontFile->GetLoader(&dwFontFileLoader)); + if (!are_same(dwFaceFontFileLoader.get(), dwFontFileLoader.get())) { + return false; + } + //} + + const void* dwFaceFontRefKey; + UINT32 dwFaceFontRefKeySize; + const void* dwFontRefKey; + UINT32 dwFontRefKeySize; + HRB(dwFaceFontFile->GetReferenceKey(&dwFaceFontRefKey, &dwFaceFontRefKeySize)); + HRB(dwFontFile->GetReferenceKey(&dwFontRefKey, &dwFontRefKeySize)); + if (dwFaceFontRefKeySize != dwFontRefKeySize) { + return false; + } + if (0 != memcmp(dwFaceFontRefKey, dwFontRefKey, dwFontRefKeySize)) { + return false; + } + + //TODO: better means than comparing name strings? + //NOTE: .tfc and fake bold/italic will end up here. + SkTScopedComPtr dwFaceFontFamily; + SkTScopedComPtr dwFontFamily; + HRB(dwFace->fDWriteFont->GetFontFamily(&dwFaceFontFamily)); + HRB(dwFont->GetFontFamily(&dwFontFamily)); + + SkTScopedComPtr dwFaceFontFamilyNames; + SkTScopedComPtr dwFaceFontNames; + HRB(dwFaceFontFamily->GetFamilyNames(&dwFaceFontFamilyNames)); + HRB(dwFace->fDWriteFont->GetFaceNames(&dwFaceFontNames)); + + SkTScopedComPtr dwFontFamilyNames; + SkTScopedComPtr dwFontNames; + HRB(dwFontFamily->GetFamilyNames(&dwFontFamilyNames)); + HRB(dwFont->GetFaceNames(&dwFontNames)); + + UINT32 dwFaceFontFamilyNameLength; + UINT32 dwFaceFontNameLength; + HRB(dwFaceFontFamilyNames->GetStringLength(0, &dwFaceFontFamilyNameLength)); + HRB(dwFaceFontNames->GetStringLength(0, &dwFaceFontNameLength)); + + UINT32 dwFontFamilyNameLength; + UINT32 dwFontNameLength; + HRB(dwFontFamilyNames->GetStringLength(0, &dwFontFamilyNameLength)); + HRB(dwFontNames->GetStringLength(0, &dwFontNameLength)); + + if (dwFaceFontFamilyNameLength != dwFontFamilyNameLength || + dwFaceFontNameLength != dwFontNameLength) + { + return false; + } + + SkSMallocWCHAR dwFaceFontFamilyNameChar(dwFaceFontFamilyNameLength+1); + SkSMallocWCHAR dwFaceFontNameChar(dwFaceFontNameLength+1); + HRB(dwFaceFontFamilyNames->GetString(0, dwFaceFontFamilyNameChar.get(), dwFaceFontFamilyNameLength+1)); + HRB(dwFaceFontNames->GetString(0, dwFaceFontNameChar.get(), dwFaceFontNameLength+1)); + + SkSMallocWCHAR dwFontFamilyNameChar(dwFontFamilyNameLength+1); + SkSMallocWCHAR dwFontNameChar(dwFontNameLength+1); + HRB(dwFontFamilyNames->GetString(0, dwFontFamilyNameChar.get(), dwFontFamilyNameLength+1)); + HRB(dwFontNames->GetString(0, dwFontNameChar.get(), dwFontNameLength+1)); + + return wcscmp(dwFaceFontFamilyNameChar.get(), dwFontFamilyNameChar.get()) == 0 && + wcscmp(dwFaceFontNameChar.get(), dwFontNameChar.get()) == 0; +} + +SkScalerContext_DW::SkScalerContext_DW(DWriteFontTypeface* typeface, + const SkDescriptor* desc) + : SkScalerContext(typeface, desc) + , fTypeface(SkRef(typeface)) + , fGlyphCount(-1) { + + fXform.m11 = SkScalarToFloat(fRec.fPost2x2[0][0]); + fXform.m12 = SkScalarToFloat(fRec.fPost2x2[1][0]); + fXform.m21 = SkScalarToFloat(fRec.fPost2x2[0][1]); + fXform.m22 = SkScalarToFloat(fRec.fPost2x2[1][1]); + fXform.dx = 0; + fXform.dy = 0; + + fOffscreen.init(fTypeface->fDWriteFontFace.get(), fXform, SkScalarToFloat(fRec.fTextSize)); +} + +SkScalerContext_DW::~SkScalerContext_DW() { +} + +unsigned SkScalerContext_DW::generateGlyphCount() { + if (fGlyphCount < 0) { + fGlyphCount = fTypeface->fDWriteFontFace->GetGlyphCount(); + } + return fGlyphCount; +} + +uint16_t SkScalerContext_DW::generateCharToGlyph(SkUnichar uni) { + uint16_t index = 0; + fTypeface->fDWriteFontFace->GetGlyphIndices(reinterpret_cast(&uni), 1, &index); + return index; +} + +void SkScalerContext_DW::generateAdvance(SkGlyph* glyph) { + //Delta is the difference between the right/left side bearing metric + //and where the right/left side bearing ends up after hinting. + //DirectWrite does not provide this information. + glyph->fRsbDelta = 0; + glyph->fLsbDelta = 0; + + glyph->fAdvanceX = 0; + glyph->fAdvanceY = 0; + + uint16_t glyphId = glyph->getGlyphID(); + DWRITE_GLYPH_METRICS gm; + HRVM(fTypeface->fDWriteFontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm), + "Could not get design metrics."); + + DWRITE_FONT_METRICS dwfm; + fTypeface->fDWriteFontFace->GetMetrics(&dwfm); + + SkScalar advanceX = SkScalarMulDiv(fRec.fTextSize, + SkIntToScalar(gm.advanceWidth), + SkIntToScalar(dwfm.designUnitsPerEm)); + + if (!(fRec.fFlags & kSubpixelPositioning_Flag)) { + advanceX = SkScalarRoundToScalar(advanceX); + } + + SkVector vecs[1] = { { advanceX, 0 } }; + SkMatrix mat; + fRec.getMatrixFrom2x2(&mat); + mat.mapVectors(vecs, SK_ARRAY_COUNT(vecs)); + + glyph->fAdvanceX = SkScalarToFixed(vecs[0].fX); + glyph->fAdvanceY = SkScalarToFixed(vecs[0].fY); +} + +void SkScalerContext_DW::generateMetrics(SkGlyph* glyph) { + glyph->fWidth = 0; + + this->generateAdvance(glyph); + + //Measure raster size. + fXform.dx = SkFixedToFloat(glyph->getSubXFixed()); + fXform.dy = SkFixedToFloat(glyph->getSubYFixed()); + + FLOAT advance = 0; + + UINT16 glyphId = glyph->getGlyphID(); + + DWRITE_GLYPH_OFFSET offset; + offset.advanceOffset = 0.0f; + offset.ascenderOffset = 0.0f; + + DWRITE_GLYPH_RUN run; + run.glyphCount = 1; + run.glyphAdvances = &advance; + run.fontFace = fTypeface->fDWriteFontFace.get(); + run.fontEmSize = SkScalarToFloat(fRec.fTextSize); + run.bidiLevel = 0; + run.glyphIndices = &glyphId; + run.isSideways = FALSE; + run.glyphOffsets = &offset; + + IDWriteFactory* factory = get_dwrite_factory(); + SkASSERT(factory != NULL); + + const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat; + DWRITE_RENDERING_MODE renderingMode; + DWRITE_TEXTURE_TYPE textureType; + if (isBW) { + renderingMode = DWRITE_RENDERING_MODE_ALIASED; + textureType = DWRITE_TEXTURE_ALIASED_1x1; + } else { + renderingMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; + textureType = DWRITE_TEXTURE_CLEARTYPE_3x1; + } + + SkTScopedComPtr glyphRunAnalysis; + HRVM(factory->CreateGlyphRunAnalysis(&run, + 1.0f, // pixelsPerDip, + &fXform, + renderingMode, + DWRITE_MEASURING_MODE_NATURAL, + 0.0f, // baselineOriginX, + 0.0f, // baselineOriginY, + &glyphRunAnalysis), + "Could not create glyph run analysis."); + + RECT bbox; + HRVM(glyphRunAnalysis->GetAlphaTextureBounds(textureType, &bbox), + "Could not get texture bounds."); + + glyph->fWidth = SkToU16(bbox.right - bbox.left); + glyph->fHeight = SkToU16(bbox.bottom - bbox.top); + glyph->fLeft = SkToS16(bbox.left); + glyph->fTop = SkToS16(bbox.top); +} + +void SkScalerContext_DW::generateFontMetrics(SkPaint::FontMetrics* mx, + SkPaint::FontMetrics* my) { + if (!(mx || my)) + return; + + if (mx) { + sk_bzero(mx, sizeof(*mx)); + } + if (my) { + sk_bzero(my, sizeof(*my)); + } + + DWRITE_FONT_METRICS dwfm; + fTypeface->fDWriteFontFace->GetMetrics(&dwfm); + + SkScalar upem = SkIntToScalar(dwfm.designUnitsPerEm); + if (mx) { + mx->fTop = -fRec.fTextSize * SkIntToScalar(dwfm.ascent) / upem; + mx->fAscent = mx->fTop; + mx->fDescent = fRec.fTextSize * SkIntToScalar(dwfm.descent) / upem; + mx->fBottom = mx->fDescent; + mx->fLeading = fRec.fTextSize * SkIntToScalar(dwfm.lineGap) / upem; + mx->fXHeight = fRec.fTextSize * SkIntToScalar(dwfm.xHeight) / upem; + mx->fUnderlineThickness = fRec.fTextSize * SkIntToScalar(dwfm.underlinePosition) / upem; + mx->fUnderlinePosition = -(fRec.fTextSize * SkIntToScalar(dwfm.underlineThickness) / upem); + + mx->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag; + mx->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag; + } + + if (my) { + my->fTop = -fRec.fTextSize * SkIntToScalar(dwfm.ascent) / upem; + my->fAscent = my->fTop; + my->fDescent = fRec.fTextSize * SkIntToScalar(dwfm.descent) / upem; + my->fBottom = my->fDescent; + my->fLeading = fRec.fTextSize * SkIntToScalar(dwfm.lineGap) / upem; + my->fXHeight = fRec.fTextSize * SkIntToScalar(dwfm.xHeight) / upem; + my->fUnderlineThickness = fRec.fTextSize * SkIntToScalar(dwfm.underlinePosition) / upem; + my->fUnderlinePosition = -(fRec.fTextSize * SkIntToScalar(dwfm.underlineThickness) / upem); + + my->fFlags |= SkPaint::FontMetrics::kUnderlineThinknessIsValid_Flag; + my->fFlags |= SkPaint::FontMetrics::kUnderlinePositionIsValid_Flag; + } +} + +/////////////////////////////////////////////////////////////////////////////// + +#include "SkColorPriv.h" + +static void bilevel_to_bw(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph) { + const int width = glyph.fWidth; + const size_t dstRB = (width + 7) >> 3; + uint8_t* SK_RESTRICT dst = static_cast(glyph.fImage); + + int byteCount = width >> 3; + int bitCount = width & 7; + + for (int y = 0; y < glyph.fHeight; ++y) { + if (byteCount > 0) { + for (int i = 0; i < byteCount; ++i) { + unsigned byte = 0; + byte |= src[0] & (1 << 7); + byte |= src[1] & (1 << 6); + byte |= src[2] & (1 << 5); + byte |= src[3] & (1 << 4); + byte |= src[4] & (1 << 3); + byte |= src[5] & (1 << 2); + byte |= src[6] & (1 << 1); + byte |= src[7] & (1 << 0); + dst[i] = byte; + src += 8; + } + } + if (bitCount > 0) { + unsigned byte = 0; + unsigned mask = 0x80; + for (int i = 0; i < bitCount; i++) { + byte |= (src[i]) & mask; + mask >>= 1; + } + dst[byteCount] = byte; + } + src += bitCount; + dst += dstRB; + } +} + +template +static void rgb_to_a8(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, const uint8_t* table8) { + const size_t dstRB = glyph.rowBytes(); + const U16CPU width = glyph.fWidth; + uint8_t* SK_RESTRICT dst = static_cast(glyph.fImage); + + for (U16CPU y = 0; y < glyph.fHeight; y++) { + for (U16CPU i = 0; i < width; i++) { + U8CPU r = *(src++); + U8CPU g = *(src++); + U8CPU b = *(src++); + dst[i] = sk_apply_lut_if((r + g + b) / 3, table8); + } + dst = (uint8_t*)((char*)dst + dstRB); + } +} + +template +static void rgb_to_lcd16(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, + const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) { + const size_t dstRB = glyph.rowBytes(); + const U16CPU width = glyph.fWidth; + uint16_t* SK_RESTRICT dst = static_cast(glyph.fImage); + + for (U16CPU y = 0; y < glyph.fHeight; y++) { + for (U16CPU i = 0; i < width; i++) { + U8CPU r = sk_apply_lut_if(*(src++), tableR); + U8CPU g = sk_apply_lut_if(*(src++), tableG); + U8CPU b = sk_apply_lut_if(*(src++), tableB); + dst[i] = SkPack888ToRGB16(r, g, b); + } + dst = (uint16_t*)((char*)dst + dstRB); + } +} + +template +static void rgb_to_lcd32(const uint8_t* SK_RESTRICT src, const SkGlyph& glyph, + const uint8_t* tableR, const uint8_t* tableG, const uint8_t* tableB) { + const size_t dstRB = glyph.rowBytes(); + const U16CPU width = glyph.fWidth; + SkPMColor* SK_RESTRICT dst = static_cast(glyph.fImage); + + for (U16CPU y = 0; y < glyph.fHeight; y++) { + for (U16CPU i = 0; i < width; i++) { + U8CPU r = sk_apply_lut_if(*(src++), tableR); + U8CPU g = sk_apply_lut_if(*(src++), tableG); + U8CPU b = sk_apply_lut_if(*(src++), tableB); + dst[i] = SkPackARGB32(0xFF, r, g, b); + } + dst = (SkPMColor*)((char*)dst + dstRB); + } +} + +void SkScalerContext_DW::generateImage(const SkGlyph& glyph) { + const bool isBW = SkMask::kBW_Format == fRec.fMaskFormat; + const bool isAA = !isLCD(fRec); + + //Create the mask. + const void* bits = fOffscreen.draw(glyph, isBW); + if (!bits) { + sk_bzero(glyph.fImage, glyph.computeImageSize()); + return; + } + + //Copy the mask into the glyph. + const uint8_t* src = (const uint8_t*)bits; + if (isBW) { + bilevel_to_bw(src, glyph); + } else if (isAA) { + if (fPreBlend.isApplicable()) { + rgb_to_a8(src, glyph, fPreBlend.fG); + } else { + rgb_to_a8(src, glyph, fPreBlend.fG); + } + } else if (SkMask::kLCD16_Format == glyph.fMaskFormat) { + if (fPreBlend.isApplicable()) { + rgb_to_lcd16(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); + } else { + rgb_to_lcd16(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); + } + } else { + SkASSERT(SkMask::kLCD32_Format == glyph.fMaskFormat); + if (fPreBlend.isApplicable()) { + rgb_to_lcd32(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); + } else { + rgb_to_lcd32(src, glyph, fPreBlend.fR, fPreBlend.fG, fPreBlend.fB); + } + } +} + +void SkScalerContext_DW::generatePath(const SkGlyph& glyph, SkPath* path) { + SkASSERT(&glyph && path); + + path->reset(); + + SkTScopedComPtr geometryToPath; + HRVM(SkDWriteGeometrySink::Create(path, &geometryToPath), + "Could not create geometry to path converter."); + uint16_t glyphId = glyph.getGlyphID(); + //TODO: convert to<->from DIUs? This would make a difference if hinting. + //It may not be needed, it appears that DirectWrite only hints at em size. + HRVM(fTypeface->fDWriteFontFace->GetGlyphRunOutline(SkScalarToFloat(fRec.fTextSize), + &glyphId, + NULL, //advances + NULL, //offsets + 1, //num glyphs + FALSE, //sideways + FALSE, //rtl + geometryToPath.get()), + "Could not create glyph outline."); + + SkMatrix mat; + fRec.getMatrixFrom2x2(&mat); + path->transform(mat); +} + +void DWriteFontTypeface::onGetFontDescriptor(SkFontDescriptor* desc, + bool* isLocalStream) const { + // Get the family name. + SkTScopedComPtr dwFamilyNames; + HRV(fDWriteFontFamily->GetFamilyNames(&dwFamilyNames)); + + UINT32 dwFamilyNamesLength; + HRV(dwFamilyNames->GetStringLength(0, &dwFamilyNamesLength)); + + SkSMallocWCHAR dwFamilyNameChar(dwFamilyNamesLength+1); + HRV(dwFamilyNames->GetString(0, dwFamilyNameChar.get(), dwFamilyNamesLength+1)); + + SkString utf8FamilyName; + HRV(wchar_to_skstring(dwFamilyNameChar.get(), &utf8FamilyName)); + + desc->setFamilyName(utf8FamilyName.c_str()); + *isLocalStream = SkToBool(fDWriteFontFileLoader.get()); +} + +static SkUnichar next_utf8(const void** chars) { + return SkUTF8_NextUnichar((const char**)chars); +} + +static SkUnichar next_utf16(const void** chars) { + return SkUTF16_NextUnichar((const uint16_t**)chars); +} + +static SkUnichar next_utf32(const void** chars) { + const SkUnichar** uniChars = (const SkUnichar**)chars; + SkUnichar uni = **uniChars; + *uniChars += 1; + return uni; +} + +typedef SkUnichar (*EncodingProc)(const void**); + +static EncodingProc find_encoding_proc(SkTypeface::Encoding enc) { + static const EncodingProc gProcs[] = { + next_utf8, next_utf16, next_utf32 + }; + SkASSERT((size_t)enc < SK_ARRAY_COUNT(gProcs)); + return gProcs[enc]; +} + +int DWriteFontTypeface::onCharsToGlyphs(const void* chars, Encoding encoding, + uint16_t glyphs[], int glyphCount) const +{ + if (NULL == glyphs) { + EncodingProc next_ucs4_proc = find_encoding_proc(encoding); + for (int i = 0; i < glyphCount; ++i) { + const SkUnichar c = next_ucs4_proc(&chars); + BOOL exists; + fDWriteFont->HasCharacter(c, &exists); + if (!exists) { + return i; + } + } + return glyphCount; + } + + switch (encoding) { + case SkTypeface::kUTF8_Encoding: + case SkTypeface::kUTF16_Encoding: { + static const int scratchCount = 256; + UINT32 scratch[scratchCount]; + EncodingProc next_ucs4_proc = find_encoding_proc(encoding); + for (int baseGlyph = 0; baseGlyph < glyphCount; baseGlyph += scratchCount) { + int glyphsLeft = glyphCount - baseGlyph; + int limit = SkTMin(glyphsLeft, scratchCount); + for (int i = 0; i < limit; ++i) { + scratch[i] = next_ucs4_proc(&chars); + } + fDWriteFontFace->GetGlyphIndices(scratch, limit, &glyphs[baseGlyph]); + } + break; + } + case SkTypeface::kUTF32_Encoding: { + const UINT32* utf32 = reinterpret_cast(chars); + fDWriteFontFace->GetGlyphIndices(utf32, glyphCount, glyphs); + break; + } + default: + SK_CRASH(); + } + + for (int i = 0; i < glyphCount; ++i) { + if (0 == glyphs[i]) { + return i; + } + } + return glyphCount; +} + +int DWriteFontTypeface::onCountGlyphs() const { + return fDWriteFontFace->GetGlyphCount(); +} + +int DWriteFontTypeface::onGetUPEM() const { + DWRITE_FONT_METRICS metrics; + fDWriteFontFace->GetMetrics(&metrics); + return metrics.designUnitsPerEm; +} + +class LocalizedStrings_IDWriteLocalizedStrings : public SkTypeface::LocalizedStrings { +public: + /** Takes ownership of the IDWriteLocalizedStrings. */ + explicit LocalizedStrings_IDWriteLocalizedStrings(IDWriteLocalizedStrings* strings) + : fIndex(0), fStrings(strings) + { } + + virtual bool next(SkTypeface::LocalizedString* localizedString) SK_OVERRIDE { + if (fIndex >= fStrings->GetCount()) { + return false; + } + + // String + UINT32 stringLength; + HRBM(fStrings->GetStringLength(fIndex, &stringLength), "Could not get string length."); + stringLength += 1; + + SkSMallocWCHAR wString(stringLength); + HRBM(fStrings->GetString(fIndex, wString.get(), stringLength), "Could not get string."); + + HRB(wchar_to_skstring(wString.get(), &localizedString->fString)); + + // Locale + UINT32 localeLength; + HRBM(fStrings->GetLocaleNameLength(fIndex, &localeLength), "Could not get locale length."); + localeLength += 1; + + SkSMallocWCHAR wLocale(localeLength); + HRBM(fStrings->GetLocaleName(fIndex, wLocale.get(), localeLength), "Could not get locale."); + + HRB(wchar_to_skstring(wLocale.get(), &localizedString->fLanguage)); + + ++fIndex; + return true; + } + +private: + UINT32 fIndex; + SkTScopedComPtr fStrings; +}; + +SkTypeface::LocalizedStrings* DWriteFontTypeface::onCreateFamilyNameIterator() const { + SkTScopedComPtr familyNames; + HRNM(fDWriteFontFamily->GetFamilyNames(&familyNames), "Could not obtain family names."); + + return new LocalizedStrings_IDWriteLocalizedStrings(familyNames.release()); +} + +int DWriteFontTypeface::onGetTableTags(SkFontTableTag tags[]) const { + DWRITE_FONT_FACE_TYPE type = fDWriteFontFace->GetType(); + if (type != DWRITE_FONT_FACE_TYPE_CFF && + type != DWRITE_FONT_FACE_TYPE_TRUETYPE && + type != DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) + { + return 0; + } + + int ttcIndex; + SkAutoTUnref stream(this->openStream(&ttcIndex)); + return stream.get() ? SkFontStream::GetTableTags(stream, ttcIndex, tags) : 0; +} + +class AutoDWriteTable { +public: + AutoDWriteTable(IDWriteFontFace* fontFace, UINT32 beTag) : fFontFace(fontFace), fExists(FALSE) { + // Any errors are ignored, user must check fExists anyway. + fontFace->TryGetFontTable(beTag, + reinterpret_cast(&fData), &fSize, &fLock, &fExists); + } + ~AutoDWriteTable() { + if (fExists) { + fFontFace->ReleaseFontTable(fLock); + } + } + + const uint8_t* fData; + UINT32 fSize; + BOOL fExists; +private: + // Borrowed reference, the user must ensure the fontFace stays alive. + IDWriteFontFace* fFontFace; + void* fLock; +}; + +size_t DWriteFontTypeface::onGetTableData(SkFontTableTag tag, size_t offset, + size_t length, void* data) const +{ + AutoDWriteTable table(fDWriteFontFace.get(), SkEndian_SwapBE32(tag)); + if (!table.fExists) { + return 0; + } + + if (offset > table.fSize) { + return 0; + } + size_t size = SkTMin(length, table.fSize - offset); + if (NULL != data) { + memcpy(data, table.fData + offset, size); + } + + return size; +} + +template class SkAutoIDWriteUnregister { +public: + SkAutoIDWriteUnregister(IDWriteFactory* factory, T* unregister) + : fFactory(factory), fUnregister(unregister) + { } + + ~SkAutoIDWriteUnregister() { + if (fUnregister) { + unregister(fFactory, fUnregister); + } + } + + T* detatch() { + T* old = fUnregister; + fUnregister = NULL; + return old; + } + +private: + HRESULT unregister(IDWriteFactory* factory, IDWriteFontFileLoader* unregister) { + return factory->UnregisterFontFileLoader(unregister); + } + + HRESULT unregister(IDWriteFactory* factory, IDWriteFontCollectionLoader* unregister) { + return factory->UnregisterFontCollectionLoader(unregister); + } + + IDWriteFactory* fFactory; + T* fUnregister; +}; + +static SkTypeface* create_from_stream(SkStream* stream, int ttcIndex) { + IDWriteFactory* factory = get_dwrite_factory(); + if (NULL == factory) { + return NULL; + } + + SkTScopedComPtr fontFileLoader; + HRN(StreamFontFileLoader::Create(stream, &fontFileLoader)); + HRN(factory->RegisterFontFileLoader(fontFileLoader.get())); + SkAutoIDWriteUnregister autoUnregisterFontFileLoader( + factory, fontFileLoader.get()); + + SkTScopedComPtr fontCollectionLoader; + HRN(StreamFontCollectionLoader::Create(fontFileLoader.get(), &fontCollectionLoader)); + HRN(factory->RegisterFontCollectionLoader(fontCollectionLoader.get())); + SkAutoIDWriteUnregister autoUnregisterFontCollectionLoader( + factory, fontCollectionLoader.get()); + + SkTScopedComPtr fontCollection; + HRN(factory->CreateCustomFontCollection(fontCollectionLoader.get(), NULL, 0, &fontCollection)); + + // Find the first non-simulated font which has the given ttc index. + UINT32 familyCount = fontCollection->GetFontFamilyCount(); + for (UINT32 familyIndex = 0; familyIndex < familyCount; ++familyIndex) { + SkTScopedComPtr fontFamily; + HRN(fontCollection->GetFontFamily(familyIndex, &fontFamily)); + + UINT32 fontCount = fontFamily->GetFontCount(); + for (UINT32 fontIndex = 0; fontIndex < fontCount; ++fontIndex) { + SkTScopedComPtr font; + HRN(fontFamily->GetFont(fontIndex, &font)); + if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE) { + continue; + } + + SkTScopedComPtr fontFace; + HRN(font->CreateFontFace(&fontFace)); + + UINT32 faceIndex = fontFace->GetIndex(); + if (faceIndex == ttcIndex) { + return DWriteFontTypeface::Create(fontFace.get(), font.get(), fontFamily.get(), + autoUnregisterFontFileLoader.detatch(), + autoUnregisterFontCollectionLoader.detatch()); + } + } + } + + return NULL; +} + +SkStream* DWriteFontTypeface::onOpenStream(int* ttcIndex) const { + *ttcIndex = fDWriteFontFace->GetIndex(); + + UINT32 numFiles; + HRNM(fDWriteFontFace->GetFiles(&numFiles, NULL), + "Could not get number of font files."); + if (numFiles != 1) { + return NULL; + } + + SkTScopedComPtr fontFile; + HRNM(fDWriteFontFace->GetFiles(&numFiles, &fontFile), "Could not get font files."); + + const void* fontFileKey; + UINT32 fontFileKeySize; + HRNM(fontFile->GetReferenceKey(&fontFileKey, &fontFileKeySize), + "Could not get font file reference key."); + + SkTScopedComPtr fontFileLoader; + HRNM(fontFile->GetLoader(&fontFileLoader), "Could not get font file loader."); + + SkTScopedComPtr fontFileStream; + HRNM(fontFileLoader->CreateStreamFromKey(fontFileKey, fontFileKeySize, + &fontFileStream), + "Could not create font file stream."); + + return SkNEW_ARGS(SkDWriteFontFileStream, (fontFileStream.get())); +} + +SkScalerContext* DWriteFontTypeface::onCreateScalerContext(const SkDescriptor* desc) const { + return SkNEW_ARGS(SkScalerContext_DW, (const_cast(this), desc)); +} + +void DWriteFontTypeface::onFilterRec(SkScalerContext::Rec* rec) const { + if (rec->fFlags & SkScalerContext::kLCD_BGROrder_Flag || + rec->fFlags & SkScalerContext::kLCD_Vertical_Flag) + { + rec->fMaskFormat = SkMask::kA8_Format; + } + + unsigned flagsWeDontSupport = SkScalerContext::kDevKernText_Flag | + SkScalerContext::kForceAutohinting_Flag | + SkScalerContext::kEmbeddedBitmapText_Flag | + SkScalerContext::kEmbolden_Flag | + SkScalerContext::kLCD_BGROrder_Flag | + SkScalerContext::kLCD_Vertical_Flag; + rec->fFlags &= ~flagsWeDontSupport; + + SkPaint::Hinting h = rec->getHinting(); + // DirectWrite does not provide for hinting hints. + h = SkPaint::kSlight_Hinting; + rec->setHinting(h); + +#if SK_FONT_HOST_USE_SYSTEM_SETTINGS + IDWriteFactory* factory = get_dwrite_factory(); + if (factory != NULL) { + SkTScopedComPtr defaultRenderingParams; + if (SUCCEEDED(factory->CreateRenderingParams(&defaultRenderingParams))) { + float gamma = defaultRenderingParams->GetGamma(); + rec->setDeviceGamma(gamma); + rec->setPaintGamma(gamma); + + rec->setContrast(defaultRenderingParams->GetEnhancedContrast()); + } + } +#endif +} + +/////////////////////////////////////////////////////////////////////////////// +//PDF Support + +using namespace skia_advanced_typeface_metrics_utils; + +// Construct Glyph to Unicode table. +// Unicode code points that require conjugate pairs in utf16 are not +// supported. +// TODO(arthurhsu): Add support for conjugate pairs. It looks like that may +// require parsing the TTF cmap table (platform 4, encoding 12) directly instead +// of calling GetFontUnicodeRange(). +// TODO(bungeman): This never does what anyone wants. +// What is really wanted is the text to glyphs mapping +static void populate_glyph_to_unicode(IDWriteFontFace* fontFace, + const unsigned glyphCount, + SkTDArray* glyphToUnicode) { + HRESULT hr = S_OK; + + //Do this like free type instead + UINT32 count = 0; + for (UINT32 c = 0; c < 0x10FFFF; ++c) { + UINT16 glyph; + hr = fontFace->GetGlyphIndices(&c, 1, &glyph); + if (glyph > 0) { + ++count; + } + } + + SkAutoTArray chars(count); + count = 0; + for (UINT32 c = 0; c < 0x10FFFF; ++c) { + UINT16 glyph; + hr = fontFace->GetGlyphIndices(&c, 1, &glyph); + if (glyph > 0) { + chars[count] = c; + ++count; + } + } + + SkAutoTArray glyph(count); + fontFace->GetGlyphIndices(chars.get(), count, glyph.get()); + + USHORT maxGlyph = 0; + for (USHORT j = 0; j < count; ++j) { + if (glyph[j] > maxGlyph) maxGlyph = glyph[j]; + } + + glyphToUnicode->setCount(maxGlyph+1); + for (USHORT j = 0; j < maxGlyph+1u; ++j) { + (*glyphToUnicode)[j] = 0; + } + + //'invert' + for (USHORT j = 0; j < count; ++j) { + if (glyph[j] < glyphCount && (*glyphToUnicode)[glyph[j]] == 0) { + (*glyphToUnicode)[glyph[j]] = chars[j]; + } + } +} + +static bool getWidthAdvance(IDWriteFontFace* fontFace, int gId, int16_t* advance) { + SkASSERT(advance); + + UINT16 glyphId = gId; + DWRITE_GLYPH_METRICS gm; + HRESULT hr = fontFace->GetDesignGlyphMetrics(&glyphId, 1, &gm); + + if (FAILED(hr)) { + *advance = 0; + return false; + } + + *advance = gm.advanceWidth; + return true; +} + +template class AutoTDWriteTable : public AutoDWriteTable { +public: + static const UINT32 tag = DWRITE_MAKE_OPENTYPE_TAG(T::TAG0, T::TAG1, T::TAG2, T::TAG3); + AutoTDWriteTable(IDWriteFontFace* fontFace) : AutoDWriteTable(fontFace, tag) { } + + const T* operator->() const { return reinterpret_cast(fData); } +}; + +SkAdvancedTypefaceMetrics* DWriteFontTypeface::onGetAdvancedTypefaceMetrics( + SkAdvancedTypefaceMetrics::PerGlyphInfo perGlyphInfo, + const uint32_t* glyphIDs, + uint32_t glyphIDsCount) const { + + SkAdvancedTypefaceMetrics* info = NULL; + + HRESULT hr = S_OK; + + const unsigned glyphCount = fDWriteFontFace->GetGlyphCount(); + + DWRITE_FONT_METRICS dwfm; + fDWriteFontFace->GetMetrics(&dwfm); + + info = new SkAdvancedTypefaceMetrics; + info->fEmSize = dwfm.designUnitsPerEm; + info->fMultiMaster = false; + info->fLastGlyphID = SkToU16(glyphCount - 1); + info->fStyle = 0; + + + SkTScopedComPtr familyNames; + SkTScopedComPtr faceNames; + hr = fDWriteFontFamily->GetFamilyNames(&familyNames); + hr = fDWriteFont->GetFaceNames(&faceNames); + + UINT32 familyNameLength; + hr = familyNames->GetStringLength(0, &familyNameLength); + + UINT32 faceNameLength; + hr = faceNames->GetStringLength(0, &faceNameLength); + + UINT32 size = familyNameLength+1+faceNameLength+1; + SkSMallocWCHAR wFamilyName(size); + hr = familyNames->GetString(0, wFamilyName.get(), size); + wFamilyName[familyNameLength] = L' '; + hr = faceNames->GetString(0, &wFamilyName[familyNameLength+1], size - faceNameLength + 1); + + hr = wchar_to_skstring(wFamilyName.get(), &info->fFontName); + + if (perGlyphInfo & SkAdvancedTypefaceMetrics::kToUnicode_PerGlyphInfo) { + populate_glyph_to_unicode(fDWriteFontFace.get(), glyphCount, &(info->fGlyphToUnicode)); + } + + DWRITE_FONT_FACE_TYPE fontType = fDWriteFontFace->GetType(); + if (fontType == DWRITE_FONT_FACE_TYPE_TRUETYPE || + fontType == DWRITE_FONT_FACE_TYPE_TRUETYPE_COLLECTION) { + info->fType = SkAdvancedTypefaceMetrics::kTrueType_Font; + } else { + info->fType = SkAdvancedTypefaceMetrics::kOther_Font; + info->fItalicAngle = 0; + info->fAscent = dwfm.ascent;; + info->fDescent = dwfm.descent; + info->fStemV = 0; + info->fCapHeight = dwfm.capHeight; + info->fBBox = SkIRect::MakeEmpty(); + return info; + } + + AutoTDWriteTable headTable(fDWriteFontFace.get()); + AutoTDWriteTable postTable(fDWriteFontFace.get()); + AutoTDWriteTable hheaTable(fDWriteFontFace.get()); + AutoTDWriteTable os2Table(fDWriteFontFace.get()); + if (!headTable.fExists || !postTable.fExists || !hheaTable.fExists || !os2Table.fExists) { + info->fItalicAngle = 0; + info->fAscent = dwfm.ascent;; + info->fDescent = dwfm.descent; + info->fStemV = 0; + info->fCapHeight = dwfm.capHeight; + info->fBBox = SkIRect::MakeEmpty(); + return info; + } + + //There exist CJK fonts which set the IsFixedPitch and Monospace bits, + //but have full width, latin half-width, and half-width kana. + bool fixedWidth = (postTable->isFixedPitch && + (1 == SkEndian_SwapBE16(hheaTable->numberOfHMetrics))); + //Monospace + if (fixedWidth) { + info->fStyle |= SkAdvancedTypefaceMetrics::kFixedPitch_Style; + } + //Italic + if (os2Table->version.v0.fsSelection.field.Italic) { + info->fStyle |= SkAdvancedTypefaceMetrics::kItalic_Style; + } + //Script + if (SkPanose::FamilyType::Script == os2Table->version.v0.panose.bFamilyType.value) { + info->fStyle |= SkAdvancedTypefaceMetrics::kScript_Style; + //Serif + } else if (SkPanose::FamilyType::TextAndDisplay == os2Table->version.v0.panose.bFamilyType.value && + SkPanose::Data::TextAndDisplay::SerifStyle::Triangle <= os2Table->version.v0.panose.data.textAndDisplay.bSerifStyle.value && + SkPanose::Data::TextAndDisplay::SerifStyle::NoFit != os2Table->version.v0.panose.data.textAndDisplay.bSerifStyle.value) { + info->fStyle |= SkAdvancedTypefaceMetrics::kSerif_Style; + } + + info->fItalicAngle = SkEndian_SwapBE32(postTable->italicAngle) >> 16; + + info->fAscent = SkToS16(dwfm.ascent); + info->fDescent = SkToS16(dwfm.descent); + info->fCapHeight = SkToS16(dwfm.capHeight); + + info->fBBox = SkIRect::MakeLTRB((int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMin), + (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMax), + (int32_t)SkEndian_SwapBE16((uint16_t)headTable->xMax), + (int32_t)SkEndian_SwapBE16((uint16_t)headTable->yMin)); + + //TODO: is this even desired? It seems PDF only wants this value for Type1 + //fonts, and we only get here for TrueType fonts. + info->fStemV = 0; + /* + // Figure out a good guess for StemV - Min width of i, I, !, 1. + // This probably isn't very good with an italic font. + int16_t min_width = SHRT_MAX; + info->fStemV = 0; + char stem_chars[] = {'i', 'I', '!', '1'}; + for (size_t i = 0; i < SK_ARRAY_COUNT(stem_chars); i++) { + ABC abcWidths; + if (GetCharABCWidths(hdc, stem_chars[i], stem_chars[i], &abcWidths)) { + int16_t width = abcWidths.abcB; + if (width > 0 && width < min_width) { + min_width = width; + info->fStemV = min_width; + } + } + } + */ + + // If Restricted, the font may not be embedded in a document. + // If not Restricted, the font can be embedded. + // If PreviewPrint, the embedding is read-only. + if (os2Table->version.v0.fsType.field.Restricted) { + info->fType = SkAdvancedTypefaceMetrics::kNotEmbeddable_Font; + } else if (perGlyphInfo & SkAdvancedTypefaceMetrics::kHAdvance_PerGlyphInfo) { + if (fixedWidth) { + appendRange(&info->fGlyphWidths, 0); + int16_t advance; + getWidthAdvance(fDWriteFontFace.get(), 1, &advance); + info->fGlyphWidths->fAdvance.append(1, &advance); + finishRange(info->fGlyphWidths.get(), 0, + SkAdvancedTypefaceMetrics::WidthRange::kDefault); + } else { + info->fGlyphWidths.reset( + getAdvanceData(fDWriteFontFace.get(), + glyphCount, + glyphIDs, + glyphIDsCount, + getWidthAdvance)); + } + } + + return info; +} + +/////////////////////////////////////////////////////////////////////////////// + +static void get_locale_string(IDWriteLocalizedStrings* names, const WCHAR* preferedLocale, + SkString* skname) { + UINT32 nameIndex = 0; + if (preferedLocale) { + // Ignore any errors and continue with index 0 if there is a problem. + BOOL nameExists; + names->FindLocaleName(preferedLocale, &nameIndex, &nameExists); + if (!nameExists) { + nameIndex = 0; + } + } + + UINT32 nameLength; + HRVM(names->GetStringLength(nameIndex, &nameLength), "Could not get name length."); + nameLength += 1; + + SkSMallocWCHAR name(nameLength); + HRVM(names->GetString(nameIndex, name.get(), nameLength), "Could not get string."); + + HRV(wchar_to_skstring(name.get(), skname)); +} + +SkTypeface* SkFontMgr_DirectWrite::createTypefaceFromDWriteFont( + IDWriteFontFace* fontFace, + IDWriteFont* font, + IDWriteFontFamily* fontFamily, + StreamFontFileLoader* fontFileLoader, + IDWriteFontCollectionLoader* fontCollectionLoader) const { + SkTypeface* face = FindByProcAndRef(FindByDWriteFont, font); + if (NULL == face) { + face = DWriteFontTypeface::Create(fontFace, font, fontFamily, + fontFileLoader, fontCollectionLoader); + if (face) { + Add(face, get_style(font), fontCollectionLoader != NULL); + } + } + return face; +} + +int SkFontMgr_DirectWrite::onCountFamilies() const { + return fFontCollection->GetFontFamilyCount(); +} + +void SkFontMgr_DirectWrite::onGetFamilyName(int index, SkString* familyName) const { + SkTScopedComPtr fontFamily; + HRVM(fFontCollection->GetFontFamily(index, &fontFamily), "Could not get requested family."); + + SkTScopedComPtr familyNames; + HRVM(fontFamily->GetFamilyNames(&familyNames), "Could not get family names."); + + get_locale_string(familyNames.get(), fLocaleName.get(), familyName); +} + +SkFontStyleSet* SkFontMgr_DirectWrite::onCreateStyleSet(int index) const { + SkTScopedComPtr fontFamily; + HRNM(fFontCollection->GetFontFamily(index, &fontFamily), "Could not get requested family."); + + return SkNEW_ARGS(SkFontStyleSet_DirectWrite, (this, fontFamily.get())); +} + +SkFontStyleSet* SkFontMgr_DirectWrite::onMatchFamily(const char familyName[]) const { + SkSMallocWCHAR dwFamilyName; + HRN(cstring_to_wchar(familyName, &dwFamilyName)); + + UINT32 index; + BOOL exists; + HRNM(fFontCollection->FindFamilyName(dwFamilyName.get(), &index, &exists), + "Failed while finding family by name."); + if (!exists) { + return NULL; + } + + return this->onCreateStyleSet(index); +} + +SkTypeface* SkFontMgr_DirectWrite::onMatchFamilyStyle(const char familyName[], + const SkFontStyle& fontstyle) const { + SkAutoTUnref sset(this->matchFamily(familyName)); + return sset->matchStyle(fontstyle); +} + +SkTypeface* SkFontMgr_DirectWrite::onMatchFaceStyle(const SkTypeface* familyMember, + const SkFontStyle& fontstyle) const { + SkString familyName; + SkFontStyleSet_DirectWrite sset( + this, ((DWriteFontTypeface*)familyMember)->fDWriteFontFamily.get() + ); + return sset.matchStyle(fontstyle); +} + +SkTypeface* SkFontMgr_DirectWrite::onCreateFromStream(SkStream* stream, int ttcIndex) const { + return create_from_stream(stream, ttcIndex); +} + +SkTypeface* SkFontMgr_DirectWrite::onCreateFromData(SkData* data, int ttcIndex) const { + SkAutoTUnref stream(SkNEW_ARGS(SkMemoryStream, (data))); + return this->createFromStream(stream, ttcIndex); +} + +SkTypeface* SkFontMgr_DirectWrite::onCreateFromFile(const char path[], int ttcIndex) const { + SkAutoTUnref stream(SkStream::NewFromFile(path)); + return this->createFromStream(stream, ttcIndex); +} + +HRESULT SkFontMgr_DirectWrite::getByFamilyName(const WCHAR wideFamilyName[], + IDWriteFontFamily** fontFamily) const { + UINT32 index; + BOOL exists; + HR(fFontCollection->FindFamilyName(wideFamilyName, &index, &exists)); + + if (exists) { + HR(fFontCollection->GetFontFamily(index, fontFamily)); + return S_OK; + } + return S_FALSE; +} + +HRESULT SkFontMgr_DirectWrite::getDefaultFontFamily(IDWriteFontFamily** fontFamily) const { + NONCLIENTMETRICSW metrics; + metrics.cbSize = sizeof(metrics); + if (0 == SystemParametersInfoW(SPI_GETNONCLIENTMETRICS, + sizeof(metrics), + &metrics, + 0)) { + return E_UNEXPECTED; + } + HRM(this->getByFamilyName(metrics.lfMessageFont.lfFaceName, fontFamily), + "Could not create DWrite font family from LOGFONT."); + + return S_OK; +} + +SkTypeface* SkFontMgr_DirectWrite::onLegacyCreateTypeface(const char familyName[], + unsigned styleBits) const { + SkTScopedComPtr fontFamily; + if (familyName) { + SkSMallocWCHAR wideFamilyName; + if (SUCCEEDED(cstring_to_wchar(familyName, &wideFamilyName))) { + this->getByFamilyName(wideFamilyName, &fontFamily); + } + } + + if (NULL == fontFamily.get()) { + // No family with given name, try default. + HRNM(this->getDefaultFontFamily(&fontFamily), "Could not get default font family."); + } + + SkTScopedComPtr font; + DWRITE_FONT_WEIGHT weight = (styleBits & SkTypeface::kBold) + ? DWRITE_FONT_WEIGHT_BOLD + : DWRITE_FONT_WEIGHT_NORMAL; + DWRITE_FONT_STRETCH stretch = DWRITE_FONT_STRETCH_NORMAL; + DWRITE_FONT_STYLE italic = (styleBits & SkTypeface::kItalic) + ? DWRITE_FONT_STYLE_ITALIC + : DWRITE_FONT_STYLE_NORMAL; + HRNM(fontFamily->GetFirstMatchingFont(weight, stretch, italic, &font), + "Could not get matching font."); + + SkTScopedComPtr fontFace; + HRNM(font->CreateFontFace(&fontFace), "Could not create font face."); + + return this->createTypefaceFromDWriteFont(fontFace.get(), font.get(), fontFamily.get()); +} + +/////////////////////////////////////////////////////////////////////////////// + +int SkFontStyleSet_DirectWrite::count() { + return fFontFamily->GetFontCount(); +} + +SkTypeface* SkFontStyleSet_DirectWrite::createTypeface(int index) { + SkTScopedComPtr font; + HRNM(fFontFamily->GetFont(index, &font), "Could not get font."); + + SkTScopedComPtr fontFace; + HRNM(font->CreateFontFace(&fontFace), "Could not create font face."); + + return fFontMgr->createTypefaceFromDWriteFont(fontFace.get(), font.get(), fFontFamily.get()); +} + +void SkFontStyleSet_DirectWrite::getStyle(int index, SkFontStyle* fs, SkString* styleName) { + SkTScopedComPtr font; + HRVM(fFontFamily->GetFont(index, &font), "Could not get font."); + + SkFontStyle::Slant slant; + switch (font->GetStyle()) { + case DWRITE_FONT_STYLE_NORMAL: + slant = SkFontStyle::kUpright_Slant; + break; + case DWRITE_FONT_STYLE_OBLIQUE: + case DWRITE_FONT_STYLE_ITALIC: + slant = SkFontStyle::kItalic_Slant; + break; + default: + SkASSERT(false); + } + + int weight = font->GetWeight(); + int width = font->GetStretch(); + + *fs = SkFontStyle(weight, width, slant); + + SkTScopedComPtr faceNames; + if (SUCCEEDED(font->GetFaceNames(&faceNames))) { + get_locale_string(faceNames.get(), fFontMgr->fLocaleName.get(), styleName); + } +} + +SkTypeface* SkFontStyleSet_DirectWrite::matchStyle(const SkFontStyle& pattern) { + DWRITE_FONT_STYLE slant; + switch (pattern.slant()) { + case SkFontStyle::kUpright_Slant: + slant = DWRITE_FONT_STYLE_NORMAL; + break; + case SkFontStyle::kItalic_Slant: + slant = DWRITE_FONT_STYLE_ITALIC; + break; + default: + SkASSERT(false); + } + + DWRITE_FONT_WEIGHT weight = (DWRITE_FONT_WEIGHT)pattern.weight(); + DWRITE_FONT_STRETCH width = (DWRITE_FONT_STRETCH)pattern.width(); + + SkTScopedComPtr font; + // TODO: perhaps use GetMatchingFonts and get the least simulated? + HRNM(fFontFamily->GetFirstMatchingFont(weight, width, slant, &font), + "Could not match font in family."); + + SkTScopedComPtr fontFace; + HRNM(font->CreateFontFace(&fontFace), "Could not create font face."); + + return fFontMgr->createTypefaceFromDWriteFont(fontFace.get(), font.get(), + fFontFamily.get()); +} + +/////////////////////////////////////////////////////////////////////////////// + +typedef decltype(GetUserDefaultLocaleName)* GetUserDefaultLocaleNameProc; +static HRESULT GetGetUserDefaultLocaleNameProc(GetUserDefaultLocaleNameProc* proc) { + *proc = reinterpret_cast( + GetProcAddress(LoadLibraryW(L"Kernel32.dll"), "GetUserDefaultLocaleName") + ); + if (!*proc) { + HRESULT hr = HRESULT_FROM_WIN32(GetLastError()); + if (!IS_ERROR(hr)) { + hr = ERROR_PROC_NOT_FOUND; + } + return hr; + } + return S_OK; +} + +SkFontMgr* SkFontMgr_New_DirectWrite() { + IDWriteFactory* factory = get_dwrite_factory(); + if (NULL == factory) { + return NULL; + } + + SkTScopedComPtr sysFontCollection; + HRNM(factory->GetSystemFontCollection(&sysFontCollection, FALSE), + "Could not get system font collection."); + + WCHAR localeNameStorage[LOCALE_NAME_MAX_LENGTH]; + WCHAR* localeName = NULL; + int localeNameLen = 0; + + // Dynamically load GetUserDefaultLocaleName function, as it is not available on XP. + GetUserDefaultLocaleNameProc getUserDefaultLocaleNameProc = NULL; + HRESULT hr = GetGetUserDefaultLocaleNameProc(&getUserDefaultLocaleNameProc); + if (NULL == getUserDefaultLocaleNameProc) { + SK_TRACEHR(hr, "Could not get GetUserDefaultLocaleName."); + } else { + localeNameLen = getUserDefaultLocaleNameProc(localeNameStorage, LOCALE_NAME_MAX_LENGTH); + if (localeNameLen) { + localeName = localeNameStorage; + }; + } + + return SkNEW_ARGS(SkFontMgr_DirectWrite, (sysFontCollection.get(), localeName, localeNameLen)); +}