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

Commit 33376da5 authored by Eyad Aboulouz's avatar Eyad Aboulouz
Browse files
characters wrapping down to the next line when there's enough space on the line itself.

-Fixes issue with RTL characters overflowing outside the allowable margin for some layouts

-Optimizes measurement of layouts and text by only processing Bidi if there are any Arabic characters available
as opposed to processing Bidi if there are any RTL character available. (This is because Arabic is the only RTL
language where the width of characters changes due to reshaping. Other RTL languages do not require reshaping
and hence do not require this extra processing.)

***Special thanks to Eran Mizrahi***

Change-Id: I8ca00c564c2fede51e246222eeb0e3122b0b6fec
parent 41cdd822
Loading
Loading
Loading
Loading
+24 −0
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ package android.text;
     * @param off
     * @param len
     * @return int
     * @hide
     */
    public static int reorderAndReshapeBidiText(char[] chs, char[] outputChs, int off, int len) {

@@ -64,7 +65,30 @@ package android.text;
        return reorderReshapeBidiText(chs, outputChs, off, len);
    }

    /**
     * @author: Eyad Aboulouz
     * Arabic text reshaping by by calling native reshapeArabicText function
     * @param chs
     * @param map
     * @param off
     * @param len
     * @return int
     * @hide
     */
    public static int reshapeReversedArabicText(char[] chs, char[] outputChs, int off, int len) {

        if (chs == null)
            throw new NullPointerException();

        if (off < 0 || len < 0 || off + len > chs.length)
            throw new IndexOutOfBoundsException();

        return reshapeArabicText(chs, outputChs, off, len);
    }

    private native static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo);

    private native static int reorderReshapeBidiText(char[] chs, char[] outputChs, int off, int len);

    private native static int reshapeArabicText(char[] chs, char[] outputChs, int off, int len);
}
 No newline at end of file
+121 −6
Original line number Diff line number Diff line
@@ -18,11 +18,9 @@ package android.text;

import com.android.internal.R;

import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.method.TextKeyListener.Capitalize;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.AlignmentSpan;
import android.text.style.BackgroundColorSpan;
@@ -48,9 +46,6 @@ import android.util.Printer;

import com.android.internal.util.ArrayUtils;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.regex.Pattern;
import java.util.Iterator;

@@ -1697,6 +1692,17 @@ public class TextUtils {
               (c >= 0xFE70 && c <= 0xFEFE)    ;
    }

    /**
     * @hide
     */
    private static boolean isArabicCharacter(char c) {
        //range of Arabic characters per unicode specification
        return (c >= 0x0600 && c <= 0x06FF) ||
               (c >= 0x0750 && c <= 0x077F) ||
               (c >= 0xFB50 && c <= 0xFDFF) ||
               (c >= 0xFE70 && c <= 0xFEFE)    ;
    }

    /**
     * function to check if text range has RTL characters.
     * @hide
@@ -1714,6 +1720,40 @@ public class TextUtils {
        return false;
    }

    /**
     * function to check if text range has Arabic characters.
     * @hide
     */
    private static boolean hasArabicCharacters(final char[] text, int start, int end) {
        if (text == null)
            return false;

        //go through all characters
        for (int i = start; i < end; i++) {
            if (isArabicCharacter(text[i]))
                return true;
        }

        return false;
    }

    /**
     * function to check if text range has Arabic characters.
     * @hide
     */
    public static boolean hasArabicCharacters(String text, int start, int end) {
        if (text == null)
            return false;

        //go through all characters
        for (int i = start; i < end; i++) {
            if (isArabicCharacter(text.charAt(i)))
                return true;
        }

        return false;
    }

    /**
     * function to check if text range has RTL characters.
     * @hide
@@ -1741,6 +1781,16 @@ public class TextUtils {
        return src == null ? null : TextUtils.processBidi(src, 0, src.length());
    }

    /**
     * function to reshape the given text
     * @param src
     * @return String
     * @hide
     */
    public static String reshapeArabic(final String src) {
        return src == null ? null : TextUtils.reshapeArabic(src, 0, src.length());
    }

    /**
     * function to process bidi the given text
     * @param src
@@ -1751,6 +1801,16 @@ public class TextUtils {
        return src == null ? null : TextUtils.processBidi(src, 0, src.length);
    }

    /**
     * function to reshape the given text
     * @param src
     * @return char[]
     * @hide
     */
    public static char[] reshapeArabic(final char[] src) {
        return src == null ? null : TextUtils.reshapeArabicChars(src, 0, src.length);
    }

    /**
     * function to process bidi on the given text
     * @param src
@@ -1763,6 +1823,18 @@ public class TextUtils {
        return src != null && hasRTLCharacters(src, start, end) ? String.valueOf(TextUtils.processBidi(src.toCharArray(), start, end)) : src;
    }

    /**
     * function to reshape the given text
     * @param src
     * @param begin
     * @param end
     * @return String
     * @hide
     */
    public static String reshapeArabic(final String src, int start, int end) {
        return src != null && hasArabicCharacters(src, start, end) ? String.valueOf(TextUtils.reshapeArabicChars(src.toCharArray(), start, end)) : src;
    }

    /**
     * function to process bidi on the given text
     * @author: Eyad Aboulouz
@@ -1773,7 +1845,20 @@ public class TextUtils {
     * @hide
     */
    public static char[] processBidi(final char[] src, int start, int end) {
        return src != null && hasRTLCharacters(src, start, end) ? processBidiChars(src, start, end) : src;
        return src != null && hasRTLCharacters(src, start, end) ? TextUtils.processBidiChars(src, start, end) : src;
    }

    /**
     * function to reshape the given text
     * @author: Eyad Aboulouz
     * @param src
     * @param start
     * @param end
     * @return char[]
     * @hide
     */
    public static char[] reshapeArabic(final char[] src, int start, int end) {
        return src != null && hasArabicCharacters(src, start, end) ? TextUtils.reshapeArabicChars(src, start, end) : src;
    }

    /**
@@ -1806,6 +1891,36 @@ public class TextUtils {
        }
    }

    /**
     * function to reshape arabic text
     * @author: Eyad Aboulouz
     * @param src
     * @param start
     * @param end
     * @return char[]
     * @hide
     */
    private static char[] reshapeArabicChars(final char[] src, int start, int end) {

        try {
            char[] outputTxt = new char[end-start];
            char[] ret = src.clone();

            int outputSize = AndroidBidi.reshapeReversedArabicText(ret, outputTxt, start, end-start);

            if (outputSize != (end-start))
                throw new Exception ("Error Processing Bidi Reordering And Reshaping");

            System.arraycopy(outputTxt, 0, ret, start, end-start);

            return (ret);

        } catch (Exception e) {

            return (src);
        }
    }

    private static Object sLock = new Object();
    private static char[] sTemp = null;
}
+49 −1
Original line number Diff line number Diff line
@@ -114,11 +114,59 @@ static jint reorderReshapeBidiText (JNIEnv* env, jclass c, jcharArray srcArray,
    return outputSize;
}

/*
Native Arabic text reshaping
by: Eyad Aboulouz
*/
static jint reshapeArabicText (JNIEnv* env, jclass c, jcharArray srcArray, jcharArray destArray, jint offset, jint n) {

    bool hasErrors = false;
    jint outputSize = 0;
    UChar *intermediate = new UChar[n];
    UChar *intermediate2 = new UChar[n];
    UChar *output = new UChar[n];
    UErrorCode status = U_ZERO_ERROR;

    jchar* src = env->GetCharArrayElements(srcArray, NULL);

    if (src != NULL) {

        ubidi_writeReverse (src+offset, n, intermediate, n, UBIDI_DO_MIRRORING | UBIDI_REMOVE_BIDI_CONTROLS, &status);

        if (U_SUCCESS(status)) {
            outputSize = u_shapeArabic(intermediate, n, intermediate2, n, U_SHAPE_TEXT_DIRECTION_VISUAL_LTR | U_SHAPE_LETTERS_SHAPE | U_SHAPE_LENGTH_FIXED_SPACES_AT_END, &status);

            if (U_SUCCESS(status)) {

                ubidi_writeReverse (intermediate2, n, output, n, UBIDI_REMOVE_BIDI_CONTROLS, &status);

                env->SetCharArrayRegion(destArray, 0, outputSize, output);
            } else
                hasErrors = true;
        } else
            hasErrors = true;
    } else
        hasErrors = true;

    delete [] intermediate;
    delete [] intermediate2;
    delete [] output;

    env->ReleaseCharArrayElements(srcArray, src, JNI_ABORT);

    if (hasErrors)
        jniThrowException(env, "java/lang/RuntimeException", NULL);

    return outputSize;
}

static JNINativeMethod gMethods[] = {
        { "runBidi", "(I[C[BIZ)I",
        (void*) runBidi },
        { "reorderReshapeBidiText", "([C[CII)I",
        (void*) reorderReshapeBidiText }
        (void*) reorderReshapeBidiText },
        { "reshapeArabicText", "([C[CII)I",
        (void*) reshapeArabicText }
};

int register_android_text_AndroidBidi(JNIEnv* env)
+7 −7
Original line number Diff line number Diff line
@@ -1000,7 +1000,7 @@ public class Paint {
     */
    public float measureText(char[] text, int index, int count) {

        char[] text2 = TextUtils.processBidi(text, index, index+count);
        char[] text2 = TextUtils.reshapeArabic(text, index, index+count);

        if (!mHasCompatScaling) return native_measureText(text2, index, count);
        final float oldSize = getTextSize();
@@ -1022,7 +1022,7 @@ public class Paint {
     */
    public float measureText(String text, int start, int end) {

        String text2 = TextUtils.processBidi(text, start, end);
        String text2 = TextUtils.reshapeArabic(text, start, end);

        if (!mHasCompatScaling) return native_measureText(text2, start, end);
        final float oldSize = getTextSize();
@@ -1042,7 +1042,7 @@ public class Paint {
     */
    public float measureText(String text) {

        String text2 = TextUtils.processBidi(text);
        String text2 = TextUtils.reshapeArabic(text);

        if (!mHasCompatScaling) return native_measureText(text2);
        final float oldSize = getTextSize();
@@ -1101,7 +1101,7 @@ public class Paint {
    public int breakText(char[] text, int index, int count,
                                float maxWidth, float[] measuredWidth) {

        char[] text2 = TextUtils.processBidi(text);
        char[] text2 = TextUtils.reshapeArabic(text);

        if (!mHasCompatScaling) {
            return native_breakText(text2, index, count, maxWidth, measuredWidth);
@@ -1176,7 +1176,7 @@ public class Paint {
    public int breakText(String text, boolean measureForwards,
                                float maxWidth, float[] measuredWidth) {

        String text2 = TextUtils.processBidi(text);
        String text2 = TextUtils.reshapeArabic(text);

        if (!mHasCompatScaling) {
            return native_breakText(text2, measureForwards, maxWidth, measuredWidth);
@@ -1210,7 +1210,7 @@ public class Paint {
            throw new ArrayIndexOutOfBoundsException();
        }

        char[] text2 = TextUtils.processBidi(text, index, index+count);
        char[] text2 = TextUtils.reshapeArabic(text, index, index+count);

        if (!mHasCompatScaling) {
            return native_getTextWidths(mNativePaint, text2, index, count, widths);
@@ -1275,7 +1275,7 @@ public class Paint {
            throw new ArrayIndexOutOfBoundsException();
        }

        String text2 = TextUtils.processBidi(text, start, end);
        String text2 = TextUtils.reshapeArabic(text, start, end);

        if (!mHasCompatScaling) {
            return native_getTextWidths(mNativePaint, text2, start, end, widths);