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);
}