/* * Copyright (C) 2013 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "Minikin" #include "minikin/FontFamily.h" #include #include #include #include "minikin/CmapCoverage.h" #include "minikin/FamilyVariant.h" #include "minikin/HbUtils.h" #include "minikin/MinikinFont.h" #include "FontUtils.h" #include "Locale.h" #include "LocaleListCache.h" #include "MinikinInternal.h" namespace minikin { FontFamily::FontFamily(std::vector>&& fonts) : FontFamily(FamilyVariant::DEFAULT, std::move(fonts)) {} FontFamily::FontFamily(FamilyVariant variant, std::vector>&& fonts) : FontFamily(kEmptyLocaleListId, variant, std::move(fonts), false /* isCustomFallback */) {} FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant, std::vector>&& fonts, bool isCustomFallback) : mLocaleListId(localeListId), mVariant(variant), mFonts(std::move(fonts)), mIsColorEmoji(LocaleListCache::getById(localeListId).getEmojiStyle() == EmojiStyle::EMOJI), mIsCustomFallback(isCustomFallback) { MINIKIN_ASSERT(!mFonts.empty(), "FontFamily must contain at least one font."); computeCoverage(); } FontFamily::FontFamily(uint32_t localeListId, FamilyVariant variant, std::vector>&& fonts, std::unordered_set&& supportedAxes, bool isColorEmoji, bool isCustomFallback, SparseBitSet&& coverage, std::vector>&& cmapFmt14Coverage) : mLocaleListId(localeListId), mVariant(variant), mFonts(std::move(fonts)), mSupportedAxes(std::move(supportedAxes)), mIsColorEmoji(isColorEmoji), mIsCustomFallback(isCustomFallback), mCoverage(std::move(coverage)), mCmapFmt14Coverage(std::move(cmapFmt14Coverage)) {} // Read fields other than mFonts, mLocaleList. // static std::shared_ptr FontFamily::readFromInternal(BufferReader* reader, std::vector>&& fonts, uint32_t localeListId) { // FamilyVariant is uint8_t static_assert(sizeof(FamilyVariant) == 1); FamilyVariant variant = reader->read(); // AxisTag is uint32_t static_assert(sizeof(AxisTag) == 4); const auto& [axesPtr, axesCount] = reader->readArray(); std::unordered_set supportedAxes(axesPtr, axesPtr + axesCount); bool isColorEmoji = static_cast(reader->read()); bool isCustomFallback = static_cast(reader->read()); SparseBitSet coverage(reader); // Read mCmapFmt14Coverage. As it can have null entries, it is stored in the buffer as a sparse // array (size, non-null entry count, array of (index, entry)). uint32_t cmapFmt14CoverageSize = reader->read(); std::vector> cmapFmt14Coverage(cmapFmt14CoverageSize); uint32_t cmapFmt14CoverageEntryCount = reader->read(); for (uint32_t i = 0; i < cmapFmt14CoverageEntryCount; i++) { uint32_t index = reader->read(); cmapFmt14Coverage[index] = std::make_unique(reader); } return std::shared_ptr(new FontFamily( localeListId, variant, std::move(fonts), std::move(supportedAxes), isColorEmoji, isCustomFallback, std::move(coverage), std::move(cmapFmt14Coverage))); } // static uint32_t FontFamily::readLocaleListInternal(BufferReader* reader) { return LocaleListCache::readFrom(reader); } // Write fields other than mFonts. void FontFamily::writeToInternal(BufferWriter* writer) const { writer->write(mVariant); std::vector axes(mSupportedAxes.begin(), mSupportedAxes.end()); // Sort axes to be deterministic. std::sort(axes.begin(), axes.end()); writer->writeArray(axes.data(), axes.size()); writer->write(mIsColorEmoji); writer->write(mIsCustomFallback); mCoverage.writeTo(writer); // Write mCmapFmt14Coverage as a sparse array (size, non-null entry count, // array of (index, entry)) writer->write(mCmapFmt14Coverage.size()); uint32_t cmapFmt14CoverageEntryCount = 0; for (const std::unique_ptr& coverage : mCmapFmt14Coverage) { if (coverage != nullptr) cmapFmt14CoverageEntryCount++; } writer->write(cmapFmt14CoverageEntryCount); for (size_t i = 0; i < mCmapFmt14Coverage.size(); i++) { if (mCmapFmt14Coverage[i] != nullptr) { writer->write(i); mCmapFmt14Coverage[i]->writeTo(writer); } } } void FontFamily::writeLocaleListInternal(BufferWriter* writer) const { LocaleListCache::writeTo(writer, mLocaleListId); } // Compute a matching metric between two styles - 0 is an exact match static int computeMatch(FontStyle style1, FontStyle style2) { if (style1 == style2) return 0; int score = abs(style1.weight() / 100 - style2.weight() / 100); if (style1.slant() != style2.slant()) { score += 2; } return score; } static FontFakery computeFakery(FontStyle wanted, FontStyle actual) { // If desired weight is semibold or darker, and 2 or more grades // higher than actual (for example, medium 500 -> bold 700), then // select fake bold. bool isFakeBold = wanted.weight() >= 600 && (wanted.weight() - actual.weight()) >= 200; bool isFakeItalic = wanted.slant() == FontStyle::Slant::ITALIC && actual.slant() == FontStyle::Slant::UPRIGHT; return FontFakery(isFakeBold, isFakeItalic); } FakedFont FontFamily::getClosestMatch(FontStyle style) const { int bestIndex = 0; Font* bestFont = mFonts[bestIndex].get(); int bestMatch = computeMatch(bestFont->style(), style); for (size_t i = 1; i < mFonts.size(); i++) { Font* font = mFonts[i].get(); int match = computeMatch(font->style(), style); if (i == 0 || match < bestMatch) { bestFont = font; bestIndex = i; bestMatch = match; } } return FakedFont{mFonts[bestIndex], computeFakery(style, bestFont->style())}; } void FontFamily::computeCoverage() { const std::shared_ptr& font = getClosestMatch(FontStyle()).font; HbBlob cmapTable(font->baseFont(), MinikinFont::MakeTag('c', 'm', 'a', 'p')); if (cmapTable.get() == nullptr) { ALOGE("Could not get cmap table size!\n"); return; } mCoverage = CmapCoverage::getCoverage(cmapTable.get(), cmapTable.size(), &mCmapFmt14Coverage); for (size_t i = 0; i < mFonts.size(); ++i) { std::unordered_set supportedAxes = mFonts[i]->getSupportedAxes(); mSupportedAxes.insert(supportedAxes.begin(), supportedAxes.end()); } } bool FontFamily::hasGlyph(uint32_t codepoint, uint32_t variationSelector) const { if (variationSelector == 0) { return mCoverage.get(codepoint); } if (mCmapFmt14Coverage.empty()) { return false; } const uint16_t vsIndex = getVsIndex(variationSelector); if (vsIndex >= mCmapFmt14Coverage.size()) { // Even if vsIndex is INVALID_VS_INDEX, we reach here since INVALID_VS_INDEX is defined to // be at the maximum end of the range. return false; } const std::unique_ptr& bitset = mCmapFmt14Coverage[vsIndex]; if (bitset.get() == nullptr) { return false; } return bitset->get(codepoint); } std::shared_ptr FontFamily::createFamilyWithVariation( const std::vector& variations) const { if (variations.empty() || mSupportedAxes.empty()) { return nullptr; } bool hasSupportedAxis = false; for (const FontVariation& variation : variations) { if (mSupportedAxes.find(variation.axisTag) != mSupportedAxes.end()) { hasSupportedAxis = true; break; } } if (!hasSupportedAxis) { // None of variation axes are suppored by this family. return nullptr; } std::vector> fonts; for (const auto& font : mFonts) { bool supportedVariations = false; std::unordered_set supportedAxes = font->getSupportedAxes(); if (!supportedAxes.empty()) { for (const FontVariation& variation : variations) { if (supportedAxes.find(variation.axisTag) != supportedAxes.end()) { supportedVariations = true; break; } } } std::shared_ptr minikinFont; if (supportedVariations) { minikinFont = font->typeface()->createFontWithVariation(variations); } if (minikinFont == nullptr) { fonts.push_back(font); } else { fonts.push_back(Font::Builder(minikinFont).setStyle(font->style()).build()); } } return std::shared_ptr( new FontFamily(mLocaleListId, mVariant, std::move(fonts), mIsCustomFallback)); } } // namespace minikin