diff options
-rwxr-xr-x | core/java/android/text/Layout.java | 35 | ||||
-rwxr-xr-x | core/java/android/text/StaticLayout.java | 110 | ||||
-rw-r--r-- | graphics/java/android/graphics/Canvas.java | 166 |
3 files changed, 149 insertions, 162 deletions
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 1211f41..112e007 100755 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1818,43 +1818,12 @@ public abstract class Layout { // are left-to-right, the others are right-to-left. So, for example, // a line that starts with a right-to-left run has 0 at mDirections[0], // since the 'first' (ltr) run is zero length. - // - // The code currently assumes that each run is adjacent to the previous - // one, progressing in the base line direction. This isn't sufficient - // to handle nested runs, for example numeric text in an rtl context - // in an ltr paragraph. /* package */ Directions(short[] dirs) { mDirections = dirs; } - static int baseDirection(Directions dir,int length) { - if (dir == DIRS_ALL_LEFT_TO_RIGHT) { - return DIR_LEFT_TO_RIGHT; - } else if (dir == DIRS_ALL_RIGHT_TO_LEFT) { - return DIR_RIGHT_TO_LEFT; - } - - int sum=0; - int lastSwitch=0; - int i=0; - while ((i+1) < dir.mDirections.length) { - sum+=dir.mDirections[i];//-lastSwitch; - sum-=dir.mDirections[i+1];//-dir.mDirections[i]; - lastSwitch=dir.mDirections[i+1]; - i+=2; - } - - if ((i+1)==dir.mDirections.length) { - sum+=dir.mDirections[i];//-lastSwitch); - } else if (i==dir.mDirections.length) { - sum-=length-lastSwitch; - } - - if (sum>=0) { - return DIR_LEFT_TO_RIGHT; - } else { - return DIR_RIGHT_TO_LEFT; - } + boolean hasRTL() { + return mDirections.length>1 && mDirections[1]>0; } } diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index f8301d2..13768e4 100755 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -233,10 +233,20 @@ extends Layout } } + if (!easy) { + // XXX put override flags, etc. into chdirs + dir = bidi(dir >= 0 ? DIR_REQUEST_DEFAULT_LTR : DIR_REQUEST_DEFAULT_RTL, + chs, chdirs, n, false); + } + // Ensure that none of the underlying characters are treated // as viable breakpoints, and that the entire run gets the // same bidi direction. + final byte SOR = dir == DIR_LEFT_TO_RIGHT ? + Character.DIRECTIONALITY_LEFT_TO_RIGHT : + Character.DIRECTIONALITY_RIGHT_TO_LEFT; + if (source instanceof Spanned) { Spanned sp = (Spanned) source; ReplacementSpan[] spans = sp.getSpans(start, end, ReplacementSpan.class); @@ -246,14 +256,13 @@ extends Layout int b = sp.getSpanEnd(spans[y]); for (int x = a; x < b; x++) { + chdirs[x - start] = SOR; chs[x - start] = '\uFFFC'; } } } if (!easy) { - // XXX put override flags, etc. into chdirs - dir = bidi(dir, chs, chdirs, n, false); // Do mirroring for right-to-left segments @@ -638,26 +647,16 @@ extends Layout * 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; + // Heuristic - LTR unless paragraph contains any RTL chars + dir = DIR_LEFT_TO_RIGHT; 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) { + if (chInfo[j] == 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 */ @@ -666,7 +665,11 @@ extends Layout * Weak type resolution */ - // dump(chdirs, n, "initial"); + final byte SOR = dir == DIR_LEFT_TO_RIGHT ? + Character.DIRECTIONALITY_LEFT_TO_RIGHT : + Character.DIRECTIONALITY_RIGHT_TO_LEFT; + + // dump(chInfo, n, "initial"); // W1 non spacing marks for (int j = 0; j < n; j++) { @@ -678,7 +681,7 @@ extends Layout } } - // dump(chdirs, n, "W1"); + // dump(chInfo, n, "W1"); // W2 european numbers byte cur = SOR; @@ -693,11 +696,10 @@ extends Layout if (cur == Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC) chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; - else chInfo[j] = Character.DIRECTIONALITY_LEFT_TO_RIGHT; } } - // dump(chdirs, n, "W2"); + // dump(chInfo, n, "W2"); // W3 arabic letters for (int j = 0; j < n; j++) { @@ -705,7 +707,7 @@ extends Layout chInfo[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT; } - // dump(chdirs, n, "W3"); + // dump(chInfo, n, "W3"); // W4 single separator between numbers for (int j = 1; j < n - 1; j++) { @@ -713,6 +715,9 @@ extends Layout byte prev = chInfo[j - 1]; byte next = chInfo[j + 1]; + boolean isSpace = Character.isWhitespace(chs[j]); + boolean nextIsSpace = Character.isWhitespace(chs[j + 1]); + if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER_SEPARATOR) { if (prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER && next == Character.DIRECTIONALITY_EUROPEAN_NUMBER) @@ -724,10 +729,31 @@ extends Layout if (prev == Character.DIRECTIONALITY_ARABIC_NUMBER && next == Character.DIRECTIONALITY_ARABIC_NUMBER) chInfo[j] = Character.DIRECTIONALITY_ARABIC_NUMBER; + // add condition for spaces following the separator + if (nextIsSpace && + ( prev == Character.DIRECTIONALITY_EUROPEAN_NUMBER + || prev == Character.DIRECTIONALITY_ARABIC_NUMBER ) ) + chInfo[j] = SOR; + } + // add condition if the separator is a space + else if (isSpace && prev != SOR && + ( next == Character.DIRECTIONALITY_EUROPEAN_NUMBER + || next == Character.DIRECTIONALITY_ARABIC_NUMBER ) ) { + chInfo[j] = SOR; + for (int k=j+1; k < n; ++k) { + if (chInfo[k] == Character.DIRECTIONALITY_LEFT_TO_RIGHT) { + chInfo[j] = Character.DIRECTIONALITY_LEFT_TO_RIGHT; + break; + } + if (chInfo[k] == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { + chInfo[j] = Character.DIRECTIONALITY_RIGHT_TO_LEFT; + break; + } + } } } - // dump(chdirs, n, "W4"); + // dump(chInfo, n, "W4"); // W5 european number terminators boolean adjacent = false; @@ -742,7 +768,7 @@ extends Layout adjacent = false; } - //dump(chdirs, n, "W5"); + //dump(chInfo, n, "W5"); // W5 european number terminators part 2, // W6 separators and terminators @@ -769,7 +795,7 @@ extends Layout } } - // dump(chdirs, n, "W6"); + // dump(chInfo, n, "W6"); // W7 strong direction of european numbers cur = SOR; @@ -785,7 +811,7 @@ extends Layout chInfo[j] = Character.DIRECTIONALITY_LEFT_TO_RIGHT; } - // dump(chdirs, n, "W7"); + // dump(chInfo, n, "W7"); // N1, N2 neutrals cur = SOR; @@ -795,9 +821,8 @@ extends Layout if (d == Character.DIRECTIONALITY_LEFT_TO_RIGHT || d == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { cur = d; - } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER) { - cur = Character.DIRECTIONALITY_RIGHT_TO_LEFT; - } else if (d == Character.DIRECTIONALITY_ARABIC_NUMBER) { + } else if (d == Character.DIRECTIONALITY_EUROPEAN_NUMBER || + d == Character.DIRECTIONALITY_ARABIC_NUMBER) { cur = Character.DIRECTIONALITY_LEFT_TO_RIGHT; } else { byte dd = SOR; @@ -810,12 +835,10 @@ extends Layout dd == Character.DIRECTIONALITY_RIGHT_TO_LEFT) { break; } - if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER) { + if (dd == Character.DIRECTIONALITY_EUROPEAN_NUMBER || + dd == Character.DIRECTIONALITY_ARABIC_NUMBER) { dd = Character.DIRECTIONALITY_LEFT_TO_RIGHT; break; - } else if (dd == Character.DIRECTIONALITY_ARABIC_NUMBER) { - dd = Character.DIRECTIONALITY_RIGHT_TO_LEFT; - break; } } @@ -830,7 +853,7 @@ extends Layout } } - // dump(chdirs, n, "final"); + // dump(chInfo, n, "final"); // extra: enforce that all tabs and surrogate characters go the // primary direction @@ -844,6 +867,26 @@ extends Layout } } + // Deal specifically with special operators (like '+',etc.) ahead of numbers/english inside RTL paragraphs + for (int j = 0; j < n; j++) { + switch(chs[j]) { + case '+': + // For the following chars it is logical to apply the fix, but it appears + // it customary only for the "+" and we need to behave similarly to other devices: + //case '*': + //case '/': + //case '@': + //case '#': + //case '$': + //case '%': + //case '^': + //case '&': + //case '_': + //case '\\': + chInfo[j] = Character.DIRECTIONALITY_LEFT_TO_RIGHT; + } + } + return dir; } @@ -1268,7 +1311,8 @@ extends Layout } public int getParagraphDirection(int line) { - return Directions.baseDirection(mLineDirections[line],getLineEnd(line)-getLineStart(line)); + // LTR unless paragraph contains RTL chars (anywhere) + return mLineDirections[line].hasRTL() ? DIR_RIGHT_TO_LEFT : DIR_LEFT_TO_RIGHT; } public boolean getLineContainsTab(int line) { diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 266dd58..1fc9975 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -1267,104 +1267,78 @@ public class Canvas { } return hasBidi; } + /** @hide */ + private static boolean isPunctuation(char c) { + return c<='\u002f' || c=='\u0040' || (c>'\u005a' && c<='\u0060') || (c>'\u007a' && c<='\u00BF'); + } + /** @hide */ + private static boolean isRTL(char c) { + return c>=FIRST_RIGHT_TO_LEFT && c<=LAST_RIGHT_TO_LEFT; + } /** * A lightweight BiDi processing to make all draw text work with RTL languages. - * written from scratch by David Kohen (kohen dot d at gmail dot com) - 2010 * @hide **/ - public static char[] bidiProcess(char[] text,int start,int srcCount) { - - boolean hasBidi=false; - char[] destCharArray=new char[srcCount]; - - char[] buf = TemporaryBuffer.obtain(srcCount); - System.arraycopy(text,start, buf, 0, srcCount); - - // I'm doing the processing from the end of the string, since it worked well this way. - int count=0,srcIndex=0; - boolean rtlMode=true; - for (int i=0;i<srcCount;i++){ - srcIndex=srcCount-1-i; - if (buf[srcIndex]>=FIRST_RIGHT_TO_LEFT&&buf[srcIndex]<=LAST_RIGHT_TO_LEFT){ - destCharArray[i]=buf[srcIndex]; - // In rtl mode I'm mirroring glyphs. - rtlMode=true; + private static char reverseParen(char c) { + switch (c) { + case '[': + c=']'; + break; + case ']': + c='['; + break; + case '}': + c='{'; + break; + case '{': + c='}'; + break; + case '(': + c=')'; + break; + case ')': + c='('; + break; + case '>': + c='<'; + break; + case '<': + c='>'; + break; + } + return c; + } + /** @hide */ + public static char[] bidiProcess(char[] text,int start,int count) { + String cut=new String(text,start,count); + char[] tt=new char[count]; + cut.getChars(0, count, tt, 0); + boolean hasRTL=false; + for (int ii=0; ii<count; ++ii) + if (isRTL(tt[ii])) { + hasRTL = true; + break; } - else { - srcIndex=srcCount-1-i; - if (count==0) { - // Direction neutral characters - if (buf[srcIndex]<='\u002f' || - (buf[srcIndex]>'\u0039' && buf[srcIndex]<='\u0040') || - (buf[srcIndex]>'\u005a' && buf[srcIndex]<='\u0060')|| - (buf[srcIndex]>'\u007a' && buf[srcIndex]<='\u00BF')) { - - if (rtlMode){ - switch (buf[srcIndex]) { - case '[': - destCharArray[i]=']'; - break; - case ']': - destCharArray[i]='['; - break; - case '}': - destCharArray[i]='{'; - break; - case '{': - destCharArray[i]='}'; - break; - case '(': - destCharArray[i]=')'; - break; - case ')': - destCharArray[i]='('; - break; - case '>': - destCharArray[i]='<'; - break; - case '<': - destCharArray[i]='>'; - break; - default: - destCharArray[i]=buf[srcIndex]; - break; - } - } else destCharArray[i]=buf[srcIndex]; - } else { - // Handling LTR embedded strings. - while (((srcIndex-count)>=0)&&((buf[srcIndex-count]<FIRST_RIGHT_TO_LEFT)||(buf[srcIndex-count]>LAST_RIGHT_TO_LEFT))){ - count++; - } - int index=0; - int punctuationMarks=0; - - // Handling direction neutral characters in the middle of LTR - while (count>0 && (srcIndex-(count)>=0) && - (buf[srcIndex-(count-1)]<='\u002f' || - (buf[srcIndex-(count-1)]>'\u0039' && buf[srcIndex-(count-1)]<='\u0040') || - (buf[srcIndex-(count-1)]>'\u005a' && buf[srcIndex-(count-1)]<='\u0060')|| - (buf[srcIndex-(count-1)]>'\u007a' && buf[srcIndex-(count-1)]<='\u00BF'))){ - destCharArray[i+(count-1)]=buf[srcIndex-(count-1)]; - count--; - punctuationMarks++; - } - - while (count>0){ - destCharArray[i+index]=buf[srcIndex-(count-1)]; - count--; - index++; - } - count=index+punctuationMarks-1; - } - } - else { - // Avoiding spaghetti code and mangling of loop counter - count--; + if (hasRTL) { + char[] rev=new char[count]; + for(int ii=0; ii<count; ++ii) + rev[ii] = tt[count-ii-1]; + // now copy reverse back over tt, but reverse again (fixing) any non-RTL sequences: + for(int ii=0; ii<count; ) { + if (isRTL(rev[ii]) || isPunctuation(rev[ii])) { + tt[ii] = reverseParen(rev[ii]); + ++ii; + } else { + int end=ii+1; + while (end<count && !isRTL(rev[end]) && !isPunctuation(rev[end])) + ++end; + int jj=end; + while (ii<end) + tt[ii++] = rev[--jj]; } - rtlMode=false; } } - return destCharArray; + return tt; } /** @hide **/ @@ -1525,10 +1499,10 @@ public class Canvas { else if (text instanceof GraphicsOperations) { ((GraphicsOperations) text).drawText(this, start, end, x, y, paint); - } - else { - char[] buf = TemporaryBuffer.obtain(end - start); - TextUtils.getChars(text, start, end, buf, 0); + } + else { + char[] buf = TemporaryBuffer.obtain(end - start); + TextUtils.getChars(text, start, end, buf, 0); if (hasBidi) { String reshapedText=ArabicReshape.reshape(new String(buf)); /* The reshaping may make the string smaller */ @@ -1536,8 +1510,8 @@ public class Canvas { } else { drawText(buf, 0, end - start, x, y, paint,false); } - TemporaryBuffer.recycle(buf); - } + TemporaryBuffer.recycle(buf); + } } /** |