1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.text; 18 19 import android.text.Layout.Directions; 20 21 import com.android.internal.annotations.VisibleForTesting; 22 23 /** 24 * Access the ICU bidi implementation. 25 * @hide 26 */ 27 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 28 public class AndroidBidi { 29 bidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo)30 public static int bidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo) { 31 if (chs == null || chInfo == null) { 32 throw new NullPointerException(); 33 } 34 35 if (n < 0 || chs.length < n || chInfo.length < n) { 36 throw new IndexOutOfBoundsException(); 37 } 38 39 switch(dir) { 40 case Layout.DIR_REQUEST_LTR: dir = 0; break; 41 case Layout.DIR_REQUEST_RTL: dir = 1; break; 42 case Layout.DIR_REQUEST_DEFAULT_LTR: dir = -2; break; 43 case Layout.DIR_REQUEST_DEFAULT_RTL: dir = -1; break; 44 default: dir = 0; break; 45 } 46 47 int result = runBidi(dir, chs, chInfo, n, haveInfo); 48 result = (result & 0x1) == 0 ? Layout.DIR_LEFT_TO_RIGHT : Layout.DIR_RIGHT_TO_LEFT; 49 return result; 50 } 51 52 /** 53 * Returns run direction information for a line within a paragraph. 54 * 55 * @param dir base line direction, either Layout.DIR_LEFT_TO_RIGHT or 56 * Layout.DIR_RIGHT_TO_LEFT 57 * @param levels levels as returned from {@link #bidi} 58 * @param lstart start of the line in the levels array 59 * @param chars the character array (used to determine whitespace) 60 * @param cstart the start of the line in the chars array 61 * @param len the length of the line 62 * @return the directions 63 */ directions(int dir, byte[] levels, int lstart, char[] chars, int cstart, int len)64 public static Directions directions(int dir, byte[] levels, int lstart, 65 char[] chars, int cstart, int len) { 66 if (len == 0) { 67 return Layout.DIRS_ALL_LEFT_TO_RIGHT; 68 } 69 70 int baseLevel = dir == Layout.DIR_LEFT_TO_RIGHT ? 0 : 1; 71 int curLevel = levels[lstart]; 72 int minLevel = curLevel; 73 int runCount = 1; 74 for (int i = lstart + 1, e = lstart + len; i < e; ++i) { 75 int level = levels[i]; 76 if (level != curLevel) { 77 curLevel = level; 78 ++runCount; 79 } 80 } 81 82 // add final run for trailing counter-directional whitespace 83 int visLen = len; 84 if ((curLevel & 1) != (baseLevel & 1)) { 85 // look for visible end 86 while (--visLen >= 0) { 87 char ch = chars[cstart + visLen]; 88 89 if (ch == '\n') { 90 --visLen; 91 break; 92 } 93 94 if (ch != ' ' && ch != '\t') { 95 break; 96 } 97 } 98 ++visLen; 99 if (visLen != len) { 100 ++runCount; 101 } 102 } 103 104 if (runCount == 1 && minLevel == baseLevel) { 105 // we're done, only one run on this line 106 if ((minLevel & 1) != 0) { 107 return Layout.DIRS_ALL_RIGHT_TO_LEFT; 108 } 109 return Layout.DIRS_ALL_LEFT_TO_RIGHT; 110 } 111 112 int[] ld = new int[runCount * 2]; 113 int maxLevel = minLevel; 114 int levelBits = minLevel << Layout.RUN_LEVEL_SHIFT; 115 { 116 // Start of first pair is always 0, we write 117 // length then start at each new run, and the 118 // last run length after we're done. 119 int n = 1; 120 int prev = lstart; 121 curLevel = minLevel; 122 for (int i = lstart, e = lstart + visLen; i < e; ++i) { 123 int level = levels[i]; 124 if (level != curLevel) { 125 curLevel = level; 126 if (level > maxLevel) { 127 maxLevel = level; 128 } else if (level < minLevel) { 129 minLevel = level; 130 } 131 // XXX ignore run length limit of 2^RUN_LEVEL_SHIFT 132 ld[n++] = (i - prev) | levelBits; 133 ld[n++] = i - lstart; 134 levelBits = curLevel << Layout.RUN_LEVEL_SHIFT; 135 prev = i; 136 } 137 } 138 ld[n] = (lstart + visLen - prev) | levelBits; 139 if (visLen < len) { 140 ld[++n] = visLen; 141 ld[++n] = (len - visLen) | (baseLevel << Layout.RUN_LEVEL_SHIFT); 142 } 143 } 144 145 // See if we need to swap any runs. 146 // If the min level run direction doesn't match the base 147 // direction, we always need to swap (at this point 148 // we have more than one run). 149 // Otherwise, we don't need to swap the lowest level. 150 // Since there are no logically adjacent runs at the same 151 // level, if the max level is the same as the (new) min 152 // level, we have a series of alternating levels that 153 // is already in order, so there's no more to do. 154 // 155 boolean swap; 156 if ((minLevel & 1) == baseLevel) { 157 minLevel += 1; 158 swap = maxLevel > minLevel; 159 } else { 160 swap = runCount > 1; 161 } 162 if (swap) { 163 for (int level = maxLevel - 1; level >= minLevel; --level) { 164 for (int i = 0; i < ld.length; i += 2) { 165 if (levels[ld[i]] >= level) { 166 int e = i + 2; 167 while (e < ld.length && levels[ld[e]] >= level) { 168 e += 2; 169 } 170 for (int low = i, hi = e - 2; low < hi; low += 2, hi -= 2) { 171 int x = ld[low]; ld[low] = ld[hi]; ld[hi] = x; 172 x = ld[low+1]; ld[low+1] = ld[hi+1]; ld[hi+1] = x; 173 } 174 i = e + 2; 175 } 176 } 177 } 178 } 179 return new Directions(ld); 180 } 181 runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo)182 private native static int runBidi(int dir, char[] chs, byte[] chInfo, int n, boolean haveInfo); 183 }