Merge "Add Paint.hasGlyph method"
diff --git a/api/current.txt b/api/current.txt
index 1e57d19..380fcb4 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -11388,6 +11388,7 @@
     method public int getTextWidths(java.lang.String, float[]);
     method public android.graphics.Typeface getTypeface();
     method public android.graphics.Xfermode getXfermode();
+    method public boolean hasGlyph(java.lang.String);
     method public final boolean isAntiAlias();
     method public final boolean isDither();
     method public boolean isElegantTextHeight();
diff --git a/api/system-current.txt b/api/system-current.txt
index 544cbb3..40accaa 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -11677,6 +11677,7 @@
     method public int getTextWidths(java.lang.String, float[]);
     method public android.graphics.Typeface getTypeface();
     method public android.graphics.Xfermode getXfermode();
+    method public boolean hasGlyph(java.lang.String);
     method public final boolean isAntiAlias();
     method public final boolean isDither();
     method public boolean isElegantTextHeight();
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 4b43de3..873b516 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -23,6 +23,7 @@
 #include "GraphicsJNI.h"
 #include "core_jni_helpers.h"
 #include <ScopedUtfChars.h>
+#include <ScopedStringChars.h>
 
 #include "SkBlurDrawLooper.h"
 #include "SkColorFilter.h"
@@ -41,6 +42,8 @@
 #include "Paint.h"
 #include "TypefaceImpl.h"
 
+#include <vector>
+
 // temporary for debugging
 #include <Caches.h>
 #include <utils/Log.h>
@@ -972,6 +975,68 @@
                                       JNI_ABORT);
     }
 
+    static jboolean layoutContainsNotdef(const Layout& layout) {
+        for (size_t i = 0; i < layout.nGlyphs(); i++) {
+            if (layout.getGlyphId(i) == 0) {
+                return true;
+            }
+        }
+        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) */
+        size_t nChars = 0;
+        for (size_t i = 0; i < str.size(); i++) {
+            jchar c = str[i];
+            if (0xDC00 <= c && c <= 0xDFFF) {
+                // invalid UTF-16, unpaired trailing surrogate
+                return false;
+            } else if (0xD800 <= c && c <= 0xDBFF) {
+                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)) {
+                    // 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());
+            }
+            nChars++;
+        }
+        Layout layout;
+        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, str.get(), 0, str.size(),
+                str.size());
+        size_t nGlyphs = layout.nGlyphs();
+        if (nGlyphs != 1 && nChars > 1) {
+            // multiple-character input, and was not a ligature
+            // TODO: handle ZWJ/ZWNJ characters specially so we can detect certain ligatures
+            // in joining scripts, such as Arabic and Mongolian.
+            return false;
+        }
+        return nGlyphs > 0 && !layoutContainsNotdef(layout);
+    }
+
 };
 
 static JNINativeMethod methods[] = {
@@ -1057,6 +1122,7 @@
                                         (void*) PaintGlue::getStringBounds },
     {"nativeGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V",
                                     (void*) PaintGlue::getCharArrayBounds },
+    {"native_hasGlyph",           "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
 
     {"native_setShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer},
     {"native_hasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer}
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index b092e44..e0cbc9e 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -18,7 +18,7 @@
 #include "core_jni_helpers.h"
 
 #include "GraphicsJNI.h"
-#include <ScopedPrimitiveArray.h>
+#include "ScopedPrimitiveArray.h"
 #include "SkTypeface.h"
 #include "TypefaceImpl.h"
 #include <android_runtime/android_util_AssetManager.h>
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index d3cb9b1..cd5f59d 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -2249,6 +2249,26 @@
             bounds);
     }
 
+    /**
+     * Determine whether the typeface set on the paint has a glyph supporting the string. The
+     * simplest case is when the string contains a single character, in which this method
+     * determines whether the font has the character. In the case of multiple characters, the
+     * method returns true if there is a single glyph representing the ligature. For example, if
+     * the input is a pair of regional indicator symbols, determine whether there is an emoji flag
+     * for the pair.
+     *
+     * Finally, if the string contains a variation selector, the method only returns true if
+     * the fonts contains a glyph specific to that variation.
+     *
+     * Checking is done on the entire fallback chain, not just the immediate font referenced.
+     *
+     * @param string the string to test whether there is glyph support
+     * @return true if the typeface has a glyph for the string
+     */
+    public boolean hasGlyph(String string) {
+        return native_hasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string);
+    }
+
     @Override
     protected void finalize() throws Throwable {
         try {
@@ -2334,4 +2354,6 @@
                                                              String settings);
     private static native int native_getHyphenEdit(long native_object);
     private static native void native_setHyphenEdit(long native_object, int hyphen);
+    private static native boolean native_hasGlyph(long native_object, long native_typeface,
+            int bidiFlags, String string);
 }