diff options
-rw-r--r-- | core/jni/android/graphics/MinikinUtils.cpp | 5 | ||||
-rw-r--r-- | core/jni/android/graphics/MinikinUtils.h | 2 | ||||
-rw-r--r-- | core/jni/android/graphics/Paint.cpp | 38 | ||||
-rw-r--r-- | core/tests/coretests/assets/fonts/hasGlyphTestFont.ttf | bin | 0 -> 1536 bytes | |||
-rw-r--r-- | core/tests/coretests/assets/fonts/hasGlyphTestFont.ttx | 365 | ||||
-rw-r--r-- | core/tests/coretests/src/android/graphics/PaintTest.java | 62 |
6 files changed, 453 insertions, 19 deletions
diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp index 7fd288add46e..9b774b3cfc53 100644 --- a/core/jni/android/graphics/MinikinUtils.cpp +++ b/core/jni/android/graphics/MinikinUtils.cpp @@ -63,6 +63,11 @@ void MinikinUtils::doLayout(Layout* layout, const Paint* paint, int bidiFlags, layout->doLayout(buf, start, count, bufSize, bidiFlags, minikinStyle, minikinPaint); } +bool MinikinUtils::hasVariationSelector(TypefaceImpl* typeface, uint32_t codepoint, uint32_t vs) { + const TypefaceImpl* resolvedFace = TypefaceImpl_resolveDefault(typeface); + return resolvedFace->fFontCollection->hasVariationSelector(codepoint, vs); +} + float MinikinUtils::xOffsetForTextAlign(Paint* paint, const Layout& layout) { switch (paint->getTextAlign()) { case Paint::kCenter_Align: diff --git a/core/jni/android/graphics/MinikinUtils.h b/core/jni/android/graphics/MinikinUtils.h index 1ee6245f6ad7..5bf1eec4507d 100644 --- a/core/jni/android/graphics/MinikinUtils.h +++ b/core/jni/android/graphics/MinikinUtils.h @@ -40,6 +40,8 @@ public: TypefaceImpl* typeface, const uint16_t* buf, size_t start, size_t count, size_t bufSize); + static bool hasVariationSelector(TypefaceImpl* typeface, uint32_t codepoint, uint32_t vs); + static float xOffsetForTextAlign(Paint* paint, const Layout& layout); static float hOffsetForTextAlign(Paint* paint, const Layout& layout, const SkPath& path); diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index b50046fb4e28..9c11dd153871 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -39,6 +39,7 @@ #include <minikin/GraphemeBreak.h> #include <minikin/Measurement.h> +#include <unicode/utf16.h> #include "MinikinSkia.h" #include "MinikinUtils.h" #include "Paint.h" @@ -852,45 +853,44 @@ namespace PaintGlue { return false; } - static jboolean hasGlyphVariation(const Paint* paint, TypefaceImpl* typeface, jint bidiFlags, - const jchar* chars, size_t size) { - // TODO: query font for whether character has variation selector; requires a corresponding - // function in Minikin. - return false; - } - static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jlong typefaceHandle, jint bidiFlags, jstring string) { const Paint* paint = reinterpret_cast<Paint*>(paintHandle); TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); ScopedStringChars str(env, string); - /* start by rejecting variation selectors (not supported yet) */ + /* Start by rejecting unsupported base code point and variation selector pairs. */ size_t nChars = 0; + const uint32_t kStartOfString = 0xFFFFFFFF; + uint32_t prevCp = kStartOfString; for (size_t i = 0; i < str.size(); i++) { - jchar c = str[i]; - if (0xDC00 <= c && c <= 0xDFFF) { + jchar cu = str[i]; + uint32_t cp = cu; + if (U16_IS_TRAIL(cu)) { // invalid UTF-16, unpaired trailing surrogate return false; - } else if (0xD800 <= c && c <= 0xDBFF) { + } else if (U16_IS_LEAD(cu)) { if (i + 1 == str.size()) { // invalid UTF-16, unpaired leading surrogate at end of string return false; } i++; - jchar c2 = str[i]; - if (!(0xDC00 <= c2 && c2 <= 0xDFFF)) { + jchar cu2 = str[i]; + if (!U16_IS_TRAIL(cu2)) { // invalid UTF-16, unpaired leading surrogate return false; } - // UTF-16 encoding of range U+E0100..U+E01EF is DB40 DD00 .. DB40 DDEF - if (c == 0xDB40 && 0xDD00 <= c2 && c2 <= 0xDDEF) { - return hasGlyphVariation(paint, typeface, bidiFlags, str.get(), str.size()); - } - } else if (0xFE00 <= c && c <= 0xFE0F) { - return hasGlyphVariation(paint, typeface, bidiFlags, str.get(), str.size()); + cp = U16_GET_SUPPLEMENTARY(cu, cu2); + } + + if (prevCp != kStartOfString && + ((0xFE00 <= cp && cp <= 0xFE0F) || (0xE0100 <= cp && cp <= 0xE01EF)) && + !MinikinUtils::hasVariationSelector(typeface, prevCp, cp)) { + // No font has a glyph for the code point and variation selector pair. + return false; } nChars++; + prevCp = cp; } Layout layout; MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, str.get(), 0, str.size(), diff --git a/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttf b/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttf Binary files differnew file mode 100644 index 000000000000..add3f405a24f --- /dev/null +++ b/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttf diff --git a/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttx b/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttx new file mode 100644 index 000000000000..7038f4670100 --- /dev/null +++ b/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttx @@ -0,0 +1,365 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- 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. +--> +<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.0"> + + <GlyphOrder> + <!-- The 'id' attribute is only for humans; it is ignored when parsed. --> + <GlyphID id="0" name=".notdef"/> + <GlyphID id="1" name="BaseChar1"/> + <GlyphID id="2" name="BaseChar1_VS1"/> + <GlyphID id="3" name="BaseChar1_VS17"/> + <GlyphID id="4" name="BaseChar1_VS18"/> + <GlyphID id="5" name="BaseChar2"/> + <GlyphID id="6" name="BaseChar2_VS2"/> + <GlyphID id="7" name="BaseChar2_VS18"/> + <GlyphID id="8" name="BaseChar2_VS19"/> + <GlyphID id="9" name="BaseChar3"/> + <GlyphID id="10" name="BaseChar4_VS3"/> + <GlyphID id="11" name="BaseChar4_VS19"/> + <GlyphID id="12" name="BaseChar4_VS20"/> + <GlyphID id="13" name="BaseChar5"/> + <GlyphID id="14" name="BaseChar5_VS1"/> + <GlyphID id="15" name="BaseChar5_VS17"/> + <GlyphID id="16" name="BaseChar5_VS18"/> + <GlyphID id="17" name="BaseChar6"/> + <GlyphID id="18" name="BaseChar6_VS2"/> + <GlyphID id="19" name="BaseChar6_VS18"/> + <GlyphID id="20" name="BaseChar6_VS19"/> + <GlyphID id="21" name="BaseChar7"/> + <GlyphID id="22" name="BaseChar8_VS3"/> + <GlyphID id="23" name="BaseChar8_VS19"/> + <GlyphID id="24" name="BaseChar8_VS20"/> + </GlyphOrder> + + <head> + <!-- Most of this table will be recalculated by the compiler --> + <tableVersion value="1.0"/> + <fontRevision value="1.0"/> + <checkSumAdjustment value="0x640cdb2f"/> + <magicNumber value="0x5f0f3cf5"/> + <flags value="00000000 00000011"/> + <unitsPerEm value="1000"/> + <created value="Wed Sep 9 08:01:17 2015"/> + <modified value="Wed Sep 9 08:48:07 2015"/> + <xMin value="30"/> + <yMin value="-200"/> + <xMax value="629"/> + <yMax value="800"/> + <macStyle value="00000000 00000000"/> + <lowestRecPPEM value="7"/> + <fontDirectionHint value="2"/> + <indexToLocFormat value="0"/> + <glyphDataFormat value="0"/> + </head> + + <hhea> + <tableVersion value="1.0"/> + <ascent value="1000"/> + <descent value="-200"/> + <lineGap value="0"/> + <advanceWidthMax value="659"/> + <minLeftSideBearing value="0"/> + <minRightSideBearing value="30"/> + <xMaxExtent value="629"/> + <caretSlopeRise value="1"/> + <caretSlopeRun value="0"/> + <caretOffset value="0"/> + <reserved0 value="0"/> + <reserved1 value="0"/> + <reserved2 value="0"/> + <reserved3 value="0"/> + <metricDataFormat value="0"/> + <numberOfHMetrics value="18"/> + </hhea> + + <maxp> + <!-- Most of this table will be recalculated by the compiler --> + <tableVersion value="0x10000"/> + <numGlyphs value="54"/> + <maxPoints value="73"/> + <maxContours value="10"/> + <maxCompositePoints value="0"/> + <maxCompositeContours value="0"/> + <maxZones value="2"/> + <maxTwilightPoints value="12"/> + <maxStorage value="28"/> + <maxFunctionDefs value="119"/> + <maxInstructionDefs value="0"/> + <maxStackElements value="61"/> + <maxSizeOfInstructions value="2967"/> + <maxComponentElements value="0"/> + <maxComponentDepth value="0"/> + </maxp> + + <OS_2> + <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex' + will be recalculated by the compiler --> + <version value="3"/> + <xAvgCharWidth value="594"/> + <usWeightClass value="400"/> + <usWidthClass value="5"/> + <fsType value="00000000 00001000"/> + <ySubscriptXSize value="650"/> + <ySubscriptYSize value="600"/> + <ySubscriptXOffset value="0"/> + <ySubscriptYOffset value="75"/> + <ySuperscriptXSize value="650"/> + <ySuperscriptYSize value="600"/> + <ySuperscriptXOffset value="0"/> + <ySuperscriptYOffset value="350"/> + <yStrikeoutSize value="50"/> + <yStrikeoutPosition value="300"/> + <sFamilyClass value="0"/> + <panose> + <bFamilyType value="0"/> + <bSerifStyle value="0"/> + <bWeight value="5"/> + <bProportion value="0"/> + <bContrast value="0"/> + <bStrokeVariation value="0"/> + <bArmStyle value="0"/> + <bLetterForm value="0"/> + <bMidline value="0"/> + <bXHeight value="0"/> + </panose> + <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/> + <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/> + <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/> + <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/> + <achVendID value="UKWN"/> + <fsSelection value="00000000 01000000"/> + <usFirstCharIndex value="32"/> + <usLastCharIndex value="122"/> + <sTypoAscender value="800"/> + <sTypoDescender value="-200"/> + <sTypoLineGap value="200"/> + <usWinAscent value="1000"/> + <usWinDescent value="200"/> + <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/> + <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/> + <sxHeight value="500"/> + <sCapHeight value="700"/> + <usDefaultChar value="0"/> + <usBreakChar value="32"/> + <usMaxContext value="0"/> + </OS_2> + + <hmtx> + <mtx name=".notdef" width="500" lsb="93"/> + <mtx name="BaseChar1" width="500" lsb="93"/> + <mtx name="BaseChar1_VS1" width="500" lsb="93"/> + <mtx name="BaseChar1_VS17" width="500" lsb="93"/> + <mtx name="BaseChar1_VS18" width="500" lsb="93"/> + <mtx name="BaseChar2" width="500" lsb="93"/> + <mtx name="BaseChar2_VS2" width="500" lsb="93"/> + <mtx name="BaseChar2_VS18" width="500" lsb="93"/> + <mtx name="BaseChar2_VS19" width="500" lsb="93"/> + <mtx name="BaseChar3" width="500" lsb="93"/> + <mtx name="BaseChar4_VS3" width="500" lsb="93"/> + <mtx name="BaseChar4_VS19" width="500" lsb="93"/> + <mtx name="BaseChar4_VS20" width="500" lsb="93"/> + <mtx name="BaseChar5" width="500" lsb="93"/> + <mtx name="BaseChar5_VS1" width="500" lsb="93"/> + <mtx name="BaseChar5_VS17" width="500" lsb="93"/> + <mtx name="BaseChar5_VS18" width="500" lsb="93"/> + <mtx name="BaseChar6" width="500" lsb="93"/> + <mtx name="BaseChar6_VS2" width="500" lsb="93"/> + <mtx name="BaseChar6_VS18" width="500" lsb="93"/> + <mtx name="BaseChar6_VS19" width="500" lsb="93"/> + <mtx name="BaseChar7" width="500" lsb="93"/> + <mtx name="BaseChar8_VS3" width="500" lsb="93"/> + <mtx name="BaseChar8_VS19" width="500" lsb="93"/> + <mtx name="BaseChar8_VS20" width="500" lsb="93"/> + </hmtx> + + <cmap> + <tableVersion version="0"/> + <cmap_format_12 format="12" reserved="0" length="6" nGroups="1" platformID="3" platEncID="10" language="0"> + <map code="0x0061" name="BaseChar1" /> + <map code="0x0062" name="BaseChar2" /> + <map code="0x0063" name="BaseChar3" /> + <!-- No cmap4 entry for BaseChar4 --> + <map code="0x1F000" name="BaseChar5" /> + <map code="0x1F001" name="BaseChar6" /> + <map code="0x1F002" name="BaseChar7" /> + <!-- No cmap4 entry for BaseChar8 --> + </cmap_format_12> + <cmap_format_14 format="14" platformID="0" platEncID="5" length="24" numVarSelectorRecords="3"> + <map uvs="0xFE00" uv="0x0061" name="BaseChar1_VS1" /> + <map uvs="0xE0100" uv="0x0061" name="BaseChar1_VS17" /> + <map uvs="0xE0101" uv="0x0061" name="BaseChar1_VS18" /> + <map uvs="0xE0102" uv="0x0061" name="None" /> + + <map uvs="0xFE01" uv="0x0062" name="BaseChar2_VS2" /> + <map uvs="0xE0101" uv="0x0062" name="BaseChar2_VS18" /> + <map uvs="0xE0102" uv="0x0062" name="BaseChar2_VS19" /> + <map uvs="0xE0103" uv="0x0062" name="None" /> + + <map uvs="0xFE02" uv="0x0064" name="BaseChar4_VS3" /> + <map uvs="0xE0102" uv="0x0064" name="BaseChar4_VS19" /> + <map uvs="0xE0103" uv="0x0064" name="BaseChar4_VS20" /> + <!-- There is no default glyph for U+0064 U+E0104 but there is a entry for + default UVS entry. hasGlyph should return false in this + case. --> + <map uvs="0xE0104" uv="0x0064" name="None" /> + + <map uvs="0xFE00" uv="0x1F000" name="BaseChar5_VS1" /> + <map uvs="0xE0100" uv="0x1F000" name="BaseChar5_VS17" /> + <map uvs="0xE0101" uv="0x1F000" name="BaseChar5_VS18" /> + <map uvs="0xE0102" uv="0x1F000" name="None" /> + + <map uvs="0xFE01" uv="0x1F001" name="BaseChar6_VS2" /> + <map uvs="0xE0101" uv="0x1F001" name="BaseChar6_VS18" /> + <map uvs="0xE0102" uv="0x1F001" name="BaseChar6_VS19" /> + <map uvs="0xE0103" uv="0x1F001" name="None" /> + + <map uvs="0xFE02" uv="0x1F003" name="BaseChar8_VS3" /> + <map uvs="0xE0102" uv="0x1F003" name="BaseChar8_VS19" /> + <map uvs="0xE0103" uv="0x1F003" name="BaseChar8_VS20" /> + <!-- There is no default glyph for U+1F003 U+E0104 but there is a entry for + default UVS entry. hasGlyph should return false in this + case. --> + <map uvs="0xE0104" uv="0x1F003" name="None" /> + </cmap_format_14> + </cmap> + + <loca> + <!-- The 'loca' table will be calculated by the compiler --> + </loca> + + <glyf> + + <!-- The xMin, yMin, xMax and yMax values + will be recalculated by the compiler. --> + + <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + + <TTGlyph name="BaseChar1" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar1_VS1" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar1_VS17" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar1_VS18" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar2" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar2_VS2" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar2_VS18" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar2_VS19" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar3" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar4_VS3" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar4_VS19" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar4_VS20" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar5" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar5_VS1" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar5_VS17" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar5_VS18" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar6" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar6_VS2" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar6_VS18" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar6_VS19" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar7" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar8_VS3" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar8_VS19" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + <TTGlyph name="BaseChar8_VS20" xMin="0" yMin="0" xMax="0" yMax="0"> + <contour></contour><instructions><assembly></assembly></instructions> + </TTGlyph> + </glyf> + + <name> + <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True"> + Paint.hasGlyph Test + </namerecord> + <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True"> + Regular + </namerecord> + <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True"> + Paint.hasGlyph Test + </namerecord> + <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True"> + hasGlyphTestFont-Regular + </namerecord> + <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409"> + Paint.hasGlyph Test + </namerecord> + <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409"> + Regular + </namerecord> + <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409"> + hasGlyphTestFont Test + </namerecord> + <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409"> + hasGlyphTestFont-Regular + </namerecord> + </name> + + <post> + <formatType value="3.0"/> + <italicAngle value="0.0"/> + <underlinePosition value="-75"/> + <underlineThickness value="50"/> + <isFixedPitch value="0"/> + <minMemType42 value="0"/> + <maxMemType42 value="0"/> + <minMemType1 value="0"/> + <maxMemType1 value="0"/> + </post> + +</ttFont> diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java index e97bb33487c4..2a3d463463c0 100644 --- a/core/tests/coretests/src/android/graphics/PaintTest.java +++ b/core/tests/coretests/src/android/graphics/PaintTest.java @@ -20,6 +20,9 @@ import android.graphics.Paint; import android.test.InstrumentationTestCase; import android.test.suitebuilder.annotation.SmallTest; +import java.util.Arrays; +import java.util.HashSet; + /** * PaintTest tests {@link Paint}. */ @@ -94,4 +97,63 @@ public class PaintTest extends InstrumentationTestCase { testCase.mWidthWithHinting, widths); } } + + private static class HasGlyphTestCase { + public final int mBaseCodepoint; + public final HashSet<Integer> mVariationSelectors; + + public HasGlyphTestCase(int baseCodepoint, Integer[] variationSelectors) { + mBaseCodepoint = baseCodepoint; + mVariationSelectors = new HashSet<>(Arrays.asList(variationSelectors)); + } + } + + private static String codePointsToString(int[] codepoints) { + StringBuilder sb = new StringBuilder(); + for (int codepoint : codepoints) { + sb.append(Character.toChars(codepoint)); + } + return sb.toString(); + } + + public void testHasGlyph_variationSelectors() { + final Typeface fontTypeface = Typeface.createFromAsset( + getInstrumentation().getContext().getAssets(), "fonts/hasGlyphTestFont.ttf"); + Paint p = new Paint(); + p.setTypeface(fontTypeface); + + // Usually latin letters U+0061..U+0064 and Mahjong Tiles U+1F000..U+1F003 don't have + // variation selectors. This test may fail if system pre-installed fonts have a variation + // selector support for U+0061..U+0064 and U+1F000..U+1F003. + HasGlyphTestCase[] HAS_GLYPH_TEST_CASES = { + new HasGlyphTestCase(0x0061, new Integer[] {0xFE00, 0xE0100, 0xE0101, 0xE0102}), + new HasGlyphTestCase(0x0062, new Integer[] {0xFE01, 0xE0101, 0xE0102, 0xE0103}), + new HasGlyphTestCase(0x0063, new Integer[] {}), + new HasGlyphTestCase(0x0064, new Integer[] {0xFE02, 0xE0102, 0xE0103}), + + new HasGlyphTestCase(0x1F000, new Integer[] {0xFE00, 0xE0100, 0xE0101, 0xE0102}), + new HasGlyphTestCase(0x1F001, new Integer[] {0xFE01, 0xE0101, 0xE0102, 0xE0103}), + new HasGlyphTestCase(0x1F002, new Integer[] {}), + new HasGlyphTestCase(0x1F003, new Integer[] {0xFE02, 0xE0102, 0xE0103}), + }; + + for (HasGlyphTestCase testCase : HAS_GLYPH_TEST_CASES) { + for (int vs = 0xFE00; vs <= 0xE01EF; ++vs) { + // Move to variation selector supplements after variation selectors. + if (vs == 0xFF00) { + vs = 0xE0100; + } + final String signature = + "hasGlyph(U+" + Integer.toHexString(testCase.mBaseCodepoint) + + " U+" + Integer.toHexString(vs) + ")"; + final String testString = + codePointsToString(new int[] {testCase.mBaseCodepoint, vs}); + if (testCase.mVariationSelectors.contains(vs)) { + assertTrue(signature + " is expected to be true", p.hasGlyph(testString)); + } else { + assertFalse(signature + " is expected to be false", p.hasGlyph(testString)); + } + } + } + } } |