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

Commit c4c3a0f5 authored by Roozbeh Pournader's avatar Roozbeh Pournader Committed by Android (Google) Code Review
Browse files

Merge "Support fallback linespacing in DynamicLayout"

parents 8754a2d6 15b213d5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -41414,6 +41414,7 @@ package android.text {
    method public android.text.DynamicLayout.Builder setJustificationMode(int);
    method public android.text.DynamicLayout.Builder setLineSpacing(float, float);
    method public android.text.DynamicLayout.Builder setTextDirection(android.text.TextDirectionHeuristic);
    method public android.text.DynamicLayout.Builder setUseLineSpacingFromFallbacks(boolean);
  }
  public abstract interface Editable implements java.lang.Appendable java.lang.CharSequence android.text.GetChars android.text.Spannable {
+1 −0
Original line number Diff line number Diff line
@@ -45018,6 +45018,7 @@ package android.text {
    method public android.text.DynamicLayout.Builder setJustificationMode(int);
    method public android.text.DynamicLayout.Builder setLineSpacing(float, float);
    method public android.text.DynamicLayout.Builder setTextDirection(android.text.TextDirectionHeuristic);
    method public android.text.DynamicLayout.Builder setUseLineSpacingFromFallbacks(boolean);
  }
  public abstract interface Editable implements java.lang.Appendable java.lang.CharSequence android.text.GetChars android.text.Spannable {
+1 −0
Original line number Diff line number Diff line
@@ -41678,6 +41678,7 @@ package android.text {
    method public android.text.DynamicLayout.Builder setJustificationMode(int);
    method public android.text.DynamicLayout.Builder setLineSpacing(float, float);
    method public android.text.DynamicLayout.Builder setTextDirection(android.text.TextDirectionHeuristic);
    method public android.text.DynamicLayout.Builder setUseLineSpacingFromFallbacks(boolean);
  }
  public abstract interface Editable implements java.lang.Appendable java.lang.CharSequence android.text.GetChars android.text.Spannable {
+28 −3
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ public class DynamicLayout extends Layout
            b.mSpacingMult = DEFAULT_LINESPACING_MULTIPLIER;
            b.mSpacingAdd = DEFAULT_LINESPACING_ADDITION;
            b.mIncludePad = true;
            b.mFallbackLineSpacing = false;
            b.mEllipsizedWidth = width;
            b.mEllipsize = null;
            b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
@@ -171,6 +172,25 @@ public class DynamicLayout extends Layout
            return this;
        }

        /**
         * Set whether to respect the ascent and descent of the fallback fonts that are used in
         * displaying the text (which is needed to avoid text from consecutive lines running into
         * each other). If set, fallback fonts that end up getting used can increase the ascent
         * and descent of the lines that they are used on.
         *
         * <p>For backward compatibility reasons, the default is {@code false}, but setting this to
         * true is strongly recommended. It is required to be true if text could be in languages
         * like Burmese or Tibetan where text is typically much taller or deeper than Latin text.
         *
         * @param useLineSpacingFromFallbacks whether to expand linespacing based on fallback fonts
         * @return this builder, useful for chaining
         */
        @NonNull
        public Builder setUseLineSpacingFromFallbacks(boolean useLineSpacingFromFallbacks) {
            mFallbackLineSpacing = useLineSpacingFromFallbacks;
            return this;
        }

        /**
         * Set the width as used for ellipsizing purposes, if it differs from the normal layout
         * width. The default is the {@code width} passed to {@link #obtain}.
@@ -270,6 +290,7 @@ public class DynamicLayout extends Layout
        private float mSpacingMult;
        private float mSpacingAdd;
        private boolean mIncludePad;
        private boolean mFallbackLineSpacing;
        private int mBreakStrategy;
        private int mHyphenationFrequency;
        private int mJustificationMode;
@@ -320,7 +341,7 @@ public class DynamicLayout extends Layout
                         @IntRange(from = 0) int ellipsizedWidth) {
        this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
                spacingmult, spacingadd, includepad,
                StaticLayout.BREAK_STRATEGY_SIMPLE, StaticLayout.HYPHENATION_FREQUENCY_NONE,
                Layout.BREAK_STRATEGY_SIMPLE, Layout.HYPHENATION_FREQUENCY_NONE,
                Layout.JUSTIFICATION_MODE_NONE, ellipsize, ellipsizedWidth);
    }

@@ -388,6 +409,7 @@ public class DynamicLayout extends Layout

    private void generate(@NonNull Builder b) {
        mBase = b.mBase;
        mFallbackLineSpacing = b.mFallbackLineSpacing;
        if (b.mEllipsize != null) {
            mInts = new PackedIntVector(COLUMNS_ELLIPSIZE);
            mEllipsizedWidth = b.mEllipsizedWidth;
@@ -573,6 +595,7 @@ public class DynamicLayout extends Layout
                .setWidth(getWidth())
                .setTextDirection(getTextDirectionHeuristic())
                .setLineSpacing(getSpacingAdd(), getSpacingMultiplier())
                .setUseLineSpacingFromFallbacks(mFallbackLineSpacing)
                .setEllipsizedWidth(mEllipsizedWidth)
                .setEllipsize(mEllipsizeAt)
                .setBreakStrategy(mBreakStrategy)
@@ -1033,11 +1056,12 @@ public class DynamicLayout extends Layout
        private void reflow(CharSequence s, int where, int before, int after) {
            DynamicLayout ml = mLayout.get();

            if (ml != null)
            if (ml != null) {
                ml.reflow(s, where, before, after);
            else if (s instanceof Spannable)
            } else if (s instanceof Spannable) {
                ((Spannable) s).removeSpan(this);
            }
        }

        public void beforeTextChanged(CharSequence s, int where, int before, int after) {
            // Intentionally empty
@@ -1093,6 +1117,7 @@ public class DynamicLayout extends Layout
    private CharSequence mDisplay;
    private ChangeWatcher mWatcher;
    private boolean mIncludePad;
    private boolean mFallbackLineSpacing;
    private boolean mEllipsize;
    private int mEllipsizedWidth;
    private TextUtils.TruncateAt mEllipsizeAt;
+73 −0
Original line number Diff line number Diff line
@@ -186,4 +186,77 @@ public class DynamicLayoutTest {
                ALIGN_NORMAL, 1.0f /*spacingMultiplier*/, 0f /*spacingAdd*/, false /*includepad*/);
        layout.getLineExtra(100);
    }

    @Test
    public void testFallbackLineSpacing() {
        // All glyphs in the fonts are 1em wide.
        final String[] testFontFiles = {
            // ascent == 1em, descent == 2em, only supports 'a' and space
            "ascent1em-descent2em.ttf",
            // ascent == 3em, descent == 4em, only supports 'b'
            "ascent3em-descent4em.ttf"
        };
        final String xml = "<?xml version='1.0' encoding='UTF-8'?>"
                + "<familyset>"
                + "  <family name='sans-serif'>"
                + "    <font weight='400' style='normal'>ascent1em-descent2em.ttf</font>"
                + "  </family>"
                + "  <family>"
                + "    <font weight='400' style='normal'>ascent3em-descent4em.ttf</font>"
                + "  </family>"
                + "</familyset>";

        try (FontFallbackSetup setup =
                new FontFallbackSetup("DynamicLayout", testFontFiles, xml)) {
            final TextPaint paint = setup.getPaintFor("sans-serif");
            final int textSize = 100;
            paint.setTextSize(textSize);
            assertEquals(-textSize, paint.ascent(), 0.0f);
            assertEquals(2 * textSize, paint.descent(), 0.0f);

            final int paraWidth = 5 * textSize;
            final String text = "aaaaa aabaa aaaaa"; // This should result in three lines.

            // Old line spacing. All lines should get their ascent and descents from the first font.
            DynamicLayout layout = DynamicLayout.Builder
                    .obtain(text, paint, paraWidth)
                    .setIncludePad(false)
                    .setUseLineSpacingFromFallbacks(false)
                    .build();
            assertEquals(3, layout.getLineCount());
            assertEquals(-textSize, layout.getLineAscent(0));
            assertEquals(2 * textSize, layout.getLineDescent(0));
            assertEquals(-textSize, layout.getLineAscent(1));
            assertEquals(2 * textSize, layout.getLineDescent(1));
            assertEquals(-textSize, layout.getLineAscent(2));
            assertEquals(2 * textSize, layout.getLineDescent(2));

            // New line spacing. The second line has a 'b', so it needs more ascent and descent.
            layout = DynamicLayout.Builder
                    .obtain(text, paint, paraWidth)
                    .setIncludePad(false)
                    .setUseLineSpacingFromFallbacks(true)
                    .build();
            assertEquals(3, layout.getLineCount());
            assertEquals(-textSize, layout.getLineAscent(0));
            assertEquals(2 * textSize, layout.getLineDescent(0));
            assertEquals(-3 * textSize, layout.getLineAscent(1));
            assertEquals(4 * textSize, layout.getLineDescent(1));
            assertEquals(-textSize, layout.getLineAscent(2));
            assertEquals(2 * textSize, layout.getLineDescent(2));

            // The default is the old line spacing, for backward compatibility.
            layout = DynamicLayout.Builder
                    .obtain(text, paint, paraWidth)
                    .setIncludePad(false)
                    .build();
            assertEquals(3, layout.getLineCount());
            assertEquals(-textSize, layout.getLineAscent(0));
            assertEquals(2 * textSize, layout.getLineDescent(0));
            assertEquals(-textSize, layout.getLineAscent(1));
            assertEquals(2 * textSize, layout.getLineDescent(1));
            assertEquals(-textSize, layout.getLineAscent(2));
            assertEquals(2 * textSize, layout.getLineDescent(2));
        }
    }
}
Loading