Loading core/java/android/text/DynamicLayout.java +15 −4 Original line number Diff line number Diff line Loading @@ -356,6 +356,8 @@ public class DynamicLayout extends Layout ints[DESCENT] = desc; objects[0] = reflowed.getLineDirections(i); ints[HYPHEN] = reflowed.getHyphen(i); if (mEllipsize) { ints[ELLIPSIS_START] = reflowed.getEllipsisStart(i); ints[ELLIPSIS_COUNT] = reflowed.getEllipsisCount(i); Loading Loading @@ -631,6 +633,14 @@ public class DynamicLayout extends Layout return mBottomPadding; } /** * @hide */ @Override public int getHyphen(int line) { return mInts.getValue(line, HYPHEN); } @Override public int getEllipsizedWidth() { return mEllipsizedWidth; Loading Loading @@ -739,11 +749,12 @@ public class DynamicLayout extends Layout private static final int TAB = START; private static final int TOP = 1; private static final int DESCENT = 2; private static final int COLUMNS_NORMAL = 3; private static final int HYPHEN = 3; private static final int COLUMNS_NORMAL = 4; private static final int ELLIPSIS_START = 3; private static final int ELLIPSIS_COUNT = 4; private static final int COLUMNS_ELLIPSIZE = 5; private static final int ELLIPSIS_START = 4; private static final int ELLIPSIS_COUNT = 5; private static final int COLUMNS_ELLIPSIZE = 6; private static final int START_MASK = 0x1FFFFFFF; private static final int DIR_SHIFT = 30; Loading core/java/android/text/Hyphenator.java 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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 android.util.Log; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.HashMap; import java.util.Locale; /** * Hyphenator is a wrapper class for a native implementation of automatic hyphenation, * in essence finding valid hyphenation opportunities in a word. * * @hide */ /* package */ class Hyphenator { // This class has deliberately simple lifetime management (no finalizer) because in // the common case a process will use a very small number of locales. private static String TAG = "Hyphenator"; static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>(); private long mNativePtr; private Hyphenator(long nativePtr) { mNativePtr = nativePtr; } public static long get(Locale locale) { synchronized (sMap) { Hyphenator result = sMap.get(locale); if (result == null) { result = loadHyphenator(locale); sMap.put(locale, result); } return result == null ? 0 : result.mNativePtr; } } private static Hyphenator loadHyphenator(Locale locale) { // TODO: find pattern dictionary (from system location) that best matches locale if (Locale.US.equals(locale)) { File f = new File("/data/local/tmp/hyph-en-us.pat.txt"); try { RandomAccessFile rf = new RandomAccessFile(f, "r"); byte[] buf = new byte[(int)rf.length()]; rf.read(buf); rf.close(); String patternData = new String(buf); long nativePtr = StaticLayout.nLoadHyphenator(patternData); return new Hyphenator(nativePtr); } catch (IOException e) { Log.e(TAG, "error loading hyphenation " + f); } } return null; } } core/java/android/text/Layout.java +21 −11 Original line number Diff line number Diff line Loading @@ -225,17 +225,17 @@ public abstract class Layout { // Draw the lines, one at a time. // The baseline is the top of the following line minus the current line's descent. for (int i = firstLine; i <= lastLine; i++) { for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) { int start = previousLineEnd; previousLineEnd = getLineStart(i + 1); int end = getLineVisibleEnd(i, start, previousLineEnd); previousLineEnd = getLineStart(lineNum + 1); int end = getLineVisibleEnd(lineNum, start, previousLineEnd); int ltop = previousLineBottom; int lbottom = getLineTop(i+1); int lbottom = getLineTop(lineNum + 1); previousLineBottom = lbottom; int lbaseline = lbottom - getLineDescent(i); int lbaseline = lbottom - getLineDescent(lineNum); int dir = getParagraphDirection(i); int dir = getParagraphDirection(lineNum); int left = 0; int right = mWidth; Loading @@ -254,7 +254,7 @@ public abstract class Layout { // just collect the ones present at the start of the paragraph. // If spanEnd is before the end of the paragraph, that's not // our problem. if (start >= spanEnd && (i == firstLine || isFirstParaLine)) { if (start >= spanEnd && (lineNum == firstLine || isFirstParaLine)) { spanEnd = sp.nextSpanTransition(start, textLength, ParagraphStyle.class); spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class); Loading @@ -280,7 +280,7 @@ public abstract class Layout { int startLine = getLineForOffset(sp.getSpanStart(spans[n])); // if there is more than one LeadingMarginSpan2, use // the count that is greatest if (i < startLine + count) { if (lineNum < startLine + count) { useFirstLineMargin = true; break; } Loading @@ -304,7 +304,7 @@ public abstract class Layout { } } boolean hasTabOrEmoji = getLineContainsTab(i); boolean hasTabOrEmoji = getLineContainsTab(lineNum); // Can't tell if we have tabs for sure, currently if (hasTabOrEmoji && !tabStopsIsInitialized) { if (tabStops == null) { Loading Loading @@ -333,7 +333,7 @@ public abstract class Layout { x = right; } } else { int max = (int)getLineExtent(i, tabStops, false); int max = (int)getLineExtent(lineNum, tabStops, false); if (align == Alignment.ALIGN_OPPOSITE) { if (dir == DIR_LEFT_TO_RIGHT) { x = right - max; Loading @@ -346,7 +346,8 @@ public abstract class Layout { } } Directions directions = getLineDirections(i); paint.setHyphenEdit(getHyphen(lineNum)); Directions directions = getLineDirections(lineNum); if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTabOrEmoji) { // XXX: assumes there's nothing additional to be done canvas.drawText(buf, start, end, x, lbaseline, paint); Loading Loading @@ -677,6 +678,15 @@ public abstract class Layout { */ public abstract int getBottomPadding(); /** * Returns the hyphen edit for a line. * * @hide */ public int getHyphen(int line) { return 0; } /** * Returns true if the character at offset and the preceding character Loading core/java/android/text/StaticLayout.java +29 −14 Original line number Diff line number Diff line Loading @@ -170,7 +170,8 @@ public class StaticLayout extends Layout { * Measurement and break iteration is done in native code. The protocol for using * the native code is as follows. * * For each paragraph, do a nSetText of the paragraph text. Also do nSetLineWidth. * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab * stops, break strategy (and possibly other parameters in the future). * * Then, for each run within the paragraph: * - setLocale (this must be done at least for the first run, optional afterwards) Loading @@ -187,7 +188,7 @@ public class StaticLayout extends Layout { private void setLocale(Locale locale) { if (!locale.equals(mLocale)) { nSetLocale(mNativePtr, locale.toLanguageTag()); nSetLocale(mNativePtr, locale.toLanguageTag(), Hyphenator.get(locale)); mLocale = locale; } } Loading Loading @@ -531,7 +532,7 @@ public class StaticLayout extends Layout { int[] breaks = lineBreaks.breaks; float[] lineWidths = lineBreaks.widths; boolean[] flags = lineBreaks.flags; int[] flags = lineBreaks.flags; // here is the offset of the starting character of the line we are currently measuring int here = paraStart; Loading Loading @@ -617,7 +618,7 @@ public class StaticLayout extends Layout { fm.top, fm.bottom, v, spacingmult, spacingadd, null, null, fm, false, null, fm, 0, needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd, includepad, trackpad, null, null, bufStart, ellipsize, Loading @@ -629,7 +630,7 @@ public class StaticLayout extends Layout { int above, int below, int top, int bottom, int v, float spacingmult, float spacingadd, LineHeightSpan[] chooseHt, int[] chooseHtv, Paint.FontMetricsInt fm, boolean hasTabOrEmoji, Paint.FontMetricsInt fm, int flags, boolean needMultiply, byte[] chdirs, int dir, boolean easy, int bufEnd, boolean includePad, boolean trackPad, char[] chs, Loading Loading @@ -722,8 +723,10 @@ public class StaticLayout extends Layout { lines[off + mColumns + START] = end; lines[off + mColumns + TOP] = v; if (hasTabOrEmoji) lines[off + TAB] |= TAB_MASK; // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining // one bit for start field lines[off + TAB] |= flags & TAB_MASK; lines[off + HYPHEN] = flags; lines[off + DIR] |= dir << DIR_SHIFT; Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT; Loading Loading @@ -942,6 +945,14 @@ public class StaticLayout extends Layout { return mBottomPadding; } /** * @hide */ @Override public int getHyphen(int line) { return mLines[mColumns * line + HYPHEN] & 0xff; } @Override public int getEllipsisCount(int line) { if (mColumns < COLUMNS_ELLIPSIZE) { Loading @@ -968,7 +979,10 @@ public class StaticLayout extends Layout { private static native long nNewBuilder(); private static native void nFreeBuilder(long nativePtr); private static native void nFinishBuilder(long nativePtr); private static native void nSetLocale(long nativePtr, String locale); /* package */ static native long nLoadHyphenator(String patternData); private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator); // Set up paragraph text and settings; done as one big method to minimize jni crossings private static native void nSetupParagraph(long nativePtr, char[] text, int length, Loading @@ -991,22 +1005,23 @@ public class StaticLayout extends Layout { // to reduce the number of JNI calls in the common case where the // arrays do not have to be resized private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle, int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength); int[] recycleBreaks, float[] recycleWidths, int[] recycleFlags, int recycleLength); private int mLineCount; private int mTopPadding, mBottomPadding; private int mColumns; private int mEllipsizedWidth; private static final int COLUMNS_NORMAL = 3; private static final int COLUMNS_ELLIPSIZE = 5; private static final int COLUMNS_NORMAL = 4; private static final int COLUMNS_ELLIPSIZE = 6; private static final int START = 0; private static final int DIR = START; private static final int TAB = START; private static final int TOP = 1; private static final int DESCENT = 2; private static final int ELLIPSIS_START = 3; private static final int ELLIPSIS_COUNT = 4; private static final int HYPHEN = 3; private static final int ELLIPSIS_START = 4; private static final int ELLIPSIS_COUNT = 5; private int[] mLines; private Directions[] mLineDirections; Loading @@ -1028,7 +1043,7 @@ public class StaticLayout extends Layout { private static final int INITIAL_SIZE = 16; public int[] breaks = new int[INITIAL_SIZE]; public float[] widths = new float[INITIAL_SIZE]; public boolean[] flags = new boolean[INITIAL_SIZE]; // hasTabOrEmoji public int[] flags = new int[INITIAL_SIZE]; // hasTabOrEmoji // breaks, widths, and flags should all have the same length } Loading core/java/android/text/TextLine.java +4 −0 Original line number Diff line number Diff line Loading @@ -955,6 +955,10 @@ class TextLine { span.updateDrawState(wp); } // Only draw hyphen on last run in line if (jnext < mLen) { wp.setHyphenEdit(0); } x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x, top, y, bottom, fmi, needWidth || jnext < measureLimit); } Loading Loading
core/java/android/text/DynamicLayout.java +15 −4 Original line number Diff line number Diff line Loading @@ -356,6 +356,8 @@ public class DynamicLayout extends Layout ints[DESCENT] = desc; objects[0] = reflowed.getLineDirections(i); ints[HYPHEN] = reflowed.getHyphen(i); if (mEllipsize) { ints[ELLIPSIS_START] = reflowed.getEllipsisStart(i); ints[ELLIPSIS_COUNT] = reflowed.getEllipsisCount(i); Loading Loading @@ -631,6 +633,14 @@ public class DynamicLayout extends Layout return mBottomPadding; } /** * @hide */ @Override public int getHyphen(int line) { return mInts.getValue(line, HYPHEN); } @Override public int getEllipsizedWidth() { return mEllipsizedWidth; Loading Loading @@ -739,11 +749,12 @@ public class DynamicLayout extends Layout private static final int TAB = START; private static final int TOP = 1; private static final int DESCENT = 2; private static final int COLUMNS_NORMAL = 3; private static final int HYPHEN = 3; private static final int COLUMNS_NORMAL = 4; private static final int ELLIPSIS_START = 3; private static final int ELLIPSIS_COUNT = 4; private static final int COLUMNS_ELLIPSIZE = 5; private static final int ELLIPSIS_START = 4; private static final int ELLIPSIS_COUNT = 5; private static final int COLUMNS_ELLIPSIZE = 6; private static final int START_MASK = 0x1FFFFFFF; private static final int DIR_SHIFT = 30; Loading
core/java/android/text/Hyphenator.java 0 → 100644 +76 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 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 android.util.Log; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.util.HashMap; import java.util.Locale; /** * Hyphenator is a wrapper class for a native implementation of automatic hyphenation, * in essence finding valid hyphenation opportunities in a word. * * @hide */ /* package */ class Hyphenator { // This class has deliberately simple lifetime management (no finalizer) because in // the common case a process will use a very small number of locales. private static String TAG = "Hyphenator"; static HashMap<Locale, Hyphenator> sMap = new HashMap<Locale, Hyphenator>(); private long mNativePtr; private Hyphenator(long nativePtr) { mNativePtr = nativePtr; } public static long get(Locale locale) { synchronized (sMap) { Hyphenator result = sMap.get(locale); if (result == null) { result = loadHyphenator(locale); sMap.put(locale, result); } return result == null ? 0 : result.mNativePtr; } } private static Hyphenator loadHyphenator(Locale locale) { // TODO: find pattern dictionary (from system location) that best matches locale if (Locale.US.equals(locale)) { File f = new File("/data/local/tmp/hyph-en-us.pat.txt"); try { RandomAccessFile rf = new RandomAccessFile(f, "r"); byte[] buf = new byte[(int)rf.length()]; rf.read(buf); rf.close(); String patternData = new String(buf); long nativePtr = StaticLayout.nLoadHyphenator(patternData); return new Hyphenator(nativePtr); } catch (IOException e) { Log.e(TAG, "error loading hyphenation " + f); } } return null; } }
core/java/android/text/Layout.java +21 −11 Original line number Diff line number Diff line Loading @@ -225,17 +225,17 @@ public abstract class Layout { // Draw the lines, one at a time. // The baseline is the top of the following line minus the current line's descent. for (int i = firstLine; i <= lastLine; i++) { for (int lineNum = firstLine; lineNum <= lastLine; lineNum++) { int start = previousLineEnd; previousLineEnd = getLineStart(i + 1); int end = getLineVisibleEnd(i, start, previousLineEnd); previousLineEnd = getLineStart(lineNum + 1); int end = getLineVisibleEnd(lineNum, start, previousLineEnd); int ltop = previousLineBottom; int lbottom = getLineTop(i+1); int lbottom = getLineTop(lineNum + 1); previousLineBottom = lbottom; int lbaseline = lbottom - getLineDescent(i); int lbaseline = lbottom - getLineDescent(lineNum); int dir = getParagraphDirection(i); int dir = getParagraphDirection(lineNum); int left = 0; int right = mWidth; Loading @@ -254,7 +254,7 @@ public abstract class Layout { // just collect the ones present at the start of the paragraph. // If spanEnd is before the end of the paragraph, that's not // our problem. if (start >= spanEnd && (i == firstLine || isFirstParaLine)) { if (start >= spanEnd && (lineNum == firstLine || isFirstParaLine)) { spanEnd = sp.nextSpanTransition(start, textLength, ParagraphStyle.class); spans = getParagraphSpans(sp, start, spanEnd, ParagraphStyle.class); Loading @@ -280,7 +280,7 @@ public abstract class Layout { int startLine = getLineForOffset(sp.getSpanStart(spans[n])); // if there is more than one LeadingMarginSpan2, use // the count that is greatest if (i < startLine + count) { if (lineNum < startLine + count) { useFirstLineMargin = true; break; } Loading @@ -304,7 +304,7 @@ public abstract class Layout { } } boolean hasTabOrEmoji = getLineContainsTab(i); boolean hasTabOrEmoji = getLineContainsTab(lineNum); // Can't tell if we have tabs for sure, currently if (hasTabOrEmoji && !tabStopsIsInitialized) { if (tabStops == null) { Loading Loading @@ -333,7 +333,7 @@ public abstract class Layout { x = right; } } else { int max = (int)getLineExtent(i, tabStops, false); int max = (int)getLineExtent(lineNum, tabStops, false); if (align == Alignment.ALIGN_OPPOSITE) { if (dir == DIR_LEFT_TO_RIGHT) { x = right - max; Loading @@ -346,7 +346,8 @@ public abstract class Layout { } } Directions directions = getLineDirections(i); paint.setHyphenEdit(getHyphen(lineNum)); Directions directions = getLineDirections(lineNum); if (directions == DIRS_ALL_LEFT_TO_RIGHT && !mSpannedText && !hasTabOrEmoji) { // XXX: assumes there's nothing additional to be done canvas.drawText(buf, start, end, x, lbaseline, paint); Loading Loading @@ -677,6 +678,15 @@ public abstract class Layout { */ public abstract int getBottomPadding(); /** * Returns the hyphen edit for a line. * * @hide */ public int getHyphen(int line) { return 0; } /** * Returns true if the character at offset and the preceding character Loading
core/java/android/text/StaticLayout.java +29 −14 Original line number Diff line number Diff line Loading @@ -170,7 +170,8 @@ public class StaticLayout extends Layout { * Measurement and break iteration is done in native code. The protocol for using * the native code is as follows. * * For each paragraph, do a nSetText of the paragraph text. Also do nSetLineWidth. * For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab * stops, break strategy (and possibly other parameters in the future). * * Then, for each run within the paragraph: * - setLocale (this must be done at least for the first run, optional afterwards) Loading @@ -187,7 +188,7 @@ public class StaticLayout extends Layout { private void setLocale(Locale locale) { if (!locale.equals(mLocale)) { nSetLocale(mNativePtr, locale.toLanguageTag()); nSetLocale(mNativePtr, locale.toLanguageTag(), Hyphenator.get(locale)); mLocale = locale; } } Loading Loading @@ -531,7 +532,7 @@ public class StaticLayout extends Layout { int[] breaks = lineBreaks.breaks; float[] lineWidths = lineBreaks.widths; boolean[] flags = lineBreaks.flags; int[] flags = lineBreaks.flags; // here is the offset of the starting character of the line we are currently measuring int here = paraStart; Loading Loading @@ -617,7 +618,7 @@ public class StaticLayout extends Layout { fm.top, fm.bottom, v, spacingmult, spacingadd, null, null, fm, false, null, fm, 0, needMultiply, measured.mLevels, measured.mDir, measured.mEasy, bufEnd, includepad, trackpad, null, null, bufStart, ellipsize, Loading @@ -629,7 +630,7 @@ public class StaticLayout extends Layout { int above, int below, int top, int bottom, int v, float spacingmult, float spacingadd, LineHeightSpan[] chooseHt, int[] chooseHtv, Paint.FontMetricsInt fm, boolean hasTabOrEmoji, Paint.FontMetricsInt fm, int flags, boolean needMultiply, byte[] chdirs, int dir, boolean easy, int bufEnd, boolean includePad, boolean trackPad, char[] chs, Loading Loading @@ -722,8 +723,10 @@ public class StaticLayout extends Layout { lines[off + mColumns + START] = end; lines[off + mColumns + TOP] = v; if (hasTabOrEmoji) lines[off + TAB] |= TAB_MASK; // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining // one bit for start field lines[off + TAB] |= flags & TAB_MASK; lines[off + HYPHEN] = flags; lines[off + DIR] |= dir << DIR_SHIFT; Directions linedirs = DIRS_ALL_LEFT_TO_RIGHT; Loading Loading @@ -942,6 +945,14 @@ public class StaticLayout extends Layout { return mBottomPadding; } /** * @hide */ @Override public int getHyphen(int line) { return mLines[mColumns * line + HYPHEN] & 0xff; } @Override public int getEllipsisCount(int line) { if (mColumns < COLUMNS_ELLIPSIZE) { Loading @@ -968,7 +979,10 @@ public class StaticLayout extends Layout { private static native long nNewBuilder(); private static native void nFreeBuilder(long nativePtr); private static native void nFinishBuilder(long nativePtr); private static native void nSetLocale(long nativePtr, String locale); /* package */ static native long nLoadHyphenator(String patternData); private static native void nSetLocale(long nativePtr, String locale, long nativeHyphenator); // Set up paragraph text and settings; done as one big method to minimize jni crossings private static native void nSetupParagraph(long nativePtr, char[] text, int length, Loading @@ -991,22 +1005,23 @@ public class StaticLayout extends Layout { // to reduce the number of JNI calls in the common case where the // arrays do not have to be resized private static native int nComputeLineBreaks(long nativePtr, LineBreaks recycle, int[] recycleBreaks, float[] recycleWidths, boolean[] recycleFlags, int recycleLength); int[] recycleBreaks, float[] recycleWidths, int[] recycleFlags, int recycleLength); private int mLineCount; private int mTopPadding, mBottomPadding; private int mColumns; private int mEllipsizedWidth; private static final int COLUMNS_NORMAL = 3; private static final int COLUMNS_ELLIPSIZE = 5; private static final int COLUMNS_NORMAL = 4; private static final int COLUMNS_ELLIPSIZE = 6; private static final int START = 0; private static final int DIR = START; private static final int TAB = START; private static final int TOP = 1; private static final int DESCENT = 2; private static final int ELLIPSIS_START = 3; private static final int ELLIPSIS_COUNT = 4; private static final int HYPHEN = 3; private static final int ELLIPSIS_START = 4; private static final int ELLIPSIS_COUNT = 5; private int[] mLines; private Directions[] mLineDirections; Loading @@ -1028,7 +1043,7 @@ public class StaticLayout extends Layout { private static final int INITIAL_SIZE = 16; public int[] breaks = new int[INITIAL_SIZE]; public float[] widths = new float[INITIAL_SIZE]; public boolean[] flags = new boolean[INITIAL_SIZE]; // hasTabOrEmoji public int[] flags = new int[INITIAL_SIZE]; // hasTabOrEmoji // breaks, widths, and flags should all have the same length } Loading
core/java/android/text/TextLine.java +4 −0 Original line number Diff line number Diff line Loading @@ -955,6 +955,10 @@ class TextLine { span.updateDrawState(wp); } // Only draw hyphen on last run in line if (jnext < mLen) { wp.setHyphenEdit(0); } x += handleText(wp, j, jnext, i, inext, runIsRtl, c, x, top, y, bottom, fmi, needWidth || jnext < measureLimit); } Loading