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

Commit 20c48c99 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Pass all locale information down to Minikin"

parents 4fed695e ef7cfa17
Loading
Loading
Loading
Loading
+21 −9
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.text;

import android.annotation.Nullable;
import android.graphics.Paint;
import android.os.LocaleList;
import android.text.style.LeadingMarginSpan;
import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
import android.text.style.LineHeightSpan;
@@ -333,6 +334,16 @@ public class StaticLayout extends Layout {
            return this;
        }

        private long[] getHyphenators(LocaleList locales) {
            final int length = locales.size();
            final long[] result = new long[length];
            for (int i = 0; i < length; i++) {
                final Locale locale = locales.get(i);
                result[i] = Hyphenator.get(locale).getNativePtr();
            }
            return result;
        }

        /**
         * Measurement and break iteration is done in native code. The protocol for using
         * the native code is as follows.
@@ -342,7 +353,7 @@ public class StaticLayout extends Layout {
         * future).
         *
         * Then, for each run within the paragraph:
         *  - setLocale (this must be done at least for the first run, optional afterwards)
         *  - setLocales (this must be done at least for the first run, optional afterwards)
         *  - one of the following, depending on the type of run:
         *    + addStyleRun (a text run, to be measured in native code)
         *    + addMeasuredRun (a run already measured in Java, passed into native code)
@@ -354,15 +365,15 @@ public class StaticLayout extends Layout {
         * After all paragraphs, call finish() to release expensive buffers.
         */

        private void setLocale(Locale locale) {
            if (!locale.equals(mLocale)) {
                nSetLocale(mNativePtr, locale.toLanguageTag(),
                        Hyphenator.get(locale).getNativePtr());
                mLocale = locale;
        private void setLocales(LocaleList locales) {
            if (!locales.equals(mLocales)) {
                nSetLocales(mNativePtr, locales.toLanguageTags(), getHyphenators(locales));
                mLocales = locales;
            }
        }

        /* package */ float addStyleRun(TextPaint paint, int start, int end, boolean isRtl) {
            setLocales(paint.getTextLocales());
            return nAddStyleRun(mNativePtr, paint.getNativeInstance(), paint.mNativeTypeface,
                    start, end, isRtl);
        }
@@ -425,7 +436,7 @@ public class StaticLayout extends Layout {
        // This will go away and be subsumed by native builder code
        MeasuredText mMeasuredText;

        Locale mLocale;
        LocaleList mLocales;

        private static final SynchronizedPool<Builder> sPool = new SynchronizedPool<Builder>(3);
    }
@@ -594,7 +605,7 @@ public class StaticLayout extends Layout {
        // store fontMetrics per span range
        // must be a multiple of 4 (and > 0) (store top, bottom, ascent, and descent per range)
        int[] fmCache = new int[4 * 4];
        b.setLocale(paint.getTextLocale());  // TODO: also respect LocaleSpan within the text
        b.setLocales(paint.getTextLocales());

        mLineCount = 0;

@@ -1308,7 +1319,8 @@ public class StaticLayout extends Layout {
    /* package */ static native long nLoadHyphenator(ByteBuffer buf, int offset,
            int minPrefix, int minSuffix);

    private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator);
    private static native void nSetLocales(long nativePtr, String locales,
            long[] nativeHyphenators);

    private static native void nSetIndents(long nativePtr, int[] indents);

+11 −7
Original line number Diff line number Diff line
@@ -137,15 +137,19 @@ static jlong nLoadHyphenator(JNIEnv* env, jclass, jobject buffer, jint offset,
    return reinterpret_cast<jlong>(hyphenator);
}

static void nSetLocale(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleName,
        jlong nativeHyphenator) {
    ScopedIcuLocale icuLocale(env, javaLocaleName);
static void nSetLocales(JNIEnv* env, jclass, jlong nativePtr, jstring javaLocaleNames,
        jlongArray nativeHyphenators) {
    minikin::LineBreaker* b = reinterpret_cast<minikin::LineBreaker*>(nativePtr);
    minikin::Hyphenator* hyphenator = reinterpret_cast<minikin::Hyphenator*>(nativeHyphenator);

    if (icuLocale.valid()) {
        b->setLocale(icuLocale.locale(), hyphenator);
    ScopedUtfChars localeNames(env, javaLocaleNames);
    ScopedLongArrayRO hyphArr(env, nativeHyphenators);
    const size_t numLocales = hyphArr.size();
    std::vector<minikin::Hyphenator*> hyphVec;
    hyphVec.reserve(numLocales);
    for (size_t i = 0; i < numLocales; i++) {
        hyphVec.push_back(reinterpret_cast<minikin::Hyphenator*>(hyphArr[i]));
    }
    b->setLocales(localeNames.c_str(), hyphVec);
}

static void nSetIndents(JNIEnv* env, jclass, jlong nativePtr, jintArray indents) {
@@ -194,7 +198,7 @@ static const JNINativeMethod gMethods[] = {
    {"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
    {"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
    {"nLoadHyphenator", "(Ljava/nio/ByteBuffer;III)J", (void*) nLoadHyphenator},
    {"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
    {"nSetLocales", "(JLjava/lang/String;[J)V", (void*) nSetLocales},
    {"nSetupParagraph", "(J[CIFIF[IIIIZ)V", (void*) nSetupParagraph},
    {"nSetIndents", "(J[I)V", (void*) nSetIndents},
    {"nAddStyleRun", "(JJJIIZ)F", (void*) nAddStyleRun},
+75 −0
Original line number Diff line number Diff line
@@ -22,10 +22,12 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import android.graphics.Paint.FontMetricsInt;
import android.os.LocaleList;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.text.Layout.Alignment;
import android.text.method.EditorState;
import android.text.style.LocaleSpan;
import android.util.Log;

import org.junit.Before;
@@ -35,6 +37,7 @@ import org.junit.runner.RunWith;
import java.text.Normalizer;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
 * Tests StaticLayout vertical metrics behavior.
@@ -671,4 +674,76 @@ public class StaticLayoutTest {
            assertEquals(testLabel, 4, layout.getOffsetToRightOf(5));
        }
    }

    @Test
    public void testLocaleSpanAffectsHyphenation() {
        TextPaint paint = new TextPaint();
        paint.setTextLocale(Locale.US);
        // Private use language, with no hyphenation rules.
        final Locale privateLocale = Locale.forLanguageTag("qaa");

        final String longWord = "philanthropic";
        final float wordWidth = paint.measureText(longWord);
        // Wide enough that words get hyphenated by default.
        final int paraWidth = Math.round(wordWidth * 1.8f);
        final String sentence = longWord + " " + longWord + " " + longWord + " " + longWord + " "
                + longWord + " " + longWord;

        final int numEnglishLines = StaticLayout.Builder
                .obtain(sentence, 0, sentence.length(), paint, paraWidth)
                .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
                .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
                .build()
                .getLineCount();

        {
            final SpannableString text = new SpannableString(sentence);
            text.setSpan(new LocaleSpan(privateLocale), 0, text.length(),
                    Spanned.SPAN_INCLUSIVE_INCLUSIVE);
            final int numPrivateLocaleLines = StaticLayout.Builder
                    .obtain(text, 0, text.length(), paint, paraWidth)
                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
                    .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
                    .build()
                    .getLineCount();

            // Since the paragraph set to English gets hyphenated, the number of lines would be
            // smaller than the number of lines when there is a span setting a language that
            // doesn't get hyphenated.
            assertTrue(numEnglishLines < numPrivateLocaleLines);
        }
        {
            // Same as the above test, except that the locale span now uses a locale list starting
            // with the private non-hyphenating locale.
            final SpannableString text = new SpannableString(sentence);
            final LocaleList locales = new LocaleList(privateLocale, Locale.US);
            text.setSpan(new LocaleSpan(locales), 0, text.length(),
                    Spanned.SPAN_INCLUSIVE_INCLUSIVE);
            final int numPrivateLocaleLines = StaticLayout.Builder
                    .obtain(text, 0, text.length(), paint, paraWidth)
                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
                    .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
                    .build()
                    .getLineCount();

            assertTrue(numEnglishLines < numPrivateLocaleLines);
        }
        {
            final SpannableString text = new SpannableString(sentence);
            // Apply the private LocaleSpan only to the first word, which is not getting hyphenated
            // anyway.
            text.setSpan(new LocaleSpan(privateLocale), 0, longWord.length(),
                    Spanned.SPAN_INCLUSIVE_INCLUSIVE);
            final int numPrivateLocaleLines = StaticLayout.Builder
                    .obtain(text, 0, text.length(), paint, paraWidth)
                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL)
                    .setBreakStrategy(Layout.BREAK_STRATEGY_HIGH_QUALITY)
                    .build()
                    .getLineCount();

            // Since the first word is not hyphenated anyway (there's enough width), the LocaleSpan
            // should not affect the layout.
            assertEquals(numEnglishLines, numPrivateLocaleLines);
        }
    }
}