Loading core/java/android/text/Layout.java +5 −0 Original line number Diff line number Diff line Loading @@ -1937,6 +1937,11 @@ public abstract class Layout { public static final int DIR_LEFT_TO_RIGHT = 1; public static final int DIR_RIGHT_TO_LEFT = -1; /* package */ static final int DIR_REQUEST_LTR = 1; /* package */ static final int DIR_REQUEST_RTL = -1; /* package */ static final int DIR_REQUEST_DEFAULT_LTR = 2; /* package */ static final int DIR_REQUEST_DEFAULT_RTL = -2; public enum Alignment { ALIGN_NORMAL, ALIGN_OPPOSITE, Loading core/java/android/text/StaticLayout.java +239 −210 Original line number Diff line number Diff line Loading @@ -234,215 +234,9 @@ extends Layout } if (!easy) { AndroidCharacter.getDirectionalities(chs, chdirs, end - start); /* * Determine primary paragraph direction */ for (int j = start; j < end; j++) { int d = chdirs[j - start]; if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT) { dir = DIR_LEFT_TO_RIGHT; break; } if (d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { dir = DIR_RIGHT_TO_LEFT; break; } } /* * XXX Explicit overrides should go here */ /* * Weak type resolution */ final byte SOR = dir == DIR_LEFT_TO_RIGHT ? Character.DIRECTIONALITY_LEFT_TO_RIGHT : Character.DIRECTIONALITY_RIGHT_TO_LEFT; // dump(chdirs, n, "initial"); // W1 non spacing marks for (int j = 0; j < n; j++) { if (chdirs[j] == Character.NON_SPACING_MARK) { if (j == 0) chdirs[j] = SOR; else chdirs[j] = chdirs[j - 1]; } } // dump(chdirs, n, "W1"); // W2 european numbers byte cur = SOR; for (int j = 0; j < n; j++) { byte d = chdirs[j]; if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) cur = d; else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) { if (cur == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) chdirs[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; } } // dump(chdirs, n, "W2"); // W3 arabic letters for (int j = 0; j < n; j++) { if (chdirs[j] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) chdirs[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT; } // dump(chdirs, n, "W3"); // W4 single separator between numbers for (int j = 1; j < n - 1; j++) { byte d = chdirs[j]; byte prev = chdirs[j - 1]; byte next = chdirs[j + 1]; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) { if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; } else if (d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) { if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER && next == Character.DIRECTIONALITY_ARABIC_NUMBER) chdirs[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; } } // dump(chdirs, n, "W4"); // W5 european number terminators boolean adjacent = false; for (int j = 0; j < n; j++) { byte d = chdirs[j]; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) adjacent = true; else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR && adjacent) chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; else adjacent = false; } //dump(chdirs, n, "W5"); // W5 european number terminators part 2, // W6 separators and terminators adjacent = false; for (int j = n - 1; j >= 0; j--) { byte d = chdirs[j]; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) adjacent = true; else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR) { if (adjacent) chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; else chdirs[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; } else { adjacent = false; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR || d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR || d == Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR || d == Character.DIRECTIONALITY_SEGMENT_SEPARATOR) chdirs[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; } } // dump(chdirs, n, "W6"); // W7 strong direction of european numbers cur = SOR; for (int j = 0; j < n; j++) { byte d = chdirs[j]; if (d == SOR || d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) cur = d; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) chdirs[j] = cur; } // dump(chdirs, n, "W7"); // N1, N2 neutrals cur = SOR; for (int j = 0; j < n; j++) { byte d = chdirs[j]; if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { cur = d; } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER || d == Character.DIRECTIONALITY_ARABIC_NUMBER) { cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT; } else { byte dd = SOR; int k; for (k = j + 1; k < n; k++) { dd = chdirs[k]; if (dd == Character.DIRECTIONALITY_LEFT_TO_RIGHT || dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { break; } if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER || dd == Character.DIRECTIONALITY_ARABIC_NUMBER) { dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT; break; } } for (int y = j; y < k; y++) { if (dd == cur) chdirs[y] = cur; else chdirs[y] = SOR; } j = k - 1; } } // dump(chdirs, n, "final"); // extra: enforce that all tabs and surrogate characters go the // primary direction // TODO: actually do directions right for surrogates for (int j = 0; j < n; j++) { char c = chs[j]; if (c == '\t' || (c >= 0xD800 && c <= 0xDFFF)) { chdirs[j] = SOR; } } // extra: enforce that object replacements go to the // primary direction // and that none of the underlying characters are treated // as viable breakpoints // Ensure that none of the underlying characters are treated // as viable breakpoints, and that the entire run gets the // same bidi direction. if (source instanceof Spanned) { Spanned sp = (Spanned) source; Loading @@ -453,12 +247,14 @@ extends Layout int b = sp.getSpanEnd(spans[y]); for (int x = a; x < b; x++) { chdirs[x - start] = SOR; chs[x - start] = '\uFFFC'; } } } // XXX put override flags, etc. into chdirs dir = bidi(dir, chs, chdirs, n, false); // Do mirroring for right-to-left segments for (int i = 0; i < n; i++) { Loading Loading @@ -810,6 +606,239 @@ extends Layout } } /** * Runs the unicode bidi algorithm on the first n chars in chs, returning * the char dirs in chInfo and the base line direction of the first * paragraph. * * XXX change result from dirs to levels * * @param dir the direction flag, either DIR_REQUEST_LTR, * DIR_REQUEST_RTL, DIR_REQUEST_DEFAULT_LTR, or DIR_REQUEST_DEFAULT_RTL. * @param chs the text to examine * @param chInfo on input, if hasInfo is true, override and other flags * representing out-of-band embedding information. On output, the generated * dirs of the text. * @param n the length of the text/information in chs and chInfo * @param hasInfo true if chInfo has input information, otherwise the * input data in chInfo is ignored. * @return the resolved direction level of the first paragraph, either * DIR_LEFT_TO_RIGHT or DIR_RIGHT_TO_LEFT. */ /* package */ static int bidi(int dir, char[] chs, byte[] chInfo, int n, boolean hasInfo) { AndroidCharacter.getDirectionalities(chs, chInfo, n); /* * Determine primary paragraph direction if not specified */ if (dir != DIR_REQUEST_LTR && dir != DIR_REQUEST_RTL) { // set up default dir = dir >= 0 ? DIR_LEFT_TO_RIGHT : DIR_RIGHT_TO_LEFT; for (int j = 0; j < n; j++) { int d = chInfo[j]; if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT) { dir = DIR_LEFT_TO_RIGHT; break; } if (d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { dir = DIR_RIGHT_TO_LEFT; break; } } } final byte SOR = dir == DIR_LEFT_TO_RIGHT ? Character.DIRECTIONALITY_LEFT_TO_RIGHT : Character.DIRECTIONALITY_RIGHT_TO_LEFT; /* * XXX Explicit overrides should go here */ /* * Weak type resolution */ // dump(chdirs, n, "initial"); // W1 non spacing marks for (int j = 0; j < n; j++) { if (chInfo[j] == Character.NON_SPACING_MARK) { if (j == 0) chInfo[j] = SOR; else chInfo[j] = chInfo[j - 1]; } } // dump(chdirs, n, "W1"); // W2 european numbers byte cur = SOR; for (int j = 0; j < n; j++) { byte d = chInfo[j]; if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) cur = d; else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) { if (cur == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; } } // dump(chdirs, n, "W2"); // W3 arabic letters for (int j = 0; j < n; j++) { if (chInfo[j] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) chInfo[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT; } // dump(chdirs, n, "W3"); // W4 single separator between numbers for (int j = 1; j < n - 1; j++) { byte d = chInfo[j]; byte prev = chInfo[j - 1]; byte next = chInfo[j + 1]; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) { if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; } else if (d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) { if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER && next == Character.DIRECTIONALITY_ARABIC_NUMBER) chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; } } // dump(chdirs, n, "W4"); // W5 european number terminators boolean adjacent = false; for (int j = 0; j < n; j++) { byte d = chInfo[j]; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) adjacent = true; else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR && adjacent) chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; else adjacent = false; } //dump(chdirs, n, "W5"); // W5 european number terminators part 2, // W6 separators and terminators adjacent = false; for (int j = n - 1; j >= 0; j--) { byte d = chInfo[j]; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) adjacent = true; else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR) { if (adjacent) chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; else chInfo[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; } else { adjacent = false; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR || d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR || d == Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR || d == Character.DIRECTIONALITY_SEGMENT_SEPARATOR) chInfo[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; } } // dump(chdirs, n, "W6"); // W7 strong direction of european numbers cur = SOR; for (int j = 0; j < n; j++) { byte d = chInfo[j]; if (d == SOR || d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) cur = d; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) chInfo[j] = cur; } // dump(chdirs, n, "W7"); // N1, N2 neutrals cur = SOR; for (int j = 0; j < n; j++) { byte d = chInfo[j]; if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { cur = d; } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER || d == Character.DIRECTIONALITY_ARABIC_NUMBER) { cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT; } else { byte dd = SOR; int k; for (k = j + 1; k < n; k++) { dd = chInfo[k]; if (dd == Character.DIRECTIONALITY_LEFT_TO_RIGHT || dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { break; } if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER || dd == Character.DIRECTIONALITY_ARABIC_NUMBER) { dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT; break; } } for (int y = j; y < k; y++) { if (dd == cur) chInfo[y] = cur; else chInfo[y] = SOR; } j = k - 1; } } // dump(chdirs, n, "final"); // extra: enforce that all tabs and surrogate characters go the // primary direction // TODO: actually do directions right for surrogates for (int j = 0; j < n; j++) { char c = chs[j]; if (c == '\t' || (c >= 0xD800 && c <= 0xDFFF)) { chInfo[j] = SOR; } } return dir; } private static final char FIRST_CJK = '\u2E80'; /** * Returns true if the specified character is one of those specified Loading core/tests/coretests/src/android/text/StaticLayoutBidiTest.java 0 → 100644 +106 −0 Original line number Diff line number Diff line /* * Copyright (C) 2010 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.test.suitebuilder.annotation.SmallTest; import android.util.Log; import junit.framework.TestCase; /** * Tests StaticLayout bidi implementation. */ public class StaticLayoutBidiTest extends TestCase { public static final int REQ_DL = 2; // Layout.DIR_REQUEST_DEFAULT_LTR; public static final int REQ_DR = -2; // Layout.DIR_REQUEST_DEFAULT_RTL; public static final int REQ_L = 1; // Layout.DIR_REQUEST_LTR; public static final int REQ_R = -1; // Layout.DIR_REQUEST_RTL; public static final int L = Layout.DIR_LEFT_TO_RIGHT; public static final int R = Layout.DIR_RIGHT_TO_LEFT; public static final String SP = " "; public static final String ALEF = "\u05d0"; public static final String BET = "\u05d1"; public static final String GIMEL = "\u05d2"; public static final String DALET = "\u05d3"; @SmallTest public void testAllLtr() { expectBidi(REQ_DL, "a test", "000000", L); } @SmallTest public void testLtrRtl() { expectBidi(REQ_DL, "abc " + ALEF + BET + GIMEL, "0000111", L); } @SmallTest public void testAllRtl() { expectBidi(REQ_DL, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", R); } @SmallTest public void testRtlLtr() { expectBidi(REQ_DL, ALEF + BET + GIMEL + " abc", "1111000", R); } @SmallTest public void testRAllLtr() { expectBidi(REQ_R, "a test", "000000", R); } @SmallTest public void testRLtrRtl() { expectBidi(REQ_R, "abc " + ALEF + BET + GIMEL, "0001111", R); } @SmallTest public void testLAllRtl() { expectBidi(REQ_L, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", L); } @SmallTest public void testLRtlLtr() { expectBidi(REQ_L, ALEF + BET + GIMEL + " abc", "1110000", L); } private void expectBidi(int dir, String text, String expectedLevels, int expectedDir) { char[] chs = text.toCharArray(); int n = chs.length; byte[] chInfo = new byte[n]; int resultDir = StaticLayout.bidi(dir, chs, chInfo, n, false); { StringBuilder sb = new StringBuilder("xdirs:"); for (int i = 0; i < n; ++i) { sb.append(" ").append(String.valueOf(chInfo[i])); } Log.i("BIDI", sb.toString()); } char[] resultLevelChars = new char[n]; for (int i = 0; i < n; ++i) { resultLevelChars[i] = (char)('0' + chInfo[i]); } String resultLevels = new String(resultLevelChars); assertEquals("direction", expectedDir, resultDir); assertEquals("levels", expectedLevels, resultLevels); } } Loading
core/java/android/text/Layout.java +5 −0 Original line number Diff line number Diff line Loading @@ -1937,6 +1937,11 @@ public abstract class Layout { public static final int DIR_LEFT_TO_RIGHT = 1; public static final int DIR_RIGHT_TO_LEFT = -1; /* package */ static final int DIR_REQUEST_LTR = 1; /* package */ static final int DIR_REQUEST_RTL = -1; /* package */ static final int DIR_REQUEST_DEFAULT_LTR = 2; /* package */ static final int DIR_REQUEST_DEFAULT_RTL = -2; public enum Alignment { ALIGN_NORMAL, ALIGN_OPPOSITE, Loading
core/java/android/text/StaticLayout.java +239 −210 Original line number Diff line number Diff line Loading @@ -234,215 +234,9 @@ extends Layout } if (!easy) { AndroidCharacter.getDirectionalities(chs, chdirs, end - start); /* * Determine primary paragraph direction */ for (int j = start; j < end; j++) { int d = chdirs[j - start]; if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT) { dir = DIR_LEFT_TO_RIGHT; break; } if (d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { dir = DIR_RIGHT_TO_LEFT; break; } } /* * XXX Explicit overrides should go here */ /* * Weak type resolution */ final byte SOR = dir == DIR_LEFT_TO_RIGHT ? Character.DIRECTIONALITY_LEFT_TO_RIGHT : Character.DIRECTIONALITY_RIGHT_TO_LEFT; // dump(chdirs, n, "initial"); // W1 non spacing marks for (int j = 0; j < n; j++) { if (chdirs[j] == Character.NON_SPACING_MARK) { if (j == 0) chdirs[j] = SOR; else chdirs[j] = chdirs[j - 1]; } } // dump(chdirs, n, "W1"); // W2 european numbers byte cur = SOR; for (int j = 0; j < n; j++) { byte d = chdirs[j]; if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) cur = d; else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) { if (cur == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) chdirs[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; } } // dump(chdirs, n, "W2"); // W3 arabic letters for (int j = 0; j < n; j++) { if (chdirs[j] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) chdirs[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT; } // dump(chdirs, n, "W3"); // W4 single separator between numbers for (int j = 1; j < n - 1; j++) { byte d = chdirs[j]; byte prev = chdirs[j - 1]; byte next = chdirs[j + 1]; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) { if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; } else if (d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) { if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER && next == Character.DIRECTIONALITY_ARABIC_NUMBER) chdirs[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; } } // dump(chdirs, n, "W4"); // W5 european number terminators boolean adjacent = false; for (int j = 0; j < n; j++) { byte d = chdirs[j]; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) adjacent = true; else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR && adjacent) chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; else adjacent = false; } //dump(chdirs, n, "W5"); // W5 european number terminators part 2, // W6 separators and terminators adjacent = false; for (int j = n - 1; j >= 0; j--) { byte d = chdirs[j]; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) adjacent = true; else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR) { if (adjacent) chdirs[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; else chdirs[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; } else { adjacent = false; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR || d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR || d == Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR || d == Character.DIRECTIONALITY_SEGMENT_SEPARATOR) chdirs[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; } } // dump(chdirs, n, "W6"); // W7 strong direction of european numbers cur = SOR; for (int j = 0; j < n; j++) { byte d = chdirs[j]; if (d == SOR || d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) cur = d; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) chdirs[j] = cur; } // dump(chdirs, n, "W7"); // N1, N2 neutrals cur = SOR; for (int j = 0; j < n; j++) { byte d = chdirs[j]; if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { cur = d; } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER || d == Character.DIRECTIONALITY_ARABIC_NUMBER) { cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT; } else { byte dd = SOR; int k; for (k = j + 1; k < n; k++) { dd = chdirs[k]; if (dd == Character.DIRECTIONALITY_LEFT_TO_RIGHT || dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { break; } if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER || dd == Character.DIRECTIONALITY_ARABIC_NUMBER) { dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT; break; } } for (int y = j; y < k; y++) { if (dd == cur) chdirs[y] = cur; else chdirs[y] = SOR; } j = k - 1; } } // dump(chdirs, n, "final"); // extra: enforce that all tabs and surrogate characters go the // primary direction // TODO: actually do directions right for surrogates for (int j = 0; j < n; j++) { char c = chs[j]; if (c == '\t' || (c >= 0xD800 && c <= 0xDFFF)) { chdirs[j] = SOR; } } // extra: enforce that object replacements go to the // primary direction // and that none of the underlying characters are treated // as viable breakpoints // Ensure that none of the underlying characters are treated // as viable breakpoints, and that the entire run gets the // same bidi direction. if (source instanceof Spanned) { Spanned sp = (Spanned) source; Loading @@ -453,12 +247,14 @@ extends Layout int b = sp.getSpanEnd(spans[y]); for (int x = a; x < b; x++) { chdirs[x - start] = SOR; chs[x - start] = '\uFFFC'; } } } // XXX put override flags, etc. into chdirs dir = bidi(dir, chs, chdirs, n, false); // Do mirroring for right-to-left segments for (int i = 0; i < n; i++) { Loading Loading @@ -810,6 +606,239 @@ extends Layout } } /** * Runs the unicode bidi algorithm on the first n chars in chs, returning * the char dirs in chInfo and the base line direction of the first * paragraph. * * XXX change result from dirs to levels * * @param dir the direction flag, either DIR_REQUEST_LTR, * DIR_REQUEST_RTL, DIR_REQUEST_DEFAULT_LTR, or DIR_REQUEST_DEFAULT_RTL. * @param chs the text to examine * @param chInfo on input, if hasInfo is true, override and other flags * representing out-of-band embedding information. On output, the generated * dirs of the text. * @param n the length of the text/information in chs and chInfo * @param hasInfo true if chInfo has input information, otherwise the * input data in chInfo is ignored. * @return the resolved direction level of the first paragraph, either * DIR_LEFT_TO_RIGHT or DIR_RIGHT_TO_LEFT. */ /* package */ static int bidi(int dir, char[] chs, byte[] chInfo, int n, boolean hasInfo) { AndroidCharacter.getDirectionalities(chs, chInfo, n); /* * Determine primary paragraph direction if not specified */ if (dir != DIR_REQUEST_LTR && dir != DIR_REQUEST_RTL) { // set up default dir = dir >= 0 ? DIR_LEFT_TO_RIGHT : DIR_RIGHT_TO_LEFT; for (int j = 0; j < n; j++) { int d = chInfo[j]; if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT) { dir = DIR_LEFT_TO_RIGHT; break; } if (d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { dir = DIR_RIGHT_TO_LEFT; break; } } } final byte SOR = dir == DIR_LEFT_TO_RIGHT ? Character.DIRECTIONALITY_LEFT_TO_RIGHT : Character.DIRECTIONALITY_RIGHT_TO_LEFT; /* * XXX Explicit overrides should go here */ /* * Weak type resolution */ // dump(chdirs, n, "initial"); // W1 non spacing marks for (int j = 0; j < n; j++) { if (chInfo[j] == Character.NON_SPACING_MARK) { if (j == 0) chInfo[j] = SOR; else chInfo[j] = chInfo[j - 1]; } } // dump(chdirs, n, "W1"); // W2 european numbers byte cur = SOR; for (int j = 0; j < n; j++) { byte d = chInfo[j]; if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) cur = d; else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) { if (cur == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; } } // dump(chdirs, n, "W2"); // W3 arabic letters for (int j = 0; j < n; j++) { if (chInfo[j] == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) chInfo[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT; } // dump(chdirs, n, "W3"); // W4 single separator between numbers for (int j = 1; j < n - 1; j++) { byte d = chInfo[j]; byte prev = chInfo[j - 1]; byte next = chInfo[j + 1]; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) { if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; } else if (d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR) { if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER && next == Character.DIRECTIONALITY_ARABIC_NUMBER) chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; } } // dump(chdirs, n, "W4"); // W5 european number terminators boolean adjacent = false; for (int j = 0; j < n; j++) { byte d = chInfo[j]; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) adjacent = true; else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR && adjacent) chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; else adjacent = false; } //dump(chdirs, n, "W5"); // W5 european number terminators part 2, // W6 separators and terminators adjacent = false; for (int j = n - 1; j >= 0; j--) { byte d = chInfo[j]; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) adjacent = true; else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_TERMINATOR) { if (adjacent) chInfo[j] = Character.DIRECTIONALITY_EUROPEAN_NUMBER; else chInfo[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; } else { adjacent = false; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR || d == Character.DIRECTIONALITY_COMMON_NUMBER_SEPARATOR || d == Character.DIRECTIONALITY_PARAGRAPH_SEPARATOR || d == Character.DIRECTIONALITY_SEGMENT_SEPARATOR) chInfo[j] = Character.DIRECTIONALITY_OTHER_NEUTRALS; } } // dump(chdirs, n, "W6"); // W7 strong direction of european numbers cur = SOR; for (int j = 0; j < n; j++) { byte d = chInfo[j]; if (d == SOR || d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) cur = d; if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) chInfo[j] = cur; } // dump(chdirs, n, "W7"); // N1, N2 neutrals cur = SOR; for (int j = 0; j < n; j++) { byte d = chInfo[j]; if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { cur = d; } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER || d == Character.DIRECTIONALITY_ARABIC_NUMBER) { cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT; } else { byte dd = SOR; int k; for (k = j + 1; k < n; k++) { dd = chInfo[k]; if (dd == Character.DIRECTIONALITY_LEFT_TO_RIGHT || dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { break; } if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER || dd == Character.DIRECTIONALITY_ARABIC_NUMBER) { dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT; break; } } for (int y = j; y < k; y++) { if (dd == cur) chInfo[y] = cur; else chInfo[y] = SOR; } j = k - 1; } } // dump(chdirs, n, "final"); // extra: enforce that all tabs and surrogate characters go the // primary direction // TODO: actually do directions right for surrogates for (int j = 0; j < n; j++) { char c = chs[j]; if (c == '\t' || (c >= 0xD800 && c <= 0xDFFF)) { chInfo[j] = SOR; } } return dir; } private static final char FIRST_CJK = '\u2E80'; /** * Returns true if the specified character is one of those specified Loading
core/tests/coretests/src/android/text/StaticLayoutBidiTest.java 0 → 100644 +106 −0 Original line number Diff line number Diff line /* * Copyright (C) 2010 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.test.suitebuilder.annotation.SmallTest; import android.util.Log; import junit.framework.TestCase; /** * Tests StaticLayout bidi implementation. */ public class StaticLayoutBidiTest extends TestCase { public static final int REQ_DL = 2; // Layout.DIR_REQUEST_DEFAULT_LTR; public static final int REQ_DR = -2; // Layout.DIR_REQUEST_DEFAULT_RTL; public static final int REQ_L = 1; // Layout.DIR_REQUEST_LTR; public static final int REQ_R = -1; // Layout.DIR_REQUEST_RTL; public static final int L = Layout.DIR_LEFT_TO_RIGHT; public static final int R = Layout.DIR_RIGHT_TO_LEFT; public static final String SP = " "; public static final String ALEF = "\u05d0"; public static final String BET = "\u05d1"; public static final String GIMEL = "\u05d2"; public static final String DALET = "\u05d3"; @SmallTest public void testAllLtr() { expectBidi(REQ_DL, "a test", "000000", L); } @SmallTest public void testLtrRtl() { expectBidi(REQ_DL, "abc " + ALEF + BET + GIMEL, "0000111", L); } @SmallTest public void testAllRtl() { expectBidi(REQ_DL, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", R); } @SmallTest public void testRtlLtr() { expectBidi(REQ_DL, ALEF + BET + GIMEL + " abc", "1111000", R); } @SmallTest public void testRAllLtr() { expectBidi(REQ_R, "a test", "000000", R); } @SmallTest public void testRLtrRtl() { expectBidi(REQ_R, "abc " + ALEF + BET + GIMEL, "0001111", R); } @SmallTest public void testLAllRtl() { expectBidi(REQ_L, ALEF + SP + ALEF + BET + GIMEL + DALET, "111111", L); } @SmallTest public void testLRtlLtr() { expectBidi(REQ_L, ALEF + BET + GIMEL + " abc", "1110000", L); } private void expectBidi(int dir, String text, String expectedLevels, int expectedDir) { char[] chs = text.toCharArray(); int n = chs.length; byte[] chInfo = new byte[n]; int resultDir = StaticLayout.bidi(dir, chs, chInfo, n, false); { StringBuilder sb = new StringBuilder("xdirs:"); for (int i = 0; i < n; ++i) { sb.append(" ").append(String.valueOf(chInfo[i])); } Log.i("BIDI", sb.toString()); } char[] resultLevelChars = new char[n]; for (int i = 0; i < n; ++i) { resultLevelChars[i] = (char)('0' + chInfo[i]); } String resultLevels = new String(resultLevelChars); assertEquals("direction", expectedDir, resultDir); assertEquals("levels", expectedLevels, resultLevels); } }