Merge "Use bidi heuristics correctly in BoringLayout#isBoring()"
This commit is contained in:
committed by
Android (Google) Code Review
commit
61b54967e5
@@ -220,16 +220,6 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
|
||||
return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if not boring; the width, ascent, and descent if boring.
|
||||
* @hide
|
||||
*/
|
||||
public static Metrics isBoring(CharSequence text,
|
||||
TextPaint paint,
|
||||
TextDirectionHeuristic textDir) {
|
||||
return isBoring(text, paint, textDir, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if not boring; the width, ascent, and descent in the
|
||||
* provided Metrics object (or a new one if the provided one was null)
|
||||
@@ -239,6 +229,45 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
|
||||
return isBoring(text, paint, TextDirectionHeuristics.FIRSTSTRONG_LTR, metrics);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the text contains any RTL characters, bidi format characters, or surrogate
|
||||
* code units.
|
||||
*/
|
||||
private static boolean hasAnyInterestingChars(CharSequence text, int textLength) {
|
||||
final int MAX_BUF_LEN = 500;
|
||||
final char[] buffer = TextUtils.obtain(MAX_BUF_LEN);
|
||||
try {
|
||||
for (int start = 0; start < textLength; start += MAX_BUF_LEN) {
|
||||
final int end = Math.min(start + MAX_BUF_LEN, textLength);
|
||||
|
||||
// No need to worry about getting half codepoints, since we consider surrogate code
|
||||
// units "interesting" as soon we see one.
|
||||
TextUtils.getChars(text, start, end, buffer, 0);
|
||||
|
||||
final int len = end - start;
|
||||
for (int i = 0; i < len; i++) {
|
||||
final char c = buffer[i];
|
||||
|
||||
if (c == '\n' || c == '\t' ||
|
||||
(c >= 0x0590 && c <= 0x08FF) || // RTL scripts
|
||||
c == 0x200E || // Bidi format character
|
||||
c == 0x200F || // Bidi format character
|
||||
(c >= 0x202A && c <= 0x202E) || // Bidi format characters
|
||||
(c >= 0x2066 && c <= 0x2069) || // Bidi format characters
|
||||
(c >= 0xD800 && c <= 0xDFFF) || // surrogate pairs
|
||||
(c >= 0xFB1D && c <= 0xFDFF) || // Hebrew and Arabic presentation forms
|
||||
(c >= 0xFE70 && c <= 0xFEFE) // Arabic presentation forms
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
TextUtils.recycle(buffer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null if not boring; the width, ascent, and descent in the
|
||||
* provided Metrics object (or a new one if the provided one was null)
|
||||
@@ -247,73 +276,35 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
|
||||
*/
|
||||
public static Metrics isBoring(CharSequence text, TextPaint paint,
|
||||
TextDirectionHeuristic textDir, Metrics metrics) {
|
||||
final int MAX_BUF_LEN = 500;
|
||||
final char[] buffer = TextUtils.obtain(MAX_BUF_LEN);
|
||||
final int textLength = text.length();
|
||||
boolean boring = true;
|
||||
|
||||
outer:
|
||||
for (int start = 0; start < textLength; start += MAX_BUF_LEN) {
|
||||
final int end = Math.min(start + MAX_BUF_LEN, textLength);
|
||||
|
||||
// No need to worry about getting half codepoints, since we reject surrogate code units
|
||||
// as non-boring as soon we see one.
|
||||
TextUtils.getChars(text, start, end, buffer, 0);
|
||||
|
||||
final int len = end - start;
|
||||
for (int i = 0; i < len; i++) {
|
||||
final char c = buffer[i];
|
||||
|
||||
if (c == '\n' || c == '\t' ||
|
||||
(c >= 0x0590 && c <= 0x08FF) || // RTL scripts
|
||||
c == 0x200F || // Bidi format character
|
||||
(c >= 0x202A && c <= 0x202E) || // Bidi format characters
|
||||
(c >= 0x2066 && c <= 0x2069) || // Bidi format characters
|
||||
(c >= 0xD800 && c <= 0xDFFF) || // surrogate pairs
|
||||
(c >= 0xFB1D && c <= 0xFDFF) || // Hebrew and Arabic presentation forms
|
||||
(c >= 0xFE70 && c <= 0xFEFE) // Arabic presentation forms
|
||||
) {
|
||||
boring = false;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This looks a little suspicious, and in some cases can result in O(n^2)
|
||||
// run time. Consider moving outside the loop.
|
||||
if (textDir != null && textDir.isRtl(buffer, 0, len)) {
|
||||
boring = false;
|
||||
break outer;
|
||||
}
|
||||
if (hasAnyInterestingChars(text, textLength)) {
|
||||
return null; // There are some interesting characters. Not boring.
|
||||
}
|
||||
|
||||
TextUtils.recycle(buffer);
|
||||
|
||||
if (boring && text instanceof Spanned) {
|
||||
if (textDir != null && textDir.isRtl(text, 0, textLength)) {
|
||||
return null; // The heuristic considers the whole text RTL. Not boring.
|
||||
}
|
||||
if (text instanceof Spanned) {
|
||||
Spanned sp = (Spanned) text;
|
||||
Object[] styles = sp.getSpans(0, textLength, ParagraphStyle.class);
|
||||
if (styles.length > 0) {
|
||||
boring = false;
|
||||
return null; // There are some PargraphStyle spans. Not boring.
|
||||
}
|
||||
}
|
||||
|
||||
if (boring) {
|
||||
Metrics fm = metrics;
|
||||
if (fm == null) {
|
||||
fm = new Metrics();
|
||||
} else {
|
||||
fm.reset();
|
||||
}
|
||||
|
||||
TextLine line = TextLine.obtain();
|
||||
line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
|
||||
Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
|
||||
fm.width = (int) Math.ceil(line.metrics(fm));
|
||||
TextLine.recycle(line);
|
||||
|
||||
return fm;
|
||||
Metrics fm = metrics;
|
||||
if (fm == null) {
|
||||
fm = new Metrics();
|
||||
} else {
|
||||
return null;
|
||||
fm.reset();
|
||||
}
|
||||
|
||||
TextLine line = TextLine.obtain();
|
||||
line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
|
||||
Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
|
||||
fm.width = (int) Math.ceil(line.metrics(fm));
|
||||
TextLine.recycle(line);
|
||||
|
||||
return fm;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user