Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f7f969e6 authored by Raph Levien's avatar Raph Levien
Browse files

Add Paint.hasGlyph method

This patch adds a method to determine whether a typeface has a glyph
to support a particular Unicode character or sequence.

The implementation is based on shaping the text and checking for the
presence of .notdef glyphs, and counting the number of glyphs in the
ligature case. In the case of variation selector control characters, it
currently just returns false because there is no variation selector
support yet (Mongolian Free Variation Selectors work because they're
shaped with GSUB instead).

Change-Id: I8ee4ae0d6e81a0ac57f961eb02dcc35aabd87042
parent 78c926c9
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -11387,6 +11387,7 @@ package android.graphics {
    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();
+1 −0
Original line number Diff line number Diff line
@@ -11673,6 +11673,7 @@ package android.graphics {
    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();
+66 −0
Original line number Diff line number Diff line
@@ -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 @@ public:
                                      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 @@ static JNINativeMethod methods[] = {
                                        (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}
+1 −1
Original line number Diff line number Diff line
@@ -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>
+22 −0
Original line number Diff line number Diff line
@@ -2249,6 +2249,26 @@ public class Paint {
            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 @@ public class Paint {
                                                             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);
}