diff --git a/engine/src/flutter/include/minikin/FontCollection.h b/engine/src/flutter/include/minikin/FontCollection.h index ca24e386a03..09c9356e094 100644 --- a/engine/src/flutter/include/minikin/FontCollection.h +++ b/engine/src/flutter/include/minikin/FontCollection.h @@ -40,6 +40,11 @@ public: void itemize(const uint16_t *string, size_t string_length, FontStyle style, std::vector* result) const; + // Returns true if there is a glyph for the code point and variation selector pair. + // Returns false if no fonts have a glyph for the code point and variation + // selector pair, or invalid variation selector is passed. + bool hasVariationSelector(uint32_t baseCodepoint, uint32_t variationSelector) const; + // Get the base font for the given style, useful for font-wide metrics. MinikinFont* baseFont(FontStyle style); diff --git a/engine/src/flutter/libs/minikin/FontCollection.cpp b/engine/src/flutter/libs/minikin/FontCollection.cpp index 9fe0686e29f..74974137ecf 100644 --- a/engine/src/flutter/libs/minikin/FontCollection.cpp +++ b/engine/src/flutter/libs/minikin/FontCollection.cpp @@ -67,6 +67,10 @@ FontCollection::FontCollection(const vector& typefaces) : "Font collection must have at least one valid typeface"); size_t nPages = (mMaxChar + kPageMask) >> kLogCharsPerPage; size_t offset = 0; + // TODO: Use variation selector map for mRanges construction. + // A font can have a glyph for a base code point and variation selector pair but no glyph for + // the base code point without variation selector. The family won't be listed in the range in + // this case. for (size_t i = 0; i < nPages; i++) { Range dummy; mRanges.push_back(dummy); @@ -177,6 +181,24 @@ static bool isVariationSelector(uint32_t c) { return (0xFE00 <= c && c <= 0xFE0F) || (0xE0100 <= c && c <= 0xE01EF); } +bool FontCollection::hasVariationSelector(uint32_t baseCodepoint, + uint32_t variationSelector) const { + if (!isVariationSelector(variationSelector)) { + return false; + } + if (baseCodepoint >= mMaxChar) { + return false; + } + // Currently mRanges can not be used here since it isn't aware of the variation sequence. + // TODO: Use mRanges for narrowing down the search range. + for (size_t i = 0; i < mFamilies.size(); i++) { + if (mFamilies[i]->hasVariationSelector(baseCodepoint, variationSelector)) { + return true; + } + } + return false; +} + void FontCollection::itemize(const uint16_t *string, size_t string_size, FontStyle style, vector* result) const { FontLanguage lang = style.getLanguage(); diff --git a/engine/src/flutter/tests/Android.mk b/engine/src/flutter/tests/Android.mk index b6bc15c0dcf..d12dca72e3b 100644 --- a/engine/src/flutter/tests/Android.mk +++ b/engine/src/flutter/tests/Android.mk @@ -39,6 +39,7 @@ LOCAL_STATIC_LIBRARIES += \ libxml2 LOCAL_SRC_FILES += \ + FontCollectionTest.cpp \ FontCollectionItemizeTest.cpp \ FontFamilyTest.cpp \ FontTestUtils.cpp \ diff --git a/engine/src/flutter/tests/FontCollectionTest.cpp b/engine/src/flutter/tests/FontCollectionTest.cpp new file mode 100644 index 00000000000..593575617f2 --- /dev/null +++ b/engine/src/flutter/tests/FontCollectionTest.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2015 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. + */ + +#include + +#include +#include "MinikinFontForTest.h" +#include "MinikinInternal.h" + +namespace android { + +// The test font has following glyphs. +// U+82A6 +// U+82A6 U+FE00 (VS1) +// U+82A6 U+E0100 (VS17) +// U+82A6 U+E0101 (VS18) +// U+82A6 U+E0102 (VS19) +// U+845B +// U+845B U+FE01 (VS2) +// U+845B U+E0101 (VS18) +// U+845B U+E0102 (VS19) +// U+845B U+E0103 (VS20) +// U+537F +// U+717D U+FE02 (VS3) +// U+717D U+E0102 (VS19) +// U+717D U+E0103 (VS20) +const char kVsTestFont[] = "/data/minikin/test/data/VarioationSelectorTest-Regular.ttf"; + +void expectVSGlyphs(const FontCollection& fc, uint32_t codepoint, const std::set& vsSet) { + for (uint32_t vs = 0xFE00; vs <= 0xE01EF; ++vs) { + // Move to variation selectors supplements after variation selectors. + if (vs == 0xFF00) { + vs = 0xE0100; + } + if (vsSet.find(vs) == vsSet.end()) { + EXPECT_FALSE(fc.hasVariationSelector(codepoint, vs)) + << "Glyph for U+" << std::hex << codepoint << " U+" << vs; + } else { + EXPECT_TRUE(fc.hasVariationSelector(codepoint, vs)) + << "Glyph for U+" << std::hex << codepoint << " U+" << vs; + } + } +} + +TEST(FontCollectionTest, hasVariationSelectorTest) { + FontFamily* family = new FontFamily(); + family->addFont(new MinikinFontForTest(kVsTestFont)); + std::vector families({family}); + FontCollection fc(families); + family->Unref(); + + EXPECT_FALSE(fc.hasVariationSelector(0x82A6, 0)); + expectVSGlyphs(fc, 0x82A6, std::set({0xFE00, 0xE0100, 0xE0101, 0xE0102})); + + EXPECT_FALSE(fc.hasVariationSelector(0x845B, 0)); + expectVSGlyphs(fc, 0x845B, std::set({0xFE01, 0xE0101, 0xE0102, 0xE0103})); + + EXPECT_FALSE(fc.hasVariationSelector(0x537F, 0)); + expectVSGlyphs(fc, 0x537F, std::set({})); + + EXPECT_FALSE(fc.hasVariationSelector(0x717D, 0)); + expectVSGlyphs(fc, 0x717D, std::set({0xFE02, 0xE0102, 0xE0103})); +} + +} // namespace android