From bb1a96647a82df5a134bf631fc9db342e7ef61de Mon Sep 17 00:00:00 2001 From: Seigo Nonaka Date: Wed, 21 Oct 2015 23:24:25 +0900 Subject: [PATCH] Make Paint.hasGlyph variation selector aware. With this CL, Paint.hasGlyph returns true if the typeface supports the passed code point and variation selector pair. Bug: 11256006 Change-Id: If29cd0c5942dc78bd862804106e29539bdeee569 --- core/jni/android/graphics/MinikinUtils.cpp | 5 + core/jni/android/graphics/MinikinUtils.h | 2 + core/jni/android/graphics/Paint.cpp | 38 +- .../assets/fonts/hasGlyphTestFont.ttf | Bin 0 -> 1536 bytes .../assets/fonts/hasGlyphTestFont.ttx | 365 ++++++++++++++++++ .../src/android/graphics/PaintTest.java | 62 +++ 6 files changed, 453 insertions(+), 19 deletions(-) create mode 100644 core/tests/coretests/assets/fonts/hasGlyphTestFont.ttf create mode 100644 core/tests/coretests/assets/fonts/hasGlyphTestFont.ttx diff --git a/core/jni/android/graphics/MinikinUtils.cpp b/core/jni/android/graphics/MinikinUtils.cpp index 7fd288add46ef..9b774b3cfc530 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 1ee6245f6ad70..5bf1eec4507d9 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 b50046fb4e28e..9c11dd1538719 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -39,6 +39,7 @@ #include #include +#include #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(paintHandle); TypefaceImpl* typeface = reinterpret_cast(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 new file mode 100644 index 0000000000000000000000000000000000000000..add3f405a24f1d5ca772fbc2a9fbd06b92cc40d5 GIT binary patch literal 1536 zcmeHHO-NKx6#nje&vEi+v~>Dk3?vqjRuUofy9ix~HkgqRYImG*o-}_rPjnVpi-HIj zf(yY-1T9(wK_D1J5fr#-)1pnAh!!qg*e;yD^X_{Ex(Zr$hV#xj-~H~nXYM)gK>?`4 zJ=jQGy>_z89m}2r)G?yB#`C)L{;j1Mzoxp`Tx9}{)hb{QGahv_I&IG!og()+^{h+4 zd1`&2|DD?A=DpcS(s@PhL&j%v#c}OiaUOGjo5XoNTf&$66|P4aCki^BxqayFBH*kN zYcCbcUUd~+K+RWKhtx_PxPS4^Skw6*Q1&_ze2Oo9^lksZJF$V(!U{a@vDd5J+`Neq zwS*BrQ^*smeB^-dNo=73hcQF%Lj95eu&qJ0fEq-tM;8A*`ixbh=u#(yP$Lf`Qs-~E z-gjdFm!YdwJHZ;(So0beeU93+c(X{D%2?Om{iIHFa4Y*)$1Kf=5&21tVvA2-QrHSB zDq$tXv@RIr_rfize)Ne86&}>je$k4D9LphC63K6xTu*XDc)Ts>{ zdqbuz%qB+6q2-Z6tw7DPrzK^kjR(4mDS2)F(T#q3bHr}?majmZ+*#}IBZ>m*8? zlsP=WJQlHxCwPVzroY??d0+lV|M(^(T@(|+y_zX9BW$r(EdlQM(`E1oXXPN@66LVx zXy;eO_!c`m9QY2qI}!L1oWeptR58oOn>%m&$yl0u`M&dTXI#L K!WH?B?v!5|0pSe* literal 0 HcmV?d00001 diff --git a/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttx b/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttx new file mode 100644 index 0000000000000..7038f46701003 --- /dev/null +++ b/core/tests/coretests/assets/fonts/hasGlyphTestFont.ttx @@ -0,0 +1,365 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Paint.hasGlyph Test + + + Regular + + + Paint.hasGlyph Test + + + hasGlyphTestFont-Regular + + + Paint.hasGlyph Test + + + Regular + + + hasGlyphTestFont Test + + + hasGlyphTestFont-Regular + + + + + + + + + + + + + + + + diff --git a/core/tests/coretests/src/android/graphics/PaintTest.java b/core/tests/coretests/src/android/graphics/PaintTest.java index e97bb33487c47..2a3d463463c08 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 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)); + } + } + } + } }