Loading api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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 { api/system-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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 { api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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 { core/java/android/text/DynamicLayout.java +28 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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}. Loading Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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; Loading Loading @@ -573,6 +595,7 @@ public class DynamicLayout extends Layout .setWidth(getWidth()) .setTextDirection(getTextDirectionHeuristic()) .setLineSpacing(getSpacingAdd(), getSpacingMultiplier()) .setUseLineSpacingFromFallbacks(mFallbackLineSpacing) .setEllipsizedWidth(mEllipsizedWidth) .setEllipsize(mEllipsizeAt) .setBreakStrategy(mBreakStrategy) Loading Loading @@ -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 Loading Loading @@ -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; Loading core/tests/coretests/src/android/text/DynamicLayoutTest.java +73 −0 Original line number Diff line number Diff line Loading @@ -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
api/current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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 {
api/system-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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 {
api/test-current.txt +1 −0 Original line number Diff line number Diff line Loading @@ -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 {
core/java/android/text/DynamicLayout.java +28 −3 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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}. Loading Loading @@ -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; Loading Loading @@ -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); } Loading Loading @@ -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; Loading Loading @@ -573,6 +595,7 @@ public class DynamicLayout extends Layout .setWidth(getWidth()) .setTextDirection(getTextDirectionHeuristic()) .setLineSpacing(getSpacingAdd(), getSpacingMultiplier()) .setUseLineSpacingFromFallbacks(mFallbackLineSpacing) .setEllipsizedWidth(mEllipsizedWidth) .setEllipsize(mEllipsizeAt) .setBreakStrategy(mBreakStrategy) Loading Loading @@ -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 Loading Loading @@ -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; Loading
core/tests/coretests/src/android/text/DynamicLayoutTest.java +73 −0 Original line number Diff line number Diff line Loading @@ -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)); } } }