Loading core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttfdeleted 100644 → 0 −864 B File deleted. View file core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttxdeleted 100644 → 0 +0 −207 Original line number Diff line number Diff line <?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.0"> <GlyphOrder> <GlyphID id="0" name=".notdef"/> <GlyphID id="1" name="0em"/> <GlyphID id="2" name="1em"/> <GlyphID id="3" name="3em"/> <GlyphID id="4" name="5em"/> <GlyphID id="5" name="7em"/> <GlyphID id="6" name="10em"/> <GlyphID id="7" name="50em"/> <GlyphID id="8" name="100em"/> </GlyphOrder> <head> <tableVersion value="1.0"/> <fontRevision value="1.0"/> <checkSumAdjustment value="0x640cdb2f"/> <magicNumber value="0x5f0f3cf5"/> <flags value="00000000 00000011"/> <unitsPerEm value="100"/> <created value="Fri Mar 17 07:26:00 2017"/> <macStyle value="00000000 00000000"/> <lowestRecPPEM value="7"/> <fontDirectionHint value="2"/> <glyphDataFormat value="0"/> </head> <hhea> <tableVersion value="0x00010000"/> <ascent value="1000"/> <descent value="-200"/> <lineGap value="0"/> <caretSlopeRise value="1"/> <caretSlopeRun value="0"/> <caretOffset value="0"/> <reserved0 value="0"/> <reserved1 value="0"/> <reserved2 value="0"/> <reserved3 value="0"/> <metricDataFormat value="0"/> </hhea> <maxp> <tableVersion value="0x10000"/> <maxZones value="0"/> <maxTwilightPoints value="0"/> <maxStorage value="0"/> <maxFunctionDefs value="0"/> <maxInstructionDefs value="0"/> <maxStackElements value="0"/> <maxSizeOfInstructions value="0"/> <maxComponentElements 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="122"/> <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="50" lsb="0"/> <mtx name="0em" width="0" lsb="0"/> <mtx name="1em" width="100" lsb="0"/> <mtx name="3em" width="300" lsb="0"/> <mtx name="5em" width="500" lsb="0"/> <mtx name="7em" width="700" lsb="0"/> <mtx name="10em" width="1000" lsb="0"/> <mtx name="50em" width="5000" lsb="0"/> <mtx name="100em" width="10000" lsb="0"/> </hmtx> <cmap> <tableVersion version="0"/> <cmap_format_12 format="12" reserved="0" length="6" nGroups="1" platformID="3" platEncID="10" language="0"> <map code="0x0020" name="10em" /> <map code="0x002e" name="10em" /> <!-- . --> <map code="0x0043" name="100em" /> <!-- C --> <map code="0x0049" name="1em" /> <!-- I --> <map code="0x004c" name="50em" /> <!-- L --> <map code="0x0056" name="5em" /> <!-- V --> <map code="0x0058" name="10em" /> <!-- X --> <map code="0x005f" name="0em" /> <!-- _ --> <map code="0xfffd" name="7em" /> <!-- REPLACEMENT CHAR --> <map code="0x10331" name="10em" /> </cmap_format_12> </cmap> <loca> <!-- The 'loca' table will be calculated by the compiler --> </loca> <glyf> <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="0em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="5em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="7em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="10em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="50em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="100em" xMin="0" yMin="0" xMax="0" yMax="0" /> </glyf> <name> <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True"> Font for StaticLayoutLineBreakingTest </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 StaticLayoutLineBreakingTest </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> core/tests/coretests/src/android/text/StaticLayoutLineBreakingTest.javadeleted 100644 → 0 +0 −431 Original line number Diff line number Diff line /* * 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.content.Context;; import android.graphics.Typeface; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.Layout.Alignment; import android.text.style.MetricAffectingSpan; import android.util.Log; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) public class StaticLayoutLineBreakingTest { // Span test are currently not supported because text measurement uses the MeasuredText // internal mWorkPaint instead of the provided MockTestPaint. private static final boolean SPAN_TESTS_SUPPORTED = false; private static final boolean DEBUG = false; private static final float SPACE_MULTI = 1.0f; private static final float SPACE_ADD = 0.0f; private static final int WIDTH = 100; private static final Alignment ALIGN = Alignment.ALIGN_LEFT; private static final char SURR_FIRST = '\uD800'; private static final char SURR_SECOND = '\uDF31'; private static final int[] NO_BREAK = new int[] {}; private static final TextPaint sTextPaint = new TextPaint(); static { // The test font has following coverage and width. // U+0020: 10em // U+002E (.): 10em // U+0043 (C): 100em // U+0049 (I): 1em // U+004C (L): 50em // U+0056 (V): 5em // U+0058 (X): 10em // U+005F (_): 0em // U+FFFD (invalid surrogate will be replaced to this): 7em // U+10331 (\uD800\uDF31): 10em Context context = InstrumentationRegistry.getTargetContext(); sTextPaint.setTypeface(Typeface.createFromAsset(context.getAssets(), "fonts/StaticLayoutLineBreakingTestFont.ttf")); sTextPaint.setTextSize(1.0f); // Make 1em == 1px. } private static StaticLayout getStaticLayout(CharSequence source, int width) { return new StaticLayout(source, sTextPaint, width, ALIGN, SPACE_MULTI, SPACE_ADD, false); } private static int[] getBreaks(CharSequence source) { return getBreaks(source, WIDTH); } private static int[] getBreaks(CharSequence source, int width) { StaticLayout staticLayout = getStaticLayout(source, width); int[] breaks = new int[staticLayout.getLineCount() - 1]; for (int line = 0; line < breaks.length; line++) { breaks[line] = staticLayout.getLineEnd(line); } return breaks; } private static void debugLayout(CharSequence source, StaticLayout staticLayout) { if (DEBUG) { int count = staticLayout.getLineCount(); Log.i("SLLBTest", "\"" + source.toString() + "\": " + count + " lines"); for (int line = 0; line < count; line++) { int lineStart = staticLayout.getLineStart(line); int lineEnd = staticLayout.getLineEnd(line); Log.i("SLLBTest", "Line " + line + " [" + lineStart + ".." + lineEnd + "]\t" + source.subSequence(lineStart, lineEnd)); } } } private static void layout(CharSequence source, int[] breaks) { layout(source, breaks, WIDTH); } private static void layout(CharSequence source, int[] breaks, int width) { StaticLayout staticLayout = getStaticLayout(source, width); debugLayout(source, staticLayout); int lineCount = breaks.length + 1; assertEquals("Number of lines", lineCount, staticLayout.getLineCount()); for (int line = 0; line < lineCount; line++) { int lineStart = staticLayout.getLineStart(line); 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 void layoutMaxLines(CharSequence source, int[] breaks, int maxLines) { StaticLayout staticLayout = new StaticLayout(source, 0, source.length(), sTextPaint, WIDTH, ALIGN, TextDirectionHeuristics.LTR, SPACE_MULTI, SPACE_ADD, false /* includePad */, null, WIDTH, maxLines); debugLayout(source, staticLayout); final int lineCount = staticLayout.getLineCount(); for (int line = 0; line < lineCount; line++) { int lineStart = staticLayout.getLineStart(line); 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 && line != breaks.length - 1) { assertEquals("Line end for last line", source.length(), lineEnd); } else { assertEquals("Line end for line " + line, breaks[line], lineEnd); } } } private static final int MAX_SPAN_COUNT = 10; private static final int[] sSpanStarts = new int[MAX_SPAN_COUNT]; private static final int[] sSpanEnds = new int[MAX_SPAN_COUNT]; private static MetricAffectingSpan getMetricAffectingSpan() { return new MetricAffectingSpan() { @Override public void updateDrawState(TextPaint tp) { /* empty */ } @Override public void updateMeasureState(TextPaint p) { /* empty */ } }; } /** * Replaces the "<...>" blocks by spans, assuming non overlapping, correctly defined spans * @param text * @return A CharSequence with '<' '>' replaced by MetricAffectingSpan */ private static CharSequence spanify(String text) { int startIndex = text.indexOf('<'); if (startIndex < 0) return text; int spanCount = 0; do { int endIndex = text.indexOf('>'); if (endIndex < 0) throw new IllegalArgumentException("Unbalanced span markers"); text = text.substring(0, startIndex) + text.substring(startIndex + 1, endIndex) + text.substring(endIndex + 1); sSpanStarts[spanCount] = startIndex; sSpanEnds[spanCount] = endIndex - 2; spanCount++; startIndex = text.indexOf('<'); } while (startIndex >= 0); SpannableStringBuilder result = new SpannableStringBuilder(text); for (int i = 0; i < spanCount; i++) { result.setSpan(getMetricAffectingSpan(), sSpanStarts[i], sSpanEnds[i], Spanned.SPAN_INCLUSIVE_INCLUSIVE); } return result; } @Test public void testNoLineBreak() { // Width lower than WIDTH layout("", NO_BREAK); layout("I", NO_BREAK); layout("V", NO_BREAK); layout("X", NO_BREAK); layout("L", NO_BREAK); layout("I VILI", NO_BREAK); layout("XXXX", NO_BREAK); layout("LXXXX", NO_BREAK); // Width equal to WIDTH layout("C", NO_BREAK); layout("LL", NO_BREAK); layout("L XXXX", NO_BREAK); layout("XXXXXXXXXX", NO_BREAK); layout("XXX XXXXXX", NO_BREAK); layout("XXX XXXX X", NO_BREAK); layout("XXX XXXXX ", NO_BREAK); layout(" XXXXXXXX ", NO_BREAK); layout(" XX XXX ", NO_BREAK); // 0123456789 // Width greater than WIDTH, but no break layout(" XX XXX ", NO_BREAK); layout("XX XXX XXX ", NO_BREAK); layout("XX XXX XXX ", NO_BREAK); layout("XXXXXXXXXX ", NO_BREAK); // 01234567890 } @Test public void testOneLineBreak() { // 01234567890 layout("XX XXX XXXX", new int[] {7}); layout("XX XXXX XXX", new int[] {8}); layout("XX XXXXX XX", new int[] {9}); layout("XX XXXXXX X", new int[] {10}); // 01234567890 layout("XXXXXXXXXXX", new int[] {10}); layout("XXXXXXXXX X", new int[] {10}); layout("XXXXXXXX XX", new int[] {9}); layout("XXXXXXX XXX", new int[] {8}); layout("XXXXXX XXXX", new int[] {7}); // 01234567890 layout("LL LL", new int[] {3}); layout("LLLL", new int[] {2}); layout("C C", new int[] {2}); layout("CC", new int[] {1}); } @Test public void testSpaceAtBreak() { // 0123456789012 layout("XXXX XXXXX X", new int[] {11}); layout("XXXXXXXXXX X", new int[] {11}); layout("XXXXXXXXXV X", new int[] {11}); layout("C X", new int[] {2}); } @Test public void testMultipleSpacesAtBreak() { // 0123456789012 layout("LXX XXXX", new int[] {4}); layout("LXX XXXX", new int[] {5}); layout("LXX XXXX", new int[] {6}); layout("LXX XXXX", new int[] {7}); layout("LXX XXXX", new int[] {8}); } @Test public void testZeroWidthCharacters() { // 0123456789012345678901234 layout("X_X_X_X_X_X_X_X_X_X", NO_BREAK); layout("___X_X_X_X_X_X_X_X_X_X___", NO_BREAK); layout("C_X", new int[] {2}); layout("C__X", new int[] {3}); } /** * Note that when the text has spans, StaticLayout does not use the provided TextPaint to * measure text runs anymore. This is probably a bug. * To be able to use the fake sTextPaint and make this test pass, use mPaint instead of * mWorkPaint in MeasuredText#addStyleRun */ @Test public void testWithSpans() { if (!SPAN_TESTS_SUPPORTED) return; layout(spanify("<012 456 89>"), NO_BREAK); layout(spanify("012 <456> 89"), NO_BREAK); layout(spanify("<012> <456>< 89>"), NO_BREAK); layout(spanify("<012> <456> <89>"), NO_BREAK); layout(spanify("<012> <456> <89>012"), new int[] {8}); layout(spanify("<012> <456> 89<012>"), new int[] {8}); layout(spanify("<012> <456> <89><012>"), new int[] {8}); layout(spanify("<012> <456> 89 <123>"), new int[] {11}); layout(spanify("<012> <456> 89< 123>"), new int[] {11}); layout(spanify("<012> <456> <89> <123>"), new int[] {11}); layout(spanify("012 456 89 <LXX> XX XX"), new int[] {11, 18}); } /* * Adding a span to the string should not change the layout, since the metrics are unchanged. */ @Test public void testWithOneSpan() { if (!SPAN_TESTS_SUPPORTED) return; String[] texts = new String[] { "0123", "012 456", "012 456 89 123", "012 45678 012", "012 456 89012 456 89012", "0123456789012" }; MetricAffectingSpan metricAffectingSpan = getMetricAffectingSpan(); for (String text : texts) { // Get the line breaks without any span int[] breaks = getBreaks(text); // Add spans on all possible offsets for (int spanStart = 0; spanStart < text.length(); spanStart++) { for (int spanEnd = spanStart; spanEnd < text.length(); spanEnd++) { SpannableStringBuilder ssb = new SpannableStringBuilder(text); ssb.setSpan(metricAffectingSpan, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_INCLUSIVE); layout(ssb, breaks); } } } } @Test public void testWithTwoSpans() { if (!SPAN_TESTS_SUPPORTED) return; String[] texts = new String[] { "0123", "012 456", "012 456 89 123", "012 45678 012", "012 456 89012 456 89012", "0123456789012" }; MetricAffectingSpan metricAffectingSpan1 = getMetricAffectingSpan(); MetricAffectingSpan metricAffectingSpan2 = getMetricAffectingSpan(); for (String text : texts) { // Get the line breaks without any span int[] breaks = getBreaks(text); // Add spans on all possible offsets for (int spanStart1 = 0; spanStart1 < text.length(); spanStart1++) { for (int spanEnd1 = spanStart1; spanEnd1 < text.length(); spanEnd1++) { SpannableStringBuilder ssb = new SpannableStringBuilder(text); ssb.setSpan(metricAffectingSpan1, spanStart1, spanEnd1, Spanned.SPAN_INCLUSIVE_INCLUSIVE); for (int spanStart2 = 0; spanStart2 < text.length(); spanStart2++) { for (int spanEnd2 = spanStart2; spanEnd2 < text.length(); spanEnd2++) { ssb.setSpan(metricAffectingSpan2, spanStart2, spanEnd2, Spanned.SPAN_INCLUSIVE_INCLUSIVE); layout(ssb, breaks); } } } } } } public static String replace(String string, char c, char r) { return string.replaceAll(String.valueOf(c), String.valueOf(r)); } @Test public void testWithSurrogate() { layout("LX" + SURR_FIRST + SURR_SECOND, NO_BREAK); layout("LXXXX" + SURR_FIRST + SURR_SECOND, NO_BREAK); // LXXXXI (91) + SURR_FIRST + SURR_SECOND (10). Do not break in the middle point of // surrogatge pair. layout("LXXXXI" + SURR_FIRST + SURR_SECOND, new int[] {6}); // LXXXXI (91) + SURR_SECOND (replaced with REPLACEMENT CHARACTER. width is 7px) fits. // Break just after invalid trailing surrogate. layout("LXXXXI" + SURR_SECOND + SURR_FIRST, new int[] {7}); layout("C" + SURR_FIRST + SURR_SECOND, new int[] {1}); } @Test public void testNarrowWidth() { int[] widths = new int[] { 0, 4, 10 }; String[] texts = new String[] { "", "X", " ", "XX", " X", "XXX" }; for (String text: texts) { // 15 is such that only one character will fit int[] breaks = getBreaks(text, 15); // Width under 15 should all lead to the same line break for (int width: widths) { layout(text, breaks, width); } } } @Test public void testNarrowWidthZeroWidth() { int[] widths = new int[] { 1, 4 }; for (int width: widths) { layout("X.", new int[] {1}, width); layout("X__", NO_BREAK, width); layout("X__X", new int[] {3}, width); layout("X__X_", new int[] {3}, width); layout("_", NO_BREAK, width); layout("__", NO_BREAK, width); layout("_X", new int[] {1}, width); layout("_X_", new int[] {1}, width); layout("__X__", new int[] {2}, width); } } @Test public void testMaxLines() { layoutMaxLines("C", NO_BREAK, 1); layoutMaxLines("C C", new int[] {2}, 1); layoutMaxLines("C C", new int[] {2}, 2); layoutMaxLines("CC", new int[] {1}, 1); layoutMaxLines("CC", new int[] {1}, 2); } } Loading
core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttfdeleted 100644 → 0 −864 B File deleted. View file
core/tests/coretests/assets/fonts/StaticLayoutLineBreakingTestFont.ttxdeleted 100644 → 0 +0 −207 Original line number Diff line number Diff line <?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.0"> <GlyphOrder> <GlyphID id="0" name=".notdef"/> <GlyphID id="1" name="0em"/> <GlyphID id="2" name="1em"/> <GlyphID id="3" name="3em"/> <GlyphID id="4" name="5em"/> <GlyphID id="5" name="7em"/> <GlyphID id="6" name="10em"/> <GlyphID id="7" name="50em"/> <GlyphID id="8" name="100em"/> </GlyphOrder> <head> <tableVersion value="1.0"/> <fontRevision value="1.0"/> <checkSumAdjustment value="0x640cdb2f"/> <magicNumber value="0x5f0f3cf5"/> <flags value="00000000 00000011"/> <unitsPerEm value="100"/> <created value="Fri Mar 17 07:26:00 2017"/> <macStyle value="00000000 00000000"/> <lowestRecPPEM value="7"/> <fontDirectionHint value="2"/> <glyphDataFormat value="0"/> </head> <hhea> <tableVersion value="0x00010000"/> <ascent value="1000"/> <descent value="-200"/> <lineGap value="0"/> <caretSlopeRise value="1"/> <caretSlopeRun value="0"/> <caretOffset value="0"/> <reserved0 value="0"/> <reserved1 value="0"/> <reserved2 value="0"/> <reserved3 value="0"/> <metricDataFormat value="0"/> </hhea> <maxp> <tableVersion value="0x10000"/> <maxZones value="0"/> <maxTwilightPoints value="0"/> <maxStorage value="0"/> <maxFunctionDefs value="0"/> <maxInstructionDefs value="0"/> <maxStackElements value="0"/> <maxSizeOfInstructions value="0"/> <maxComponentElements 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="122"/> <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="50" lsb="0"/> <mtx name="0em" width="0" lsb="0"/> <mtx name="1em" width="100" lsb="0"/> <mtx name="3em" width="300" lsb="0"/> <mtx name="5em" width="500" lsb="0"/> <mtx name="7em" width="700" lsb="0"/> <mtx name="10em" width="1000" lsb="0"/> <mtx name="50em" width="5000" lsb="0"/> <mtx name="100em" width="10000" lsb="0"/> </hmtx> <cmap> <tableVersion version="0"/> <cmap_format_12 format="12" reserved="0" length="6" nGroups="1" platformID="3" platEncID="10" language="0"> <map code="0x0020" name="10em" /> <map code="0x002e" name="10em" /> <!-- . --> <map code="0x0043" name="100em" /> <!-- C --> <map code="0x0049" name="1em" /> <!-- I --> <map code="0x004c" name="50em" /> <!-- L --> <map code="0x0056" name="5em" /> <!-- V --> <map code="0x0058" name="10em" /> <!-- X --> <map code="0x005f" name="0em" /> <!-- _ --> <map code="0xfffd" name="7em" /> <!-- REPLACEMENT CHAR --> <map code="0x10331" name="10em" /> </cmap_format_12> </cmap> <loca> <!-- The 'loca' table will be calculated by the compiler --> </loca> <glyf> <TTGlyph name=".notdef" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="0em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="1em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="3em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="5em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="7em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="10em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="50em" xMin="0" yMin="0" xMax="0" yMax="0" /> <TTGlyph name="100em" xMin="0" yMin="0" xMax="0" yMax="0" /> </glyf> <name> <namerecord nameID="1" platformID="1" platEncID="0" langID="0x0" unicode="True"> Font for StaticLayoutLineBreakingTest </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 StaticLayoutLineBreakingTest </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>
core/tests/coretests/src/android/text/StaticLayoutLineBreakingTest.javadeleted 100644 → 0 +0 −431 Original line number Diff line number Diff line /* * 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.content.Context;; import android.graphics.Typeface; import android.support.test.InstrumentationRegistry; import android.support.test.filters.SmallTest; import android.support.test.runner.AndroidJUnit4; import android.text.Layout.Alignment; import android.text.style.MetricAffectingSpan; import android.util.Log; import org.junit.Test; import org.junit.runner.RunWith; @SmallTest @RunWith(AndroidJUnit4.class) public class StaticLayoutLineBreakingTest { // Span test are currently not supported because text measurement uses the MeasuredText // internal mWorkPaint instead of the provided MockTestPaint. private static final boolean SPAN_TESTS_SUPPORTED = false; private static final boolean DEBUG = false; private static final float SPACE_MULTI = 1.0f; private static final float SPACE_ADD = 0.0f; private static final int WIDTH = 100; private static final Alignment ALIGN = Alignment.ALIGN_LEFT; private static final char SURR_FIRST = '\uD800'; private static final char SURR_SECOND = '\uDF31'; private static final int[] NO_BREAK = new int[] {}; private static final TextPaint sTextPaint = new TextPaint(); static { // The test font has following coverage and width. // U+0020: 10em // U+002E (.): 10em // U+0043 (C): 100em // U+0049 (I): 1em // U+004C (L): 50em // U+0056 (V): 5em // U+0058 (X): 10em // U+005F (_): 0em // U+FFFD (invalid surrogate will be replaced to this): 7em // U+10331 (\uD800\uDF31): 10em Context context = InstrumentationRegistry.getTargetContext(); sTextPaint.setTypeface(Typeface.createFromAsset(context.getAssets(), "fonts/StaticLayoutLineBreakingTestFont.ttf")); sTextPaint.setTextSize(1.0f); // Make 1em == 1px. } private static StaticLayout getStaticLayout(CharSequence source, int width) { return new StaticLayout(source, sTextPaint, width, ALIGN, SPACE_MULTI, SPACE_ADD, false); } private static int[] getBreaks(CharSequence source) { return getBreaks(source, WIDTH); } private static int[] getBreaks(CharSequence source, int width) { StaticLayout staticLayout = getStaticLayout(source, width); int[] breaks = new int[staticLayout.getLineCount() - 1]; for (int line = 0; line < breaks.length; line++) { breaks[line] = staticLayout.getLineEnd(line); } return breaks; } private static void debugLayout(CharSequence source, StaticLayout staticLayout) { if (DEBUG) { int count = staticLayout.getLineCount(); Log.i("SLLBTest", "\"" + source.toString() + "\": " + count + " lines"); for (int line = 0; line < count; line++) { int lineStart = staticLayout.getLineStart(line); int lineEnd = staticLayout.getLineEnd(line); Log.i("SLLBTest", "Line " + line + " [" + lineStart + ".." + lineEnd + "]\t" + source.subSequence(lineStart, lineEnd)); } } } private static void layout(CharSequence source, int[] breaks) { layout(source, breaks, WIDTH); } private static void layout(CharSequence source, int[] breaks, int width) { StaticLayout staticLayout = getStaticLayout(source, width); debugLayout(source, staticLayout); int lineCount = breaks.length + 1; assertEquals("Number of lines", lineCount, staticLayout.getLineCount()); for (int line = 0; line < lineCount; line++) { int lineStart = staticLayout.getLineStart(line); 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 void layoutMaxLines(CharSequence source, int[] breaks, int maxLines) { StaticLayout staticLayout = new StaticLayout(source, 0, source.length(), sTextPaint, WIDTH, ALIGN, TextDirectionHeuristics.LTR, SPACE_MULTI, SPACE_ADD, false /* includePad */, null, WIDTH, maxLines); debugLayout(source, staticLayout); final int lineCount = staticLayout.getLineCount(); for (int line = 0; line < lineCount; line++) { int lineStart = staticLayout.getLineStart(line); 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 && line != breaks.length - 1) { assertEquals("Line end for last line", source.length(), lineEnd); } else { assertEquals("Line end for line " + line, breaks[line], lineEnd); } } } private static final int MAX_SPAN_COUNT = 10; private static final int[] sSpanStarts = new int[MAX_SPAN_COUNT]; private static final int[] sSpanEnds = new int[MAX_SPAN_COUNT]; private static MetricAffectingSpan getMetricAffectingSpan() { return new MetricAffectingSpan() { @Override public void updateDrawState(TextPaint tp) { /* empty */ } @Override public void updateMeasureState(TextPaint p) { /* empty */ } }; } /** * Replaces the "<...>" blocks by spans, assuming non overlapping, correctly defined spans * @param text * @return A CharSequence with '<' '>' replaced by MetricAffectingSpan */ private static CharSequence spanify(String text) { int startIndex = text.indexOf('<'); if (startIndex < 0) return text; int spanCount = 0; do { int endIndex = text.indexOf('>'); if (endIndex < 0) throw new IllegalArgumentException("Unbalanced span markers"); text = text.substring(0, startIndex) + text.substring(startIndex + 1, endIndex) + text.substring(endIndex + 1); sSpanStarts[spanCount] = startIndex; sSpanEnds[spanCount] = endIndex - 2; spanCount++; startIndex = text.indexOf('<'); } while (startIndex >= 0); SpannableStringBuilder result = new SpannableStringBuilder(text); for (int i = 0; i < spanCount; i++) { result.setSpan(getMetricAffectingSpan(), sSpanStarts[i], sSpanEnds[i], Spanned.SPAN_INCLUSIVE_INCLUSIVE); } return result; } @Test public void testNoLineBreak() { // Width lower than WIDTH layout("", NO_BREAK); layout("I", NO_BREAK); layout("V", NO_BREAK); layout("X", NO_BREAK); layout("L", NO_BREAK); layout("I VILI", NO_BREAK); layout("XXXX", NO_BREAK); layout("LXXXX", NO_BREAK); // Width equal to WIDTH layout("C", NO_BREAK); layout("LL", NO_BREAK); layout("L XXXX", NO_BREAK); layout("XXXXXXXXXX", NO_BREAK); layout("XXX XXXXXX", NO_BREAK); layout("XXX XXXX X", NO_BREAK); layout("XXX XXXXX ", NO_BREAK); layout(" XXXXXXXX ", NO_BREAK); layout(" XX XXX ", NO_BREAK); // 0123456789 // Width greater than WIDTH, but no break layout(" XX XXX ", NO_BREAK); layout("XX XXX XXX ", NO_BREAK); layout("XX XXX XXX ", NO_BREAK); layout("XXXXXXXXXX ", NO_BREAK); // 01234567890 } @Test public void testOneLineBreak() { // 01234567890 layout("XX XXX XXXX", new int[] {7}); layout("XX XXXX XXX", new int[] {8}); layout("XX XXXXX XX", new int[] {9}); layout("XX XXXXXX X", new int[] {10}); // 01234567890 layout("XXXXXXXXXXX", new int[] {10}); layout("XXXXXXXXX X", new int[] {10}); layout("XXXXXXXX XX", new int[] {9}); layout("XXXXXXX XXX", new int[] {8}); layout("XXXXXX XXXX", new int[] {7}); // 01234567890 layout("LL LL", new int[] {3}); layout("LLLL", new int[] {2}); layout("C C", new int[] {2}); layout("CC", new int[] {1}); } @Test public void testSpaceAtBreak() { // 0123456789012 layout("XXXX XXXXX X", new int[] {11}); layout("XXXXXXXXXX X", new int[] {11}); layout("XXXXXXXXXV X", new int[] {11}); layout("C X", new int[] {2}); } @Test public void testMultipleSpacesAtBreak() { // 0123456789012 layout("LXX XXXX", new int[] {4}); layout("LXX XXXX", new int[] {5}); layout("LXX XXXX", new int[] {6}); layout("LXX XXXX", new int[] {7}); layout("LXX XXXX", new int[] {8}); } @Test public void testZeroWidthCharacters() { // 0123456789012345678901234 layout("X_X_X_X_X_X_X_X_X_X", NO_BREAK); layout("___X_X_X_X_X_X_X_X_X_X___", NO_BREAK); layout("C_X", new int[] {2}); layout("C__X", new int[] {3}); } /** * Note that when the text has spans, StaticLayout does not use the provided TextPaint to * measure text runs anymore. This is probably a bug. * To be able to use the fake sTextPaint and make this test pass, use mPaint instead of * mWorkPaint in MeasuredText#addStyleRun */ @Test public void testWithSpans() { if (!SPAN_TESTS_SUPPORTED) return; layout(spanify("<012 456 89>"), NO_BREAK); layout(spanify("012 <456> 89"), NO_BREAK); layout(spanify("<012> <456>< 89>"), NO_BREAK); layout(spanify("<012> <456> <89>"), NO_BREAK); layout(spanify("<012> <456> <89>012"), new int[] {8}); layout(spanify("<012> <456> 89<012>"), new int[] {8}); layout(spanify("<012> <456> <89><012>"), new int[] {8}); layout(spanify("<012> <456> 89 <123>"), new int[] {11}); layout(spanify("<012> <456> 89< 123>"), new int[] {11}); layout(spanify("<012> <456> <89> <123>"), new int[] {11}); layout(spanify("012 456 89 <LXX> XX XX"), new int[] {11, 18}); } /* * Adding a span to the string should not change the layout, since the metrics are unchanged. */ @Test public void testWithOneSpan() { if (!SPAN_TESTS_SUPPORTED) return; String[] texts = new String[] { "0123", "012 456", "012 456 89 123", "012 45678 012", "012 456 89012 456 89012", "0123456789012" }; MetricAffectingSpan metricAffectingSpan = getMetricAffectingSpan(); for (String text : texts) { // Get the line breaks without any span int[] breaks = getBreaks(text); // Add spans on all possible offsets for (int spanStart = 0; spanStart < text.length(); spanStart++) { for (int spanEnd = spanStart; spanEnd < text.length(); spanEnd++) { SpannableStringBuilder ssb = new SpannableStringBuilder(text); ssb.setSpan(metricAffectingSpan, spanStart, spanEnd, Spanned.SPAN_INCLUSIVE_INCLUSIVE); layout(ssb, breaks); } } } } @Test public void testWithTwoSpans() { if (!SPAN_TESTS_SUPPORTED) return; String[] texts = new String[] { "0123", "012 456", "012 456 89 123", "012 45678 012", "012 456 89012 456 89012", "0123456789012" }; MetricAffectingSpan metricAffectingSpan1 = getMetricAffectingSpan(); MetricAffectingSpan metricAffectingSpan2 = getMetricAffectingSpan(); for (String text : texts) { // Get the line breaks without any span int[] breaks = getBreaks(text); // Add spans on all possible offsets for (int spanStart1 = 0; spanStart1 < text.length(); spanStart1++) { for (int spanEnd1 = spanStart1; spanEnd1 < text.length(); spanEnd1++) { SpannableStringBuilder ssb = new SpannableStringBuilder(text); ssb.setSpan(metricAffectingSpan1, spanStart1, spanEnd1, Spanned.SPAN_INCLUSIVE_INCLUSIVE); for (int spanStart2 = 0; spanStart2 < text.length(); spanStart2++) { for (int spanEnd2 = spanStart2; spanEnd2 < text.length(); spanEnd2++) { ssb.setSpan(metricAffectingSpan2, spanStart2, spanEnd2, Spanned.SPAN_INCLUSIVE_INCLUSIVE); layout(ssb, breaks); } } } } } } public static String replace(String string, char c, char r) { return string.replaceAll(String.valueOf(c), String.valueOf(r)); } @Test public void testWithSurrogate() { layout("LX" + SURR_FIRST + SURR_SECOND, NO_BREAK); layout("LXXXX" + SURR_FIRST + SURR_SECOND, NO_BREAK); // LXXXXI (91) + SURR_FIRST + SURR_SECOND (10). Do not break in the middle point of // surrogatge pair. layout("LXXXXI" + SURR_FIRST + SURR_SECOND, new int[] {6}); // LXXXXI (91) + SURR_SECOND (replaced with REPLACEMENT CHARACTER. width is 7px) fits. // Break just after invalid trailing surrogate. layout("LXXXXI" + SURR_SECOND + SURR_FIRST, new int[] {7}); layout("C" + SURR_FIRST + SURR_SECOND, new int[] {1}); } @Test public void testNarrowWidth() { int[] widths = new int[] { 0, 4, 10 }; String[] texts = new String[] { "", "X", " ", "XX", " X", "XXX" }; for (String text: texts) { // 15 is such that only one character will fit int[] breaks = getBreaks(text, 15); // Width under 15 should all lead to the same line break for (int width: widths) { layout(text, breaks, width); } } } @Test public void testNarrowWidthZeroWidth() { int[] widths = new int[] { 1, 4 }; for (int width: widths) { layout("X.", new int[] {1}, width); layout("X__", NO_BREAK, width); layout("X__X", new int[] {3}, width); layout("X__X_", new int[] {3}, width); layout("_", NO_BREAK, width); layout("__", NO_BREAK, width); layout("_X", new int[] {1}, width); layout("_X_", new int[] {1}, width); layout("__X__", new int[] {2}, width); } } @Test public void testMaxLines() { layoutMaxLines("C", NO_BREAK, 1); layoutMaxLines("C C", new int[] {2}, 1); layoutMaxLines("C C", new int[] {2}, 2); layoutMaxLines("CC", new int[] {1}, 1); layoutMaxLines("CC", new int[] {1}, 2); } }