diff options
| author | 2017-10-03 17:09:43 -0700 | |
|---|---|---|
| committer | 2017-10-04 14:06:11 -0700 | |
| commit | d170532c8e87490623d9f75935d54bc08991bfbd (patch) | |
| tree | 01866b2e0d42e80b9c296fa0efeee7770fcff0f2 | |
| parent | 8cf1ffec8961b0641d82e1825d62d686142a18d3 (diff) | |
Add tests for line breaking that considers overhangs
This adds tests as well as a test font that has large overhangs for
some letters.
Bug: 63938206
Test: bit FrameworksCoreTests:android.text.LineBreakingOverhangsTest
Change-Id: I94c3c03419e525ec10b5158e476a94a9e319891d
| -rw-r--r-- | core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttf | bin | 0 -> 812 bytes | |||
| -rw-r--r-- | core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttx | 234 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/text/LineBreakingOverhangsTest.java | 169 |
3 files changed, 403 insertions, 0 deletions
diff --git a/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttf b/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttf Binary files differnew file mode 100644 index 000000000000..cf769ed3e8a7 --- /dev/null +++ b/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttf diff --git a/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttx b/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttx new file mode 100644 index 000000000000..04d85922a977 --- /dev/null +++ b/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttx @@ -0,0 +1,234 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2017 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<ttFont sfntVersion="\x00\x01\x00\x00" ttLibVersion="3.9"> + + <GlyphOrder> + <!-- The 'id' attribute is only for humans; it is ignored when parsed. --> + <GlyphID id="0" name=".notdef"/> + <GlyphID id="1" name="space"/> + <GlyphID id="2" name="R"/> + <GlyphID id="3" name="a"/> + <GlyphID id="4" name="y"/> + </GlyphOrder> + + <head> + <!-- Most of this table will be recalculated by the compiler --> + <tableVersion value="1.0"/> + <fontRevision value="1.0"/> + <checkSumAdjustment value="0x26d0d624"/> + <magicNumber value="0x5f0f3cf5"/> + <flags value="00000000 00000011"/> + <unitsPerEm value="1000"/> + <created value="Tue Oct 3 23:00:00 2017"/> + <modified value="Tue Oct 3 23:33:15 2017"/> + <xMin value="-1500"/> + <yMin value="0"/> + <xMax value="5000"/> + <yMax value="1000"/> + <macStyle value="00000000 00000000"/> + <lowestRecPPEM value="7"/> + <fontDirectionHint value="2"/> + <indexToLocFormat value="0"/> + <glyphDataFormat value="0"/> + </head> + + <hhea> + <tableVersion value="0x00010000"/> + <ascent value="1000"/> + <descent value="-200"/> + <lineGap value="0"/> + <advanceWidthMax value="1000"/> + <minLeftSideBearing value="-1500"/> + <minRightSideBearing value="-4000"/> + <xMaxExtent value="5000"/> + <caretSlopeRise value="1"/> + <caretSlopeRun value="0"/> + <caretOffset value="0"/> + <reserved0 value="0"/> + <reserved1 value="0"/> + <reserved2 value="0"/> + <reserved3 value="0"/> + <metricDataFormat value="0"/> + <numberOfHMetrics value="1"/> + </hhea> + + <maxp> + <!-- Most of this table will be recalculated by the compiler --> + <tableVersion value="0x10000"/> + <numGlyphs value="5"/> + <maxPoints value="4"/> + <maxContours value="1"/> + <maxCompositePoints value="0"/> + <maxCompositeContours value="0"/> + <maxZones value="0"/> + <maxTwilightPoints value="0"/> + <maxStorage value="0"/> + <maxFunctionDefs value="0"/> + <maxInstructionDefs value="0"/> + <maxStackElements value="0"/> + <maxSizeOfInstructions value="0"/> + <maxComponentElements value="0"/> + <maxComponentDepth value="0"/> + </maxp> + + <OS_2> + <!-- The fields 'usFirstCharIndex' and 'usLastCharIndex' + will be recalculated by the compiler --> + <version value="3"/> + <xAvgCharWidth value="594"/> + <usWeightClass value="400"/> + <usWidthClass value="5"/> + <fsType value="00000000 00001000"/> + <ySubscriptXSize value="650"/> + <ySubscriptYSize value="600"/> + <ySubscriptXOffset value="0"/> + <ySubscriptYOffset value="75"/> + <ySuperscriptXSize value="650"/> + <ySuperscriptYSize value="600"/> + <ySuperscriptXOffset value="0"/> + <ySuperscriptYOffset value="350"/> + <yStrikeoutSize value="50"/> + <yStrikeoutPosition value="300"/> + <sFamilyClass value="0"/> + <panose> + <bFamilyType value="0"/> + <bSerifStyle value="0"/> + <bWeight value="5"/> + <bProportion value="0"/> + <bContrast value="0"/> + <bStrokeVariation value="0"/> + <bArmStyle value="0"/> + <bLetterForm value="0"/> + <bMidline value="0"/> + <bXHeight value="0"/> + </panose> + <ulUnicodeRange1 value="00000000 00000000 00000000 00000001"/> + <ulUnicodeRange2 value="00000000 00000000 00000000 00000000"/> + <ulUnicodeRange3 value="00000000 00000000 00000000 00000000"/> + <ulUnicodeRange4 value="00000000 00000000 00000000 00000000"/> + <achVendID value="UKWN"/> + <fsSelection value="00000000 01000000"/> + <usFirstCharIndex value="32"/> + <usLastCharIndex value="121"/> + <sTypoAscender value="800"/> + <sTypoDescender value="-200"/> + <sTypoLineGap value="200"/> + <usWinAscent value="1000"/> + <usWinDescent value="200"/> + <ulCodePageRange1 value="00000000 00000000 00000000 00000001"/> + <ulCodePageRange2 value="00000000 00000000 00000000 00000000"/> + <sxHeight value="500"/> + <sCapHeight value="700"/> + <usDefaultChar value="0"/> + <usBreakChar value="32"/> + <usMaxContext value="0"/> + </OS_2> + + <hmtx> + <mtx name=".notdef" width="1000" lsb="0"/> + <mtx name="R" width="1000" lsb="0"/> + <mtx name="a" width="1000" lsb="0"/> + <mtx name="space" width="1000" lsb="0"/> + <mtx name="y" width="1000" lsb="-1500"/> + </hmtx> + + <cmap> + <tableVersion version="0"/> + <cmap_format_12 platformID="3" platEncID="10" format="12" reserved="0" length="64" language="0" nGroups="4"> + <map code="0x20" name="space"/><!-- SPACE --> + <map code="0x52" name="R"/><!-- LATIN CAPITAL LETTER R --> + <map code="0x61" name="a"/><!-- LATIN SMALL LETTER A --> + <map code="0x79" name="y"/><!-- LATIN SMALL LETTER Y --> + </cmap_format_12> + </cmap> + + <loca> + <!-- The 'loca' table will be calculated by the compiler --> + </loca> + + <glyf> + + <!-- The xMin, yMin, xMax and yMax values + will be recalculated by the compiler. --> + + <TTGlyph name=".notdef"/><!-- contains no outline data --> + + <TTGlyph name="R" xMin="0" yMin="0" xMax="5000" yMax="1000"> + <contour> + <pt x="0" y="0" on="1"/> + <pt x="0" y="1000" on="1"/> + <pt x="5000" y="1000" on="1"/> + <pt x="5000" y="0" on="1"/> + </contour> + <instructions/> + </TTGlyph> + + <TTGlyph name="a"/><!-- contains no outline data --> + + <TTGlyph name="space"/><!-- contains no outline data --> + + <TTGlyph name="y" xMin="-1500" yMin="0" xMax="1000" yMax="1000"> + <contour> + <pt x="-1500" y="0" on="1"/> + <pt x="-1500" y="1000" on="1"/> + <pt x="1000" y="1000" on="1"/> + <pt x="1000" y="0" on="1"/> + </contour> + <instructions/> + </TTGlyph> + + </glyf> + + <name> + <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True"> + Font for LineBreakingOverhangsTest + </namerecord> + <namerecord nameID="2" platformID="1" platEncID="0" langID="0x0" unicode="True"> + Regular + </namerecord> + <namerecord nameID="4" platformID="1" platEncID="0" langID="0x0" unicode="True"> + Font for LineBreakingOverhangsTest + </namerecord> + <namerecord nameID="6" platformID="1" platEncID="0" langID="0x0" unicode="True"> + SampleFont-Regular + </namerecord> + <namerecord nameID="1" platformID="3" platEncID="1" langID="0x409"> + Sample Font + </namerecord> + <namerecord nameID="2" platformID="3" platEncID="1" langID="0x409"> + Regular + </namerecord> + <namerecord nameID="4" platformID="3" platEncID="1" langID="0x409"> + Sample Font + </namerecord> + <namerecord nameID="6" platformID="3" platEncID="1" langID="0x409"> + SampleFont-Regular + </namerecord> + </name> + + <post> + <formatType value="3.0"/> + <italicAngle value="0.0"/> + <underlinePosition value="-75"/> + <underlineThickness value="50"/> + <isFixedPitch value="0"/> + <minMemType42 value="0"/> + <maxMemType42 value="0"/> + <minMemType1 value="0"/> + <maxMemType1 value="0"/> + </post> + +</ttFont> diff --git a/core/tests/coretests/src/android/text/LineBreakingOverhangsTest.java b/core/tests/coretests/src/android/text/LineBreakingOverhangsTest.java new file mode 100644 index 000000000000..4f18b0b4c7a4 --- /dev/null +++ b/core/tests/coretests/src/android/text/LineBreakingOverhangsTest.java @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.text; + +import static org.junit.Assert.assertEquals; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.graphics.Typeface; +import android.support.test.InstrumentationRegistry; +import android.support.test.filters.SmallTest; +import android.support.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class LineBreakingOverhangsTest { + private static final int EM = 100; // Make 1em == 100px. + private static final TextPaint sTextPaint = new TextPaint(); + + static { + // The test font has following coverage and overhangs. + // All the characters have a width of 1em. + // space: no overhangs + // R: 4em overhang on the right + // a: no overhang + // y: 1.5em overhang on the left + sTextPaint.setTypeface(Typeface.createFromAsset( + InstrumentationRegistry.getTargetContext().getAssets(), + "fonts/LineBreakingOverhangsTestFont.ttf")); + sTextPaint.setTextSize(EM); + } + + private static void layout(@NonNull CharSequence source, @NonNull int[] breaks, double width, + @Nullable int[] leftPadding, @Nullable int[] rightPadding) { + layout(source, breaks, width, leftPadding, rightPadding, null /* indents */); + } + + private static void layout(@NonNull CharSequence source, @NonNull int[] breaks, double width, + @Nullable int[] leftPadding, @Nullable int[] rightPadding, @Nullable int[] indents) { + final StaticLayout staticLayout = StaticLayout.Builder + .obtain(source, 0, source.length(), sTextPaint, (int) width) + .setAvailablePaddings(leftPadding, rightPadding) + .setIndents(indents, indents) + .build(); + + final int lineCount = breaks.length + 1; + assertEquals("Number of lines", lineCount, staticLayout.getLineCount()); + + for (int line = 0; line < lineCount; line++) { + final int lineStart = staticLayout.getLineStart(line); + final int lineEnd = staticLayout.getLineEnd(line); + + if (line == 0) { + assertEquals("Line start for first line", 0, lineStart); + } else { + assertEquals("Line start for line " + line, breaks[line - 1], lineStart); + } + + if (line == lineCount - 1) { + assertEquals("Line end for last line", source.length(), lineEnd); + } else { + assertEquals("Line end for line " + line, breaks[line], lineEnd); + } + } + } + + private static final int[] NO_BREAK = new int[] {}; + + private static final int[] NO_PADDING = null; + // Maximum needed for left side of 'y'. + private static final int[] FULL_LEFT_PADDING = new int[] {(int) (1.5 * EM)}; + // Maximum padding needed for right side of 'R'. + private static final int[] FULL_RIGHT_PADDING = new int[] {4 * EM}; + + private static final int[] ONE_EM_PADDING = new int[] {1 * EM}; + private static final int[] HALF_EM_PADDING = new int[] {(int) (0.5 * EM)}; + private static final int[] QUARTER_EM_PADDING = new int[] {(int) (0.25 * EM)}; + + @Test + public void testRightOverhang() { + // The advance of "aaa R" is 5em, but the right-side overhang of 'R' would need 4em more, so + // we break the line if there's not enough overhang. + + // Enough right padding, so the whole line fits in 5em. + layout("aaa R", NO_BREAK, 5 * EM, NO_PADDING, FULL_RIGHT_PADDING); + + // No right padding, so we'd need 9em to fit the advance and the right padding of 'R'. + layout("aaa R", new int[] {4}, 8.9 * EM, NO_PADDING, NO_PADDING); + layout("aaa R", NO_BREAK, 9 * EM, NO_PADDING, NO_PADDING); + + // 1em of right padding means we can fit the string in 8em. + layout("aaa R", new int[] {4}, 7.9 * EM, NO_PADDING, ONE_EM_PADDING); + layout("aaa R", NO_BREAK, 8 * EM, NO_PADDING, ONE_EM_PADDING); + } + + @Test + public void testLeftOverhang() { + // The advance of "y a" is 3em, but the left-side overhang of 'y' would need 1.5em more, so + // we break the line if there's not enough overhang. + + // Enough left padding, so the whole line fits in 3em. + layout("y a", NO_BREAK, 3 * EM, FULL_LEFT_PADDING, NO_PADDING); + + // No right padding, so we'd need 4.5em to fit the advance and the left padding of 'y'. + layout("y a", new int[] {2}, 4.4 * EM, NO_PADDING, NO_PADDING); + layout("y a", NO_BREAK, 4.5 * EM, NO_PADDING, NO_PADDING); + + // 1em of left padding means we can fit the string in 3.5em. + layout("y a", new int[] {2}, 3.4 * EM, ONE_EM_PADDING, NO_PADDING); + layout("y a", NO_BREAK, 3.5 * EM, ONE_EM_PADDING, NO_PADDING); + } + + @Test + public void testBothSidesOverhang() { + // The advance of "y a R" is 5em, but the left-side overhang of 'y' would need 1.5em more, + // and the right side overhang or 'R' would need 4em more, so we break the line if there's + // not enough overhang. + + // Enough padding, so the whole line fits in 5em. + layout("y a R", NO_BREAK, 5 * EM, FULL_LEFT_PADDING, FULL_RIGHT_PADDING); + + // No padding, so we'd need 10.5em to fit the advance and the paddings. + layout("y a R", new int[] {4}, 10.4 * EM, NO_PADDING, NO_PADDING); + layout("y a R", NO_BREAK, 10.5 * EM, NO_PADDING, NO_PADDING); + + // 1em of padding on each side means we can fit the string in 8.5em. + layout("y a R", new int[] {4}, 8.4 * EM, ONE_EM_PADDING, ONE_EM_PADDING); + layout("y a R", NO_BREAK, 8.5 * EM, ONE_EM_PADDING, ONE_EM_PADDING); + } + + @Test + public void testIndentsDontAffectPaddings() { + // This is identical to the previous test, except that it applies wide indents of 4em on + // each side and thus needs an extra 8em of width. This test makes sure that indents and + // paddings are independent. + final int[] indents = new int[] {4 * EM}; + final int indentAdj = 8 * EM; + + // Enough padding, so the whole line fits in 5em. + layout("y a R", NO_BREAK, 5 * EM + indentAdj, FULL_LEFT_PADDING, FULL_RIGHT_PADDING, + indents); + + // No padding, so we'd need 10.5em to fit the advance and the paddings. + layout("y a R", new int[] {4}, 10.4 * EM + indentAdj, NO_PADDING, NO_PADDING, indents); + layout("y a R", NO_BREAK, 10.5 * EM + indentAdj, NO_PADDING, NO_PADDING, indents); + + // 1em of padding on each side means we can fit the string in 8.5em. + layout("y a R", new int[] {4}, 8.4 * EM + indentAdj, ONE_EM_PADDING, ONE_EM_PADDING, + indents); + layout("y a R", NO_BREAK, 8.5 * EM + indentAdj, ONE_EM_PADDING, ONE_EM_PADDING, indents); + } +} |