From 5f11abd31fa8cfa723f54bd1c98ce4e27e7d3c77 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Thu, 23 Oct 2014 14:54:42 -0700 Subject: Silently ignore invalid rangeOffset values Some fonts contain a cmap segment for char 0xffff that contains an invalid rangeOffset. This was rejected by the existing code, which means the font is considered to have empty Unicode coverage. This patch just discards the invalid segment (consistent with OpenType Sanitizer), making the custom font display. Bug: 18106256 Change-Id: Icc8616a3030f80e62db906332be64d434ae72ea2 --- libs/minikin/CmapCoverage.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/libs/minikin/CmapCoverage.cpp b/libs/minikin/CmapCoverage.cpp index 4156d69..7503372 100644 --- a/libs/minikin/CmapCoverage.cpp +++ b/libs/minikin/CmapCoverage.cpp @@ -16,9 +16,8 @@ // Determine coverage of font given its raw "cmap" OpenType table -#ifdef PRINTF_DEBUG -#include -#endif +#define LOG_TAG "Minikin" +#include #include using std::vector; @@ -38,8 +37,8 @@ static uint32_t readU32(const uint8_t* data, size_t offset) { } static void addRange(vector &coverage, uint32_t start, uint32_t end) { -#ifdef PRINTF_DEBUG - printf("adding range %d-%d\n", start, end); +#ifdef VERBOSE_DEBUG + ALOGD("adding range %d-%d\n", start, end); #endif if (coverage.empty() || coverage.back() < start) { coverage.push_back(start); @@ -82,7 +81,8 @@ static bool getCoverageFormat4(vector& coverage, const uint8_t* data, uint32_t actualRangeOffset = kHeaderSize + 6 * segCount + rangeOffset + (i + j - start) * 2; if (actualRangeOffset + 2 > size) { - return false; + // invalid rangeOffset is considered a "warning" by OpenType Sanitizer + continue; } int glyphId = readU16(data, actualRangeOffset); if (glyphId != 0) { @@ -146,8 +146,8 @@ bool CmapCoverage::getCoverage(SparseBitSet& coverage, const uint8_t* cmap_data, bestTable = i; } } -#ifdef PRINTF_DEBUG - printf("best table = %d\n", bestTable); +#ifdef VERBOSE_DEBUG + ALOGD("best table = %d\n", bestTable); #endif if (bestTable < 0) { return false; @@ -168,10 +168,11 @@ bool CmapCoverage::getCoverage(SparseBitSet& coverage, const uint8_t* cmap_data, if (success) { coverage.initFromRanges(&coverageVec.front(), coverageVec.size() >> 1); } -#ifdef PRINTF_DEBUG - for (int i = 0; i < coverageVec.size(); i += 2) { - printf("%x:%x\n", coverageVec[i], coverageVec[i + 1]); +#ifdef VERBOSE_DEBUG + for (size_t i = 0; i < coverageVec.size(); i += 2) { + ALOGD("%x:%x\n", coverageVec[i], coverageVec[i + 1]); } + ALOGD("success = %d", success); #endif return success; } -- cgit v1.2.3 From 253320d25fccbb49621926d49dcf5ef64cf529c6 Mon Sep 17 00:00:00 2001 From: Raph Levien Date: Wed, 29 Oct 2014 11:04:04 -0700 Subject: Move coverage bitmap from FontCollection to FontFamily This will significantly reduce memory usage and also speed the creation of new font families. In particular, the coverage bitmaps for the fonts in the fallback stack will be computed once in the Zygote, rather than separately in each app process. Bug: 17756900 Change-Id: I66f5706bddd4658d78fe5b709f7251ca9d2ff4f8 --- include/minikin/FontCollection.h | 12 ++---- include/minikin/FontFamily.h | 7 ++++ libs/minikin/FontCollection.cpp | 83 ++++++++++++++++------------------------ libs/minikin/FontFamily.cpp | 21 ++++++++++ 4 files changed, 63 insertions(+), 60 deletions(-) diff --git a/include/minikin/FontCollection.h b/include/minikin/FontCollection.h index 12700c6..ffdb4d1 100644 --- a/include/minikin/FontCollection.h +++ b/include/minikin/FontCollection.h @@ -21,7 +21,6 @@ #include #include -#include #include namespace android { @@ -52,17 +51,12 @@ private: static const int kLogCharsPerPage = 8; static const int kPageMask = (1 << kLogCharsPerPage) - 1; - struct FontInstance { - SparseBitSet* mCoverage; - FontFamily* mFamily; - }; - struct Range { size_t start; size_t end; }; - const FontInstance* getInstanceForChar(uint32_t ch, FontLanguage lang, int variant) const; + FontFamily* getFamilyForChar(uint32_t ch, FontLanguage lang, int variant) const; // static for allocating unique id's static uint32_t sNextId; @@ -74,10 +68,10 @@ private: uint32_t mMaxChar; // This vector has ownership of the bitsets and typeface objects. - std::vector mInstances; + std::vector mFamilies; // This vector contains pointers into mInstances - std::vector mInstanceVec; + std::vector mFamilyVec; // These are offsets into mInstanceVec, one range per page std::vector mRanges; diff --git a/include/minikin/FontFamily.h b/include/minikin/FontFamily.h index bcc2e3a..08c7a2c 100644 --- a/include/minikin/FontFamily.h +++ b/include/minikin/FontFamily.h @@ -23,6 +23,7 @@ #include #include +#include namespace android { @@ -139,6 +140,9 @@ public: size_t getNumFonts() const; MinikinFont* getFont(size_t index) const; FontStyle getStyle(size_t index) const; + + // Get Unicode coverage. Lifetime of returned bitset is same as receiver. + const SparseBitSet* getCoverage(); private: void addFontLocked(MinikinFont* typeface, FontStyle style); @@ -152,6 +156,9 @@ private: FontLanguage mLang; int mVariant; std::vector mFonts; + + SparseBitSet mCoverage; + bool mCoverageValid; }; } // namespace android diff --git a/libs/minikin/FontCollection.cpp b/libs/minikin/FontCollection.cpp index ca5b1d1..7b6b950 100644 --- a/libs/minikin/FontCollection.cpp +++ b/libs/minikin/FontCollection.cpp @@ -23,7 +23,6 @@ #include "unicode/unorm2.h" #include "MinikinInternal.h" -#include #include using std::vector; @@ -54,28 +53,12 @@ FontCollection::FontCollection(const vector& typefaces) : continue; } family->RefLocked(); - FontInstance dummy; - mInstances.push_back(dummy); // emplace_back would be better - FontInstance* instance = &mInstances.back(); - instance->mFamily = family; - instance->mCoverage = new SparseBitSet; -#ifdef VERBOSE_DEBUG - ALOGD("closest match = %p, family size = %d\n", typeface, family->getNumFonts()); -#endif - const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p'); - size_t cmapSize = 0; - bool ok = typeface->GetTable(cmapTag, NULL, &cmapSize); - UniquePtr cmapData(new uint8_t[cmapSize]); - ok = typeface->GetTable(cmapTag, cmapData.get(), &cmapSize); - CmapCoverage::getCoverage(*instance->mCoverage, cmapData.get(), cmapSize); -#ifdef VERBOSE_DEBUG - ALOGD("font coverage length=%d, first ch=%x\n", instance->mCoverage->length(), - instance->mCoverage->nextSetBit(0)); -#endif - mMaxChar = max(mMaxChar, instance->mCoverage->length()); - lastChar.push_back(instance->mCoverage->nextSetBit(0)); + mFamilies.push_back(family); // emplace_back would be better + const SparseBitSet* coverage = family->getCoverage(); + mMaxChar = max(mMaxChar, coverage->length()); + lastChar.push_back(coverage->nextSetBit(0)); } - nTypefaces = mInstances.size(); + nTypefaces = mFamilies.size(); LOG_ALWAYS_FATAL_IF(nTypefaces == 0, "Font collection must have at least one valid typeface"); size_t nPages = (mMaxChar + kPageMask) >> kLogCharsPerPage; @@ -90,10 +73,10 @@ FontCollection::FontCollection(const vector& typefaces) : range->start = offset; for (size_t j = 0; j < nTypefaces; j++) { if (lastChar[j] < (i + 1) << kLogCharsPerPage) { - const FontInstance* instance = &mInstances[j]; - mInstanceVec.push_back(instance); + FontFamily* family = mFamilies[j]; + mFamilyVec.push_back(family); offset++; - uint32_t nextChar = instance->mCoverage->nextSetBit((i + 1) << kLogCharsPerPage); + uint32_t nextChar = family->getCoverage()->nextSetBit((i + 1) << kLogCharsPerPage); #ifdef VERBOSE_DEBUG ALOGD("nextChar = %d (j = %d)\n", nextChar, j); #endif @@ -105,9 +88,8 @@ FontCollection::FontCollection(const vector& typefaces) : } FontCollection::~FontCollection() { - for (size_t i = 0; i < mInstances.size(); i++) { - delete mInstances[i].mCoverage; - mInstances[i].mFamily->UnrefLocked(); + for (size_t i = 0; i < mFamilies.size(); i++) { + mFamilies[i]->UnrefLocked(); } } @@ -117,7 +99,7 @@ FontCollection::~FontCollection() { // 3. If a font matches just language, it gets a score of 2. // 4. Matching the "compact" or "elegant" variant adds one to the score. // 5. Highest score wins, with ties resolved to the first font. -const FontCollection::FontInstance* FontCollection::getInstanceForChar(uint32_t ch, +FontFamily* FontCollection::getFamilyForChar(uint32_t ch, FontLanguage lang, int variant) const { if (ch >= mMaxChar) { return NULL; @@ -126,15 +108,14 @@ const FontCollection::FontInstance* FontCollection::getInstanceForChar(uint32_t #ifdef VERBOSE_DEBUG ALOGD("querying range %d:%d\n", range.start, range.end); #endif - const FontInstance* bestInstance = NULL; + FontFamily* bestFamily = NULL; int bestScore = -1; for (size_t i = range.start; i < range.end; i++) { - const FontInstance* instance = mInstanceVec[i]; - if (instance->mCoverage->get(ch)) { - FontFamily* family = instance->mFamily; + FontFamily* family = mFamilyVec[i]; + if (family->getCoverage()->get(ch)) { // First font family in collection always matches - if (mInstances[0].mFamily == family) { - return instance; + if (mFamilies[0] == family) { + return family; } int score = lang.match(family->lang()) * 2; if (variant != 0 && variant == family->variant()) { @@ -142,11 +123,11 @@ const FontCollection::FontInstance* FontCollection::getInstanceForChar(uint32_t } if (score > bestScore) { bestScore = score; - bestInstance = instance; + bestFamily = family; } } } - if (bestInstance == NULL && !mInstanceVec.empty()) { + if (bestFamily == NULL && !mFamilyVec.empty()) { UErrorCode errorCode = U_ZERO_ERROR; const UNormalizer2* normalizer = unorm2_getNFDInstance(&errorCode); if (U_SUCCESS(errorCode)) { @@ -155,12 +136,12 @@ const FontCollection::FontInstance* FontCollection::getInstanceForChar(uint32_t if (U_SUCCESS(errorCode) && len > 0) { int off = 0; U16_NEXT_UNSAFE(decomposed, off, ch); - return getInstanceForChar(ch, lang, variant); + return getFamilyForChar(ch, lang, variant); } } - bestInstance = &mInstances[0]; + bestFamily = mFamilies[0]; } - return bestInstance; + return bestFamily; } const uint32_t NBSP = 0xa0; @@ -183,7 +164,7 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty vector* result) const { FontLanguage lang = style.getLanguage(); int variant = style.getVariant(); - const FontInstance* lastInstance = NULL; + FontFamily* lastFamily = NULL; Run* run = NULL; int nShorts; for (size_t i = 0; i < string_size; i += nShorts) { @@ -197,17 +178,17 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty } } // Continue using existing font as long as it has coverage and is whitelisted - if (lastInstance == NULL - || !(isStickyWhitelisted(ch) && lastInstance->mCoverage->get(ch))) { - const FontInstance* instance = getInstanceForChar(ch, lang, variant); - if (i == 0 || instance != lastInstance) { + if (lastFamily == NULL + || !(isStickyWhitelisted(ch) && lastFamily->getCoverage()->get(ch))) { + FontFamily* family = getFamilyForChar(ch, lang, variant); + if (i == 0 || family != lastFamily) { size_t start = i; // Workaround for Emoji keycap until we implement per-cluster font // selection: if keycap is found in a different font that also // supports previous char, attach previous char to the new run. // Only handles non-surrogate characters. // Bug 7557244. - if (ch == KEYCAP && i && instance && instance->mCoverage->get(string[i - 1])) { + if (ch == KEYCAP && i && family && family->getCoverage()->get(string[i - 1])) { run->end--; if (run->start == run->end) { result->pop_back(); @@ -217,12 +198,12 @@ void FontCollection::itemize(const uint16_t *string, size_t string_size, FontSty Run dummy; result->push_back(dummy); run = &result->back(); - if (instance == NULL) { + if (family == NULL) { run->fakedFont.font = NULL; } else { - run->fakedFont = instance->mFamily->getClosestMatch(style); + run->fakedFont = family->getClosestMatch(style); } - lastInstance = instance; + lastFamily = family; run->start = start; } } @@ -235,10 +216,10 @@ MinikinFont* FontCollection::baseFont(FontStyle style) { } FakedFont FontCollection::baseFontFaked(FontStyle style) { - if (mInstances.empty()) { + if (mFamilies.empty()) { return FakedFont(); } - return mInstances[0].mFamily->getClosestMatch(style); + return mFamilies[0]->getClosestMatch(style); } uint32_t FontCollection::getId() const { diff --git a/libs/minikin/FontFamily.cpp b/libs/minikin/FontFamily.cpp index f688a33..d2e5867 100644 --- a/libs/minikin/FontFamily.cpp +++ b/libs/minikin/FontFamily.cpp @@ -23,6 +23,7 @@ #include "MinikinInternal.h" #include #include +#include #include #include @@ -128,6 +129,7 @@ void FontFamily::addFont(MinikinFont* typeface, FontStyle style) { void FontFamily::addFontLocked(MinikinFont* typeface, FontStyle style) { typeface->RefLocked(); mFonts.push_back(Font(typeface, style)); + mCoverageValid = false; } // Compute a matching metric between two styles - 0 is an exact match @@ -183,4 +185,23 @@ FontStyle FontFamily::getStyle(size_t index) const { return mFonts[index].style; } +const SparseBitSet* FontFamily::getCoverage() { + if (!mCoverageValid) { + const FontStyle defaultStyle; + MinikinFont* typeface = getClosestMatch(defaultStyle).font; + const uint32_t cmapTag = MinikinFont::MakeTag('c', 'm', 'a', 'p'); + size_t cmapSize = 0; + bool ok = typeface->GetTable(cmapTag, NULL, &cmapSize); + UniquePtr cmapData(new uint8_t[cmapSize]); + ok = typeface->GetTable(cmapTag, cmapData.get(), &cmapSize); + CmapCoverage::getCoverage(mCoverage, cmapData.get(), cmapSize); +#ifdef VERBOSE_DEBUG + ALOGD("font coverage length=%d, first ch=%x\n", mCoverage->length(), + mCoverage->nextSetBit(0)); +#endif + mCoverageValid = true; + } + return &mCoverage; +} + } // namespace android -- cgit v1.2.3