summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Roozbeh Pournader <roozbeh@google.com> 2017-10-03 17:09:43 -0700
committer Roozbeh Pournader <roozbeh@google.com> 2017-10-04 14:06:11 -0700
commitd170532c8e87490623d9f75935d54bc08991bfbd (patch)
tree01866b2e0d42e80b9c296fa0efeee7770fcff0f2
parent8cf1ffec8961b0641d82e1825d62d686142a18d3 (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.ttfbin0 -> 812 bytes
-rw-r--r--core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttx234
-rw-r--r--core/tests/coretests/src/android/text/LineBreakingOverhangsTest.java169
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
new file mode 100644
index 000000000000..cf769ed3e8a7
--- /dev/null
+++ b/core/tests/coretests/assets/fonts/LineBreakingOverhangsTestFont.ttf
Binary files differ
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);
+ }
+}