diff options
Diffstat (limited to 'core/java/android/text')
-rw-r--r-- | core/java/android/text/DynamicLayout.java | 19 | ||||
-rw-r--r-- | core/java/android/text/Hyphenator.java | 76 | ||||
-rw-r--r-- | core/java/android/text/Layout.java | 32 | ||||
-rw-r--r-- | core/java/android/text/StaticLayout.java | 43 | ||||
-rw-r--r-- | core/java/android/text/TextLine.java | 4 |
5 files changed, 145 insertions, 29 deletions
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java index 1bdaef0..7d2e1ef 100644 --- a/core/java/android/text/DynamicLayout.java +++ b/core/java/android/text/DynamicLayout.java @@ -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); @@ -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; @@ -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; diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java new file mode 100644 index 0000000..f4dff9b --- /dev/null +++ b/core/java/android/text/Hyphenator.java @@ -0,0 +1,76 @@ +/* + * 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; + } +} diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 928bf16..22abb18 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -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; @@ -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); @@ -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; } @@ -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) { @@ -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; @@ -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); @@ -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 diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index b47418f..4174df0 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -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) @@ -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; } } @@ -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; @@ -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, @@ -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, @@ -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; @@ -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) { @@ -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, @@ -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; @@ -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 } diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index 4725581..479242c 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -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); } |