| /* |
| * Copyright 2014 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkFontMgr_indirect.h" |
| |
| #include "SkDataTable.h" |
| #include "SkFontStyle.h" |
| #include "SkOnce.h" |
| #include "SkStream.h" |
| #include "SkTSearch.h" |
| #include "SkTypeface.h" |
| |
| class SkData; |
| class SkString; |
| |
| class SkStyleSet_Indirect : public SkFontStyleSet { |
| public: |
| /** Takes ownership of the SkRemotableFontIdentitySet. */ |
| SkStyleSet_Indirect(const SkFontMgr_Indirect* owner, int familyIndex, |
| SkRemotableFontIdentitySet* data) |
| : fOwner(SkRef(owner)), fFamilyIndex(familyIndex), fData(data) |
| { } |
| |
| int count() override { return fData->count(); } |
| |
| void getStyle(int index, SkFontStyle* fs, SkString* style) override { |
| if (fs) { |
| *fs = fData->at(index).fFontStyle; |
| } |
| if (style) { |
| // TODO: is this useful? Current locale? |
| style->reset(); |
| } |
| } |
| |
| SkTypeface* createTypeface(int index) override { |
| return fOwner->createTypefaceFromFontId(fData->at(index)); |
| } |
| |
| SkTypeface* matchStyle(const SkFontStyle& pattern) override { |
| if (fFamilyIndex >= 0) { |
| SkFontIdentity id = fOwner->fProxy->matchIndexStyle(fFamilyIndex, pattern); |
| return fOwner->createTypefaceFromFontId(id); |
| } |
| |
| // If this SkStyleSet was created via onMatchFamily we would need a call like |
| // fOwner->fProxy->matchNameStyle(fFamilyName, pattern); |
| // but would not activate fonts (only consider fonts which would come back from matchName). |
| |
| // CSS policy sounds good. |
| struct Score { |
| int score; |
| int index; |
| }; |
| |
| // Width has the greatest priority. |
| // If the value of pattern.width is 5 (normal) or less, |
| // narrower width values are checked first, then wider values. |
| // If the value of pattern.width is greater than 5 (normal), |
| // wider values are checked first, followed by narrower values. |
| |
| // Italic/Oblique has the next highest priority. |
| // If italic requested and there is some italic font, use it. |
| // If oblique requested and there is some oblique font, use it. |
| // If italic requested and there is some oblique font, use it. |
| // If oblique requested and there is some italic font, use it. |
| |
| // Exact match. |
| // If pattern.weight < 400, weights below pattern.weight are checked |
| // in descending order followed by weights above pattern.weight |
| // in ascending order until a match is found. |
| // If pattern.weight > 500, weights above pattern.weight are checked |
| // in ascending order followed by weights below pattern.weight |
| // in descending order until a match is found. |
| // If pattern.weight is 400, 500 is checked first |
| // and then the rule for pattern.weight < 400 is used. |
| // If pattern.weight is 500, 400 is checked first |
| // and then the rule for pattern.weight < 400 is used |
| |
| Score maxScore = { 0, 0 }; |
| for (int i = 0; i < fData->count(); ++i) { |
| const SkFontStyle& current = fData->at(i).fFontStyle; |
| Score currentScore = { 0, i }; |
| |
| // CSS stretch. (This is the width.) |
| // This has the highest priority. |
| if (pattern.width() <= SkFontStyle::kNormal_Width) { |
| if (current.width() <= pattern.width()) { |
| currentScore.score += 10 - pattern.width() + current.width(); |
| } else { |
| currentScore.score += 10 - current.width(); |
| } |
| } else { |
| if (current.width() > pattern.width()) { |
| currentScore.score += 10 + pattern.width() - current.width(); |
| } else { |
| currentScore.score += current.width(); |
| } |
| } |
| currentScore.score *= 1002; |
| |
| // CSS style (italic/oblique) |
| // Being italic trumps all valid weights which are not italic. |
| // Note that newer specs differentiate between italic and oblique. |
| if (pattern.isItalic() && current.isItalic()) { |
| currentScore.score += 1001; |
| } |
| |
| // Synthetics (weight/style) [no stretch synthetic?] |
| |
| // The 'closer' to the target weight, the higher the score. |
| // 1000 is the 'heaviest' recognized weight |
| if (pattern.weight() == current.weight()) { |
| currentScore.score += 1000; |
| } else if (pattern.weight() <= 500) { |
| if (pattern.weight() >= 400 && pattern.weight() < 450) { |
| if (current.weight() >= 450 && current.weight() <= 500) { |
| // Artificially boost the 500 weight. |
| // TODO: determine correct number to use. |
| currentScore.score += 500; |
| } |
| } |
| if (current.weight() <= pattern.weight()) { |
| currentScore.score += 1000 - pattern.weight() + current.weight(); |
| } else { |
| currentScore.score += 1000 - current.weight(); |
| } |
| } else if (pattern.weight() > 500) { |
| if (current.weight() > pattern.weight()) { |
| currentScore.score += 1000 + pattern.weight() - current.weight(); |
| } else { |
| currentScore.score += current.weight(); |
| } |
| } |
| |
| if (currentScore.score > maxScore.score) { |
| maxScore = currentScore; |
| } |
| } |
| |
| return this->createTypeface(maxScore.index); |
| } |
| private: |
| SkAutoTUnref<const SkFontMgr_Indirect> fOwner; |
| int fFamilyIndex; |
| SkAutoTUnref<SkRemotableFontIdentitySet> fData; |
| }; |
| |
| void SkFontMgr_Indirect::set_up_family_names(const SkFontMgr_Indirect* self) { |
| self->fFamilyNames.reset(self->fProxy->getFamilyNames()); |
| } |
| |
| int SkFontMgr_Indirect::onCountFamilies() const { |
| SkOnce(&fFamilyNamesInited, &fFamilyNamesMutex, SkFontMgr_Indirect::set_up_family_names, this); |
| return fFamilyNames->count(); |
| } |
| |
| void SkFontMgr_Indirect::onGetFamilyName(int index, SkString* familyName) const { |
| SkOnce(&fFamilyNamesInited, &fFamilyNamesMutex, SkFontMgr_Indirect::set_up_family_names, this); |
| if (index >= fFamilyNames->count()) { |
| familyName->reset(); |
| return; |
| } |
| familyName->set(fFamilyNames->atStr(index)); |
| } |
| |
| SkFontStyleSet* SkFontMgr_Indirect::onCreateStyleSet(int index) const { |
| SkRemotableFontIdentitySet* set = fProxy->getIndex(index); |
| if (NULL == set) { |
| return NULL; |
| } |
| return SkNEW_ARGS(SkStyleSet_Indirect, (this, index, set)); |
| } |
| |
| SkFontStyleSet* SkFontMgr_Indirect::onMatchFamily(const char familyName[]) const { |
| return SkNEW_ARGS(SkStyleSet_Indirect, (this, -1, fProxy->matchName(familyName))); |
| } |
| |
| SkTypeface* SkFontMgr_Indirect::createTypefaceFromFontId(const SkFontIdentity& id) const { |
| if (id.fDataId == SkFontIdentity::kInvalidDataId) { |
| return NULL; |
| } |
| |
| SkAutoMutexAcquire ama(fDataCacheMutex); |
| |
| SkAutoTUnref<SkTypeface> dataTypeface; |
| int dataTypefaceIndex = 0; |
| for (int i = 0; i < fDataCache.count(); ++i) { |
| const DataEntry& entry = fDataCache[i]; |
| if (entry.fDataId == id.fDataId) { |
| if (entry.fTtcIndex == id.fTtcIndex && |
| !entry.fTypeface->weak_expired() && entry.fTypeface->try_ref()) |
| { |
| return entry.fTypeface; |
| } |
| if (dataTypeface.get() == NULL && |
| !entry.fTypeface->weak_expired() && entry.fTypeface->try_ref()) |
| { |
| dataTypeface.reset(entry.fTypeface); |
| dataTypefaceIndex = entry.fTtcIndex; |
| } |
| } |
| |
| if (entry.fTypeface->weak_expired()) { |
| fDataCache.removeShuffle(i); |
| --i; |
| } |
| } |
| |
| // No exact match, but did find a data match. |
| if (dataTypeface.get() != NULL) { |
| SkAutoTDelete<SkStreamAsset> stream(dataTypeface->openStream(NULL)); |
| if (stream.get() != NULL) { |
| return fImpl->createFromStream(stream.detach(), dataTypefaceIndex); |
| } |
| } |
| |
| // No data match, request data and add entry. |
| SkAutoTDelete<SkStreamAsset> stream(fProxy->getData(id.fDataId)); |
| if (stream.get() == NULL) { |
| return NULL; |
| } |
| |
| SkAutoTUnref<SkTypeface> typeface(fImpl->createFromStream(stream.detach(), id.fTtcIndex)); |
| if (typeface.get() == NULL) { |
| return NULL; |
| } |
| |
| DataEntry& newEntry = fDataCache.push_back(); |
| typeface->weak_ref(); |
| newEntry.fDataId = id.fDataId; |
| newEntry.fTtcIndex = id.fTtcIndex; |
| newEntry.fTypeface = typeface.get(); // weak reference passed to new entry. |
| |
| return typeface.detach(); |
| } |
| |
| SkTypeface* SkFontMgr_Indirect::onMatchFamilyStyle(const char familyName[], |
| const SkFontStyle& fontStyle) const { |
| SkFontIdentity id = fProxy->matchNameStyle(familyName, fontStyle); |
| return this->createTypefaceFromFontId(id); |
| } |
| |
| SkTypeface* SkFontMgr_Indirect::onMatchFamilyStyleCharacter(const char familyName[], |
| const SkFontStyle& style, |
| const char* bcp47[], |
| int bcp47Count, |
| SkUnichar character) const { |
| SkFontIdentity id = fProxy->matchNameStyleCharacter(familyName, style, bcp47, |
| bcp47Count, character); |
| return this->createTypefaceFromFontId(id); |
| } |
| |
| SkTypeface* SkFontMgr_Indirect::onMatchFaceStyle(const SkTypeface* familyMember, |
| const SkFontStyle& fontStyle) const { |
| SkString familyName; |
| familyMember->getFamilyName(&familyName); |
| return this->matchFamilyStyle(familyName.c_str(), fontStyle); |
| } |
| |
| SkTypeface* SkFontMgr_Indirect::onCreateFromStream(SkStreamAsset* stream, int ttcIndex) const { |
| return fImpl->createFromStream(stream, ttcIndex); |
| } |
| |
| SkTypeface* SkFontMgr_Indirect::onCreateFromFile(const char path[], int ttcIndex) const { |
| return fImpl->createFromFile(path, ttcIndex); |
| } |
| |
| SkTypeface* SkFontMgr_Indirect::onCreateFromData(SkData* data, int ttcIndex) const { |
| return fImpl->createFromData(data, ttcIndex); |
| } |
| |
| SkTypeface* SkFontMgr_Indirect::onLegacyCreateTypeface(const char familyName[], |
| unsigned styleBits) const { |
| bool bold = SkToBool(styleBits & SkTypeface::kBold); |
| bool italic = SkToBool(styleBits & SkTypeface::kItalic); |
| SkFontStyle style = SkFontStyle(bold ? SkFontStyle::kBold_Weight |
| : SkFontStyle::kNormal_Weight, |
| SkFontStyle::kNormal_Width, |
| italic ? SkFontStyle::kItalic_Slant |
| : SkFontStyle::kUpright_Slant); |
| |
| SkAutoTUnref<SkTypeface> face(this->matchFamilyStyle(familyName, style)); |
| |
| if (NULL == face.get()) { |
| face.reset(this->matchFamilyStyle(NULL, style)); |
| } |
| |
| if (NULL == face.get()) { |
| SkFontIdentity fontId = this->fProxy->matchIndexStyle(0, style); |
| face.reset(this->createTypefaceFromFontId(fontId)); |
| } |
| |
| return face.detach(); |
| } |