summaryrefslogtreecommitdiffstats
path: root/graphics
diff options
context:
space:
mode:
authorDavid Kohen <kohen.d@gmail.com>2011-01-21 12:03:27 +0200
committerSteve Kondik <shade@chemlab.org>2011-01-22 20:51:31 -0500
commit139d6e9104d882ce6f69ec95c37be681dd91a2c4 (patch)
treee68e7b6b1aa64fa37830848c4ed5468589f9d26c /graphics
parent55ff9d2a7f3e5ebe0b1fb7647e75b8c5bf17f654 (diff)
downloadframeworks_base-139d6e9104d882ce6f69ec95c37be681dd91a2c4.zip
frameworks_base-139d6e9104d882ce6f69ec95c37be681dd91a2c4.tar.gz
frameworks_base-139d6e9104d882ce6f69ec95c37be681dd91a2c4.tar.bz2
RTL and Arabic reshaping support cherry-picked from FroYo
Change-Id: I43ee8efec12fb1edbf956b52326262c80d579885
Diffstat (limited to 'graphics')
-rw-r--r--graphics/java/android/graphics/Canvas.java437
-rw-r--r--graphics/java/android/graphics/Paint.java26
-rw-r--r--graphics/java/android/graphics/utils/ArabicReshape.java403
3 files changed, 781 insertions, 85 deletions
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index a587d0d..d8d7dd3 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.graphics.utils.ArabicReshape;
import android.text.TextUtils;
import android.text.SpannedString;
import android.text.SpannableString;
@@ -34,7 +35,9 @@ import javax.microedition.khronos.opengles.GL;
public class Canvas {
// assigned in constructors, freed in finalizer
final int mNativeCanvas;
-
+ private static final char FIRST_RIGHT_TO_LEFT = '\u0590';
+ private static final char LAST_RIGHT_TO_LEFT = '\u07b1';
+
/* Our native canvas can be either a raster, gl, or picture canvas.
If we are raster, then mGL will be null, and mBitmap may or may not be
present (our default constructor creates a raster canvas but no
@@ -44,7 +47,7 @@ public class Canvas {
*/
private Bitmap mBitmap; // if not null, mGL must be null
private GL mGL; // if not null, mBitmap must be null
-
+
// optional field set by the caller
private DrawFilter mDrawFilter;
@@ -53,7 +56,7 @@ public class Canvas {
// Used to determine when compatibility scaling is in effect.
private int mScreenDensity = Bitmap.DENSITY_NONE;
-
+
// Used by native code
@SuppressWarnings({"UnusedDeclaration"})
private int mSurfaceFormat;
@@ -72,7 +75,7 @@ public class Canvas {
/**
* Construct a canvas with the specified bitmap to draw into. The bitmap
* must be mutable.
- *
+ *
* <p>The initial target density of the canvas is the same as the given
* bitmap's density.
*
@@ -88,7 +91,7 @@ public class Canvas {
mBitmap = bitmap;
mDensity = bitmap.mDensity;
}
-
+
/*package*/ Canvas(int nativeCanvas) {
if (nativeCanvas == 0) {
throw new IllegalStateException();
@@ -96,14 +99,14 @@ public class Canvas {
mNativeCanvas = nativeCanvas;
mDensity = Bitmap.getDefaultDensity();
}
-
+
/**
* Construct a canvas with the specified gl context. All drawing through
* this canvas will be redirected to OpenGL. Note: some features may not
* be supported in this mode (e.g. some GL implementations may not support
* antialiasing or certain effects like ColorMatrix or certain Xfermodes).
* However, no exception will be thrown in those cases.
- *
+ *
* <p>The initial target density of the canvas is the same as the initial
* density of bitmaps as per {@link Bitmap#getDensity() Bitmap.getDensity()}.
*/
@@ -112,7 +115,7 @@ public class Canvas {
mGL = gl;
mDensity = Bitmap.getDefaultDensity();
}
-
+
/**
* Return the GL object associated with this canvas, or null if it is not
* backed by GL.
@@ -120,7 +123,7 @@ public class Canvas {
public GL getGL() {
return mGL;
}
-
+
/**
* Call this to free up OpenGL resources that may be cached or allocated
* on behalf of the Canvas. Any subsequent drawing with a GL-backed Canvas
@@ -129,13 +132,13 @@ public class Canvas {
public static void freeGlCaches() {
freeCaches();
}
-
+
/**
* Specify a bitmap for the canvas to draw into. As a side-effect, also
* updates the canvas's target density to match that of the bitmap.
*
* @param bitmap Specifies a mutable bitmap for the canvas to draw into.
- *
+ *
* @see #setDensity(int)
* @see #getDensity()
*/
@@ -152,7 +155,7 @@ public class Canvas {
mBitmap = bitmap;
mDensity = bitmap.mDensity;
}
-
+
/**
* Set the viewport dimensions if this canvas is GL based. If it is not,
* this method is ignored and no exception is thrown.
@@ -197,7 +200,7 @@ public class Canvas {
* to determine the scaling factor when drawing a bitmap into it.
*
* @see #setDensity(int)
- * @see Bitmap#getDensity()
+ * @see Bitmap#getDensity()
*/
public int getDensity() {
return mDensity;
@@ -213,7 +216,7 @@ public class Canvas {
* {@link Bitmap#DENSITY_NONE} to disable bitmap scaling.
*
* @see #getDensity()
- * @see Bitmap#setDensity(int)
+ * @see Bitmap#setDensity(int)
*/
public void setDensity(int density) {
if (mBitmap != null) {
@@ -226,7 +229,7 @@ public class Canvas {
public void setScreenDensity(int density) {
mScreenDensity = density;
}
-
+
// the SAVE_FLAG constants must match their native equivalents
/** restore the current matrix when restore() is called */
@@ -240,8 +243,8 @@ public class Canvas {
/** clip against the layer's bounds */
public static final int CLIP_TO_LAYER_SAVE_FLAG = 0x10;
/** restore everything when restore() is called */
- public static final int ALL_SAVE_FLAG = 0x1F;
-
+ public static final int ALL_SAVE_FLAG = 0x1F;
+
/**
* Saves the current matrix and clip onto a private stack. Subsequent
* calls to translate,scale,rotate,skew,concat or clipRect,clipPath
@@ -252,7 +255,7 @@ public class Canvas {
* @return The value to pass to restoreToCount() to balance this save()
*/
public native int save();
-
+
/**
* Based on saveFlags, can save the current matrix and clip onto a private
* stack. Subsequent calls to translate,scale,rotate,skew,concat or
@@ -287,7 +290,7 @@ public class Canvas {
paint != null ? paint.mNativePaint : 0,
saveFlags);
}
-
+
/**
* Helper version of saveLayer() that takes 4 values rather than a RectF.
*/
@@ -318,7 +321,7 @@ public class Canvas {
alpha = Math.min(255, Math.max(0, alpha));
return native_saveLayerAlpha(mNativeCanvas, bounds, alpha, saveFlags);
}
-
+
/**
* Helper for saveLayerAlpha() that takes 4 values instead of a RectF.
*/
@@ -422,7 +425,7 @@ public class Canvas {
public void concat(Matrix matrix) {
native_concat(mNativeCanvas, matrix.native_instance);
}
-
+
/**
* Completely replace the current matrix with the specified matrix. If the
* matrix parameter is null, then the current matrix is reset to identity.
@@ -434,7 +437,7 @@ public class Canvas {
native_setMatrix(mNativeCanvas,
matrix == null ? 0 : matrix.native_instance);
}
-
+
/**
* Return, in ctm, the current transformation matrix. This does not alter
* the matrix in the canvas, but just returns a copy of it.
@@ -442,7 +445,7 @@ public class Canvas {
public void getMatrix(Matrix ctm) {
native_getCTM(mNativeCanvas, ctm.native_instance);
}
-
+
/**
* Return a new matrix with a copy of the canvas' current transformation
* matrix.
@@ -452,7 +455,7 @@ public class Canvas {
getMatrix(m);
return m;
}
-
+
/**
* Modify the current clip with the specified rectangle.
*
@@ -488,7 +491,7 @@ public class Canvas {
* @return true if the resulting clip is non-empty
*/
public native boolean clipRect(RectF rect);
-
+
/**
* Intersect the current clip with the specified rectangle, which is
* expressed in local coordinates.
@@ -497,7 +500,7 @@ public class Canvas {
* @return true if the resulting clip is non-empty
*/
public native boolean clipRect(Rect rect);
-
+
/**
* Modify the current clip with the specified rectangle, which is
* expressed in local coordinates.
@@ -534,7 +537,7 @@ public class Canvas {
*/
public native boolean clipRect(float left, float top,
float right, float bottom);
-
+
/**
* Intersect the current clip with the specified rectangle, which is
* expressed in local coordinates.
@@ -550,7 +553,7 @@ public class Canvas {
*/
public native boolean clipRect(int left, int top,
int right, int bottom);
-
+
/**
* Modify the current clip with the specified path.
*
@@ -561,7 +564,7 @@ public class Canvas {
public boolean clipPath(Path path, Region.Op op) {
return native_clipPath(mNativeCanvas, path.ni(), op.nativeInt);
}
-
+
/**
* Intersect the current clip with the specified path.
*
@@ -571,7 +574,7 @@ public class Canvas {
public boolean clipPath(Path path) {
return clipPath(path, Region.Op.INTERSECT);
}
-
+
/**
* Modify the current clip with the specified region. Note that unlike
* clipRect() and clipPath() which transform their arguments by the
@@ -600,11 +603,11 @@ public class Canvas {
public boolean clipRegion(Region region) {
return clipRegion(region, Region.Op.INTERSECT);
}
-
+
public DrawFilter getDrawFilter() {
return mDrawFilter;
}
-
+
public void setDrawFilter(DrawFilter filter) {
int nativeFilter = 0;
if (filter != null) {
@@ -617,7 +620,7 @@ public class Canvas {
public enum EdgeType {
BW(0), //!< treat edges by just rounding to nearest pixel boundary
AA(1); //!< treat edges by rounding-out, since they may be antialiased
-
+
EdgeType(int nativeInt) {
this.nativeInt = nativeInt;
}
@@ -695,7 +698,7 @@ public class Canvas {
public boolean getClipBounds(Rect bounds) {
return native_getClipBounds(mNativeCanvas, bounds);
}
-
+
/**
* Retrieve the clip bounds.
*
@@ -706,7 +709,7 @@ public class Canvas {
getClipBounds(r);
return r;
}
-
+
/**
* Fill the entire canvas' bitmap (restricted to the current clip) with the
* specified RGB color, using srcover porterduff mode.
@@ -763,7 +766,7 @@ public class Canvas {
public void drawPaint(Paint paint) {
native_drawPaint(mNativeCanvas, paint.mNativePaint);
}
-
+
/**
* Draw a series of points. Each point is centered at the coordinate
* specified by pts[], and its diameter is specified by the paint's stroke
@@ -853,7 +856,7 @@ public class Canvas {
public void drawRect(Rect r, Paint paint) {
drawRect(r.left, r.top, r.right, r.bottom, paint);
}
-
+
/**
* Draw the specified Rect using the specified paint. The rectangle will
@@ -949,7 +952,7 @@ public class Canvas {
public void drawPath(Path path, Paint paint) {
native_drawPath(mNativeCanvas, path.ni(), paint.mNativePaint);
}
-
+
private static void throwIfRecycled(Bitmap bitmap) {
if (bitmap.isRecycled()) {
throw new RuntimeException(
@@ -960,7 +963,7 @@ public class Canvas {
/**
* Draw the specified bitmap, with its top/left corner at (x,y), using
* the specified paint, transformed by the current matrix.
- *
+ *
* <p>Note: if the paint contains a maskfilter that generates a mask which
* extends beyond the bitmap's original width/height (e.g. BlurMaskFilter),
* then the bitmap will be drawn as if it were in a Shader with CLAMP mode.
@@ -970,7 +973,7 @@ public class Canvas {
* <p>If the bitmap and canvas have different densities, this function
* will take care of automatically scaling the bitmap to draw at the
* same density as the canvas.
- *
+ *
* @param bitmap The bitmap to be drawn
* @param left The position of the left side of the bitmap being drawn
* @param top The position of the top side of the bitmap being drawn
@@ -987,7 +990,7 @@ public class Canvas {
* Draw the specified bitmap, scaling/translating automatically to fill
* the destination rectangle. If the source rectangle is not null, it
* specifies the subset of the bitmap to draw.
- *
+ *
* <p>Note: if the paint contains a maskfilter that generates a mask which
* extends beyond the bitmap's original width/height (e.g. BlurMaskFilter),
* then the bitmap will be drawn as if it were in a Shader with CLAMP mode.
@@ -998,7 +1001,7 @@ public class Canvas {
* This is because the source and destination rectangle coordinate
* spaces are in their respective densities, so must already have the
* appropriate scaling factor applied.
- *
+ *
* @param bitmap The bitmap to be drawn
* @param src May be null. The subset of the bitmap to be drawn
* @param dst The rectangle that the bitmap will be scaled/translated
@@ -1019,7 +1022,7 @@ public class Canvas {
* Draw the specified bitmap, scaling/translating automatically to fill
* the destination rectangle. If the source rectangle is not null, it
* specifies the subset of the bitmap to draw.
- *
+ *
* <p>Note: if the paint contains a maskfilter that generates a mask which
* extends beyond the bitmap's original width/height (e.g. BlurMaskFilter),
* then the bitmap will be drawn as if it were in a Shader with CLAMP mode.
@@ -1030,7 +1033,7 @@ public class Canvas {
* This is because the source and destination rectangle coordinate
* spaces are in their respective densities, so must already have the
* appropriate scaling factor applied.
- *
+ *
* @param bitmap The bitmap to be drawn
* @param src May be null. The subset of the bitmap to be drawn
* @param dst The rectangle that the bitmap will be scaled/translated
@@ -1046,7 +1049,7 @@ public class Canvas {
paint != null ? paint.mNativePaint : 0,
mScreenDensity, bitmap.mDensity);
}
-
+
/**
* Treat the specified array of colors as a bitmap, and draw it. This gives
* the same result as first creating a bitmap from the array, and then
@@ -1093,7 +1096,7 @@ public class Canvas {
native_drawBitmap(mNativeCanvas, colors, offset, stride, x, y, width, height, hasAlpha,
paint != null ? paint.mNativePaint : 0);
}
-
+
/** Legacy version of drawBitmap(int[] colors, ...) that took ints for x,y
*/
public void drawBitmap(int[] colors, int offset, int stride, int x, int y,
@@ -1103,7 +1106,7 @@ public class Canvas {
drawBitmap(colors, offset, stride, (float)x, (float)y, width, height,
hasAlpha, paint);
}
-
+
/**
* Draw the bitmap using the specified matrix.
*
@@ -1115,13 +1118,13 @@ public class Canvas {
nativeDrawBitmapMatrix(mNativeCanvas, bitmap.ni(), matrix.ni(),
paint != null ? paint.mNativePaint : 0);
}
-
+
private static void checkRange(int length, int offset, int count) {
if ((offset | count) < 0 || offset + count > length) {
throw new ArrayIndexOutOfBoundsException();
}
}
-
+
/**
* Draw the bitmap through the mesh, where mesh vertices are evenly
* distributed across the bitmap. There are meshWidth+1 vertices across, and
@@ -1168,18 +1171,18 @@ public class Canvas {
verts, vertOffset, colors, colorOffset,
paint != null ? paint.mNativePaint : 0);
}
-
+
public enum VertexMode {
TRIANGLES(0),
TRIANGLE_STRIP(1),
TRIANGLE_FAN(2);
-
+
VertexMode(int nativeInt) {
this.nativeInt = nativeInt;
}
final int nativeInt;
}
-
+
/**
* Draw the array of vertices, interpreted as triangles (based on mode). The
* verts array is required, and specifies the x,y pairs for each vertex. If
@@ -1208,7 +1211,7 @@ public class Canvas {
* @param indices If not null, array of indices to reference into the
* vertex (texs, colors) array.
* @param indexCount number of entries in the indices array (if not null).
- * @param paint Specifies the shader to use if the texs array is non-null.
+ * @param paint Specifies the shader to use if the texs array is non-null.
*/
public void drawVertices(VertexMode mode, int vertexCount,
float[] verts, int vertOffset,
@@ -1230,7 +1233,169 @@ public class Canvas {
vertOffset, texs, texOffset, colors, colorOffset,
indices, indexOffset, indexCount, paint.mNativePaint);
}
-
+
+ /**
+ * Since the reshaping algorithm does not test for arabic prior to starting, this is made to
+ * @hide
+ **/
+ public static boolean bidiTest(char[] text,int start,int srcCount) {
+
+ boolean hasBidi=false;
+
+ // Check if there are BiDi characters in the string, of so, we need to work.
+ for (int i=start;i<(srcCount+start);i++){
+ if (text[i]>=FIRST_RIGHT_TO_LEFT&&text[i]<=LAST_RIGHT_TO_LEFT){
+ hasBidi=true;
+ break;
+ }
+ }
+ return hasBidi;
+ }
+ /**
+ * Since the reshaping algorithm does not test for arabic prior to starting, this is made to
+ * @hide
+ **/
+ public static boolean bidiTest(String text,int start,int srcCount) {
+
+ boolean hasBidi=false;
+
+ // Check if there are BiDi characters in the string, of so, we need to work.
+ for (int i=start;i<(srcCount+start);i++){
+ if (text.charAt(i)>=FIRST_RIGHT_TO_LEFT&&text.charAt(i)<=LAST_RIGHT_TO_LEFT){
+ hasBidi=true;
+ break;
+ }
+ }
+ return hasBidi;
+ }
+ /**
+ * 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;
+ }
+ 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--;
+ }
+ rtlMode=false;
+ }
+ }
+ return destCharArray;
+ }
+
+ /** @hide **/
+ public void drawText(char[] text, int index, int count, float x, float y,
+ Paint paint,boolean bidi) {
+ if (((index | count | (index + count)) < 0) ||
+ (index + count) > text.length) {
+ throw new IndexOutOfBoundsException();
+ }
+ boolean hasBidi=bidiTest(text,index,count);
+ if (hasBidi) {
+ if (bidi) {
+ char[] bidiText=bidiProcess(text,index,count);
+ String reshapedText=ArabicReshape.reshape(new String(bidiText));
+ /* The reshaping may make the string smaller */
+ native_drawText(mNativeCanvas, reshapedText.toCharArray(), 0, count - ((count - reshapedText.length())>0 ? (count - reshapedText.length()) : 0), x, y,
+ paint.mNativePaint);
+ } else {
+ String reshapedText=ArabicReshape.reshape(new String(text));
+ /* The reshaping may make the string smaller */
+ native_drawText(mNativeCanvas, reshapedText.toCharArray(), index,
+ count - ((text.length - reshapedText.length())>0 ? (text.length - reshapedText.length()) : 0), x, y,
+ paint.mNativePaint);
+ }
+ } else {
+ native_drawText(mNativeCanvas, text, index, count, x, y,
+ paint.mNativePaint);
+ }
+ }
+
/**
* Draw the text, with origin at (x,y), using the specified paint. The
* origin is interpreted based on the Align setting in the paint.
@@ -1246,8 +1411,14 @@ public class Canvas {
(text.length - index - count)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_drawText(mNativeCanvas, text, index, count, x, y,
- paint.mNativePaint);
+
+ boolean hasBidi=bidiTest(text,index,count);
+ if (hasBidi) {
+ drawText(text,index,count,x,y,paint,true);
+ } else {
+ native_drawText(mNativeCanvas, text, index, count, x, y,
+ paint.mNativePaint);
+ }
}
/**
@@ -1259,7 +1430,28 @@ public class Canvas {
* @param y The y-coordinate of the origin of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
- public native void drawText(String text, float x, float y, Paint paint);
+ private native void native_drawText(String text, float x, float y, Paint paint);
+
+ /** @hide */
+ public void drawText(String text, float x, float y, Paint paint,boolean bidi){
+ boolean hasBidi=bidiTest(text,0,text.length());
+ if (hasBidi) {
+ if (!bidi) {
+ native_drawText(ArabicReshape.reshape(text),x,y,paint);
+ } else {
+ if (text.length() > 0) {
+ String bidiText;
+ bidiText=new String(bidiProcess(text.toCharArray(),0,text.length()));
+ native_drawText(ArabicReshape.reshape(bidiText),x,y,paint);
+ }
+ }
+ } else {
+ native_drawText(text,x,y,paint);
+ }
+ }
+ public void drawText(String text, float x, float y, Paint paint){
+ drawText(text,x,y,paint,true);
+ }
/**
* Draw the text, with origin at (x,y), using the specified paint.
@@ -1277,8 +1469,16 @@ public class Canvas {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_drawText(mNativeCanvas, text, start, end, x, y,
- paint.mNativePaint);
+ boolean hasBidi=bidiTest(text,start,end-start);
+ if (hasBidi) {
+ String reshapedText=ArabicReshape.reshape(new String(bidiProcess(text.toCharArray(),start,end-start)));
+ /* The reshaping may make the string smaller */
+ native_drawText(mNativeCanvas, reshapedText, 0, end-start - ((end-start - reshapedText.length())>0?(end-start - reshapedText.length()):0), x, y,
+ paint.mNativePaint);
+ } else {
+ native_drawText(mNativeCanvas, text, start, end, x, y,
+ paint.mNativePaint);
+ }
}
/**
@@ -1296,21 +1496,49 @@ public class Canvas {
*/
public void drawText(CharSequence text, int start, int end, float x,
float y, Paint paint) {
+ drawText(text,start,end,x,y,paint,true);
+ }
+
+ /** @hide */
+ public void drawText(CharSequence text, int start, int end, float x,
+ float y, Paint paint,boolean bidi) {
+ boolean hasBidi=bidiTest(text.toString(),start,end-start);
if (text instanceof String || text instanceof SpannedString ||
- text instanceof SpannableString) {
- native_drawText(mNativeCanvas, text.toString(), start, end, x, y,
+ text instanceof SpannableString) {
+ if (hasBidi) {
+ if (bidi) {
+ String bidiText=new String(bidiProcess(text.toString().toCharArray(),start,end-start));
+ String reshapedText=ArabicReshape.reshape(bidiText);
+ /* The reshaping may make the string smaller */
+ native_drawText(mNativeCanvas, reshapedText, 0, (end-start) - ((end-start - reshapedText.length())>0 ? (end-start - reshapedText.length()) : 0), x, y,
+ paint.mNativePaint);
+ } else {
+ String reshapedText=ArabicReshape.reshape(text.toString());
+ /* The reshaping may make the string smaller */
+ native_drawText(mNativeCanvas, reshapedText, 0, (end-start) - ((end-start - reshapedText.length())>0 ? (end-start - reshapedText.length()) : 0), x, y,
+ paint.mNativePaint);
+ }
+ } else {
+ native_drawText(mNativeCanvas, text.toString() , start, end, x, y,
paint.mNativePaint);
+ }
}
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);
- drawText(buf, 0, end - start, x, y, paint);
- TemporaryBuffer.recycle(buf);
- }
+ paint);
+ }
+ 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 */
+ drawText(reshapedText.toCharArray(), 0, (end - start) - (((end - start) - reshapedText.length())>0?((end - start) - reshapedText.length()):0), x, y, paint,false);
+ } else {
+ drawText(buf, 0, end - start, x, y, paint,false);
+ }
+ TemporaryBuffer.recycle(buf);
+ }
}
/**
@@ -1326,11 +1554,24 @@ public class Canvas {
*/
public void drawPosText(char[] text, int index, int count, float[] pos,
Paint paint) {
- if (index < 0 || index + count > text.length || count*2 > pos.length) {
+ if (index < 0 || index + count > text.length || (index+count)*2 > pos.length) {
throw new IndexOutOfBoundsException();
}
- native_drawPosText(mNativeCanvas, text, index, count, pos,
- paint.mNativePaint);
+
+ boolean hasBidi=bidiTest(text,index,count);
+ if (hasBidi) {
+ float[] relativePos = new float[count*2];
+ System.arraycopy(pos , index*2 , relativePos , 0, count*2);
+ char[] bidiText;
+ bidiText=bidiProcess(text,index,count);
+ String reshapedText=ArabicReshape.reshape(new String(bidiText));
+ /* The reshaping may make the string smaller */
+ native_drawPosText(mNativeCanvas, reshapedText.toCharArray(), 0, count - ((count - reshapedText.length())>0 ? (count - reshapedText.length()) : 0), relativePos,
+ paint.mNativePaint);
+ } else {
+ native_drawPosText(mNativeCanvas, text, index, count, pos,
+ paint.mNativePaint);
+ }
}
/**
@@ -1345,7 +1586,16 @@ public class Canvas {
if (text.length()*2 > pos.length) {
throw new ArrayIndexOutOfBoundsException();
}
- native_drawPosText(mNativeCanvas, text, pos, paint.mNativePaint);
+
+ boolean hasBidi=bidiTest(text,0,text.length());
+ if (hasBidi) {
+ String bidiText;
+ bidiText=new String(bidiProcess(text.toCharArray(),0,text.length()));
+ native_drawPosText(mNativeCanvas, ArabicReshape.reshape(bidiText), pos, paint.mNativePaint);
+ } else {
+ native_drawPosText(mNativeCanvas, text, pos,
+ paint.mNativePaint);
+ }
}
/**
@@ -1366,9 +1616,21 @@ public class Canvas {
if (index < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
- native_drawTextOnPath(mNativeCanvas, text, index, count,
- path.ni(), hOffset, vOffset,
- paint.mNativePaint);
+ boolean hasBidi=bidiTest(text,index,count);
+ if (hasBidi) {
+ char[] bidiText;
+ bidiText=bidiProcess(text,index,count);
+ String reshapedText=ArabicReshape.reshape(new String(bidiText));
+ /* The reshaping may make the string smaller */
+ native_drawTextOnPath(mNativeCanvas, reshapedText.toCharArray(), 0, count - ((count - reshapedText.length())>0 ? (count - reshapedText.length()) : 0),
+ path.ni(), hOffset, vOffset,
+ paint.mNativePaint);
+ } else {
+ native_drawTextOnPath(mNativeCanvas, text, index, count,
+ path.ni(), hOffset, vOffset,
+ paint.mNativePaint);
+ }
+ // TODO: Handle index>0
}
/**
@@ -1387,8 +1649,17 @@ public class Canvas {
public void drawTextOnPath(String text, Path path, float hOffset,
float vOffset, Paint paint) {
if (text.length() > 0) {
- native_drawTextOnPath(mNativeCanvas, text, path.ni(),
- hOffset, vOffset, paint.mNativePaint);
+ boolean hasBidi=bidiTest(text,0,text.length());
+ if (hasBidi) {
+ String bidiText;
+ bidiText=new String(bidiProcess(text.toCharArray(),0,text.length()));
+ native_drawTextOnPath(mNativeCanvas, ArabicReshape.reshape(bidiText), path.ni(),
+ hOffset, vOffset, paint.mNativePaint);
+ } else {
+ native_drawTextOnPath(mNativeCanvas, text,
+ path.ni(), hOffset, vOffset,
+ paint.mNativePaint);
+ }
}
}
@@ -1396,14 +1667,14 @@ public class Canvas {
* Save the canvas state, draw the picture, and restore the canvas state.
* This differs from picture.draw(canvas), which does not perform any
* save/restore.
- *
+ *
* @param picture The picture to be drawn
*/
public void drawPicture(Picture picture) {
picture.endRecording();
native_drawPicture(mNativeCanvas, picture.ni());
}
-
+
/**
* Draw the picture, stretched to fit into the dst rectangle.
*/
@@ -1417,7 +1688,7 @@ public class Canvas {
drawPicture(picture);
restore();
}
-
+
/**
* Draw the picture, stretched to fit into the dst rectangle.
*/
@@ -1431,7 +1702,7 @@ public class Canvas {
drawPicture(picture);
restore();
}
-
+
protected void finalize() throws Throwable {
super.finalize();
// If the constructor threw an exception before setting mNativeCanvas, the native finalizer
@@ -1552,7 +1823,7 @@ public class Canvas {
float[] verts, int vertOffset, float[] texs, int texOffset,
int[] colors, int colorOffset, short[] indices,
int indexOffset, int indexCount, int nPaint);
-
+
private static native void native_drawText(int nativeCanvas, char[] text,
int index, int count, float x,
float y, int paint);
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 3e3f87b..6d6f730 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.graphics.utils.ArabicReshape;
import android.text.TextUtils;
import android.text.SpannableString;
import android.text.SpannedString;
@@ -1299,7 +1300,18 @@ public class Paint {
if ((index | count) < 0 || index + count > text.length) {
throw new ArrayIndexOutOfBoundsException();
}
- native_getTextPath(mNativePaint, text, index, count, x, y, path.ni());
+ boolean hasBidi=Canvas.bidiTest(text,index,count);
+ if (hasBidi) {
+ char[] bidiText;
+ bidiText=Canvas.bidiProcess(text,index,count);
+ String reshapedText=ArabicReshape.reshape(new String(bidiText));
+ /* The reshaping may make the string smaller */
+ native_getTextPath(mNativePaint, reshapedText.toCharArray(), 0,
+ count - ((count-reshapedText.length())>0 ? (count-reshapedText.length()) : 0),
+ x, y, path.ni());
+ } else {
+ native_getTextPath(mNativePaint, text, index, count, x, y, path.ni());
+ }
}
/**
@@ -1320,7 +1332,17 @@ public class Paint {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_getTextPath(mNativePaint, text, start, end, x, y, path.ni());
+ boolean hasBidi=Canvas.bidiTest(text,start,start+end);
+ if (hasBidi) {
+ char[] bidiText;
+ bidiText=Canvas.bidiProcess(text.toCharArray(),start,start+end);
+ String reshapedText=ArabicReshape.reshape(new String(bidiText));
+ /* The reshaping may make the string smaller */
+ native_getTextPath(mNativePaint, reshapedText, 0, end-start - ((end-start - reshapedText.length())>0 ? (end-start - reshapedText.length()) : 0),
+ x, y, path.ni());
+ } else {
+ native_getTextPath(mNativePaint, text, start, end, x, y, path.ni());
+ }
}
/**
diff --git a/graphics/java/android/graphics/utils/ArabicReshape.java b/graphics/java/android/graphics/utils/ArabicReshape.java
new file mode 100644
index 0000000..785b4c6
--- /dev/null
+++ b/graphics/java/android/graphics/utils/ArabicReshape.java
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) 2010 Abdulaziz Alhussien
+ *
+ * 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.
+ */
+
+/* @hide */
+package android.graphics.utils;
+
+/**
+ * This code is for Arabic Reshaping.
+ * Writtien by Abdulaziz Alhussien.
+ * azizanroid@gmail.com
+ *
+ * This code is used in Mirsal, Ibrahim Keyboard, Arabic Contact, Arabic notepad applications
+ *
+ * @hide
+ */
+public class ArabicReshape {
+
+ static final char RIGHT_LEFT_CHAR = 0x0001;
+ static final char RIGHT_NOLEFT_CHAR_ALEF = 0x0006;
+ static final char RIGHT_NOLEFT_CHAR = 0x0004;
+ static final char RIGHT_LEFT_CHAR_LAM = 0x0003;
+ static final char TANWEEN = 0x000C;
+ static final char TASHKEEL = 0x000A;
+ static final char TATWEEL_CHAR = 0x0008;
+ static final char NORIGHT_NOLEFT_CHAR = 0x0007;
+ static final char NOTUSED_CHAR = 0x000F;
+ static final char NOTARABIC_CHAR = 0x0000;
+ static final char RIGHT_LEFT_CHAR_MASK = 0x0880;
+ static final char RIGHT_NOLEFT_CHAR_MASK = 0x0800;
+ static final char LEFT_CHAR_MASK = 0x0080;
+
+ private static final char allchar[][] = {
+ {0x0621, 0x0007, 0xFE80, 0xFE80, 0xFE80, 0xFE80},
+ {0x0622, 0x0806, 0xFE81, 0xFE82, 0xFEF5, 0xFEF6},
+ {0x0623, 0x0806, 0xFE83, 0xFE84, 0xFEF7, 0xFEF8},
+ {0x0624, 0x0804, 0xFE85, 0xFE86, 0xFE86, 0xFE86},
+ {0x0625, 0x0806, 0xFE87, 0xFE88, 0xFEF9, 0xFEFA},
+ {0x0626, 0x0881, 0xFE89, 0xFE8A, 0xFE8B, 0xFE8C},
+ {0x0627, 0x0806, 0xFE8D, 0xFE8E, 0xFEFB, 0xFEFC},
+ {0x0628, 0x0881, 0xFE8F, 0xFE90, 0xFE91, 0xFE92},
+ {0x0629, 0x0804, 0xFE93, 0xFE94, 0xFE94, 0xFE94},
+ {0x062A, 0x0881, 0xFE95, 0xFE96, 0xFE97, 0xFE98},
+ {0x062B, 0x0881, 0xFE99, 0xFE9A, 0xFE9B, 0xFE9C},
+ {0x062C, 0x0881, 0xFE9D, 0xFE9E, 0xFE9F, 0xFEA0},
+ {0x062D, 0x0881, 0xFEA1, 0xFEA2, 0xFEA3, 0xFEA4},
+ {0x062E, 0x0881, 0xFEA5, 0xFEA6, 0xFEA7, 0xFEA8},
+ {0x062F, 0x0804, 0xFEA9, 0xFEAA, 0xFEAA, 0xFEAA},
+ {0x0630, 0x0804, 0xFEAB, 0xFEAC, 0xFEAC, 0xFEAC},
+ {0x0631, 0x0804, 0xFEAD, 0xFEAE, 0xFEAE, 0xFEAE},
+ {0x0632, 0x0804, 0xFEAF, 0xFEB0, 0xFEB0, 0xFEB0},
+ {0x0633, 0x0881, 0xFEB1, 0xFEB2, 0xFEB3, 0xFEB4},
+ {0x0634, 0x0881, 0xFEB5, 0xFEB6, 0xFEB7, 0xFEB8},
+ {0x0635, 0x0881, 0xFEB9, 0xFEBA, 0xFEBB, 0xFEBC},
+ {0x0636, 0x0881, 0xFEBD, 0xFEBE, 0xFEBF, 0xFEC0},
+ {0x0637, 0x0881, 0xFEC1, 0xFEC2, 0xFEC3, 0xFEC4},
+ {0x0638, 0x0881, 0xFEC5, 0xFEC6, 0xFEC7, 0xFEC8},
+ {0x0639, 0x0881, 0xFEC9, 0xFECA, 0xFECB, 0xFECC},
+ {0x063A, 0x0881, 0xFECD, 0xFECE, 0xFECF, 0xFED0},
+ {0x063B, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x063C, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x063D, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x063E, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x063F, 0x000F, 0x0, 0x0, 0x0, 0x0},
+
+ {0x0640, 0x0888, 0x0640, 0x0640, 0x0640, 0x0640},
+ {0x0641, 0x0881, 0xFED1, 0xFED2, 0xFED3, 0xFED4},
+ {0x0642, 0x0881, 0xFED5, 0xFED6, 0xFED7, 0xFED8},
+ {0x0643, 0x0881, 0xFED9, 0xFEDA, 0xFEDB, 0xFEDC},
+ {0x0644, 0x0883, 0xFEDD, 0xFEDE, 0xFEDF, 0xFEE0},
+ {0x0645, 0x0881, 0xFEE1, 0xFEE2, 0xFEE3, 0xFEE4},
+ {0x0646, 0x0881, 0xFEE5, 0xFEE6, 0xFEE7, 0xFEE8},
+ {0x0647, 0x0881, 0xFEE9, 0xFEEA, 0xFEEB, 0xFEEC},
+ {0x0648, 0x0804, 0xFEED, 0xFEEE, 0xFEEE, 0xFEEE},
+ {0x0649, 0x0804, 0xFEEF, 0xFEF0, 0xFEF0, 0xFEF0},
+ {0x064A, 0x0881, 0xFEF1, 0xFEF2, 0xFEF3, 0xFEF4},
+ {0x064B, 0x000C, 0x064B, 0xFE70, 0xFE71, 0xFE70},
+ {0x064C, 0x000C, 0x064C, 0xFE72, 0xFE72, 0xFE72},
+ {0x064D, 0x000C, 0x064D, 0xFE74, 0xFE74, 0xFE74},
+ {0x064E, 0x000A, 0x064E, 0xFE76, 0xFE77, 0xFE76},
+ {0x064F, 0x000A, 0x064F, 0xFE78, 0xFE79, 0xFE78},
+ {0x0650, 0x000A, 0x0650, 0xFE7A, 0xFE7B, 0xFE7A},
+ {0x0651, 0x000A, 0x0651, 0xFE7C, 0xFE7D, 0xFE7C},
+ {0x0652, 0x000A, 0x0652, 0xFE7E, 0xFE7F, 0xFE7E},
+
+ {0x0653, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0654, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0655, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0656, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0657, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0658, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0659, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x065A, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x065B, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x065C, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x065D, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x065E, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x065F, 0x000F, 0x0, 0x0, 0x0, 0x0},
+
+ {0x0660, 0x000B, 0x0660, 0x0660, 0x0660, 0x0660},
+ {0x0661, 0x000B, 0x0661, 0x0661, 0x0661, 0x0661},
+ {0x0662, 0x000B, 0x0662, 0x0662, 0x0662, 0x0662},
+ {0x0663, 0x000B, 0x0663, 0x0663, 0x0663, 0x0663},
+ {0x0664, 0x000B, 0x0665, 0x0664, 0x0664, 0x0664},
+ {0x0665, 0x000B, 0x0665, 0x0665, 0x0665, 0x0665},
+ {0x0666, 0x000B, 0x0666, 0x0666, 0x0666, 0x0666},
+ {0x0667, 0x000B, 0x0667, 0x0667, 0x0667, 0x0667},
+ {0x0668, 0x000B, 0x0668, 0x0668, 0x0668, 0x0668},
+ {0x0669, 0x000B, 0x0669, 0x0669, 0x0669, 0x0669},
+
+ {0x066A, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x066B, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x066C, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x066D, 0x000F, 0x0, 0x0, 0x0, 0x0},
+
+ {0x066E, 0x000E, 0x065E, 0x065E, 0x065E, 0x065E},
+ {0x066F, 0x000E, 0x065F, 0x065F, 0x065F, 0x065F},
+
+ {0x0670, 0x000F, 0x0, 0x0, 0x0, 0x0},
+
+ {0x0671, 0x0804, 0xFB50, 0xFB51, 0xFB51, 0xFB51},
+ {0x0672, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0673, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0674, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0675, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0676, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0677, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0678, 0x000F, 0x0, 0x0, 0x0, 0x0},
+
+
+ {0x0679, 0x0881, 0xFB66, 0xFB67, 0xFB68, 0xFB69},
+ {0x067A, 0x0881, 0xFB5E, 0xFB5F, 0xFB60, 0xFB61},
+ {0x067B, 0x0881, 0xFB52, 0xFB53, 0xFB54, 0xFB55},
+ {0x067C, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x067D, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x067E, 0x0881, 0xFB56, 0xFB57, 0xFB58, 0xFB59},
+ {0x067F, 0x0881, 0xFB62, 0xFB63, 0xFB64, 0xFB65},
+ {0x0680, 0x0881, 0xFB5A, 0xFB5B, 0xFB5C, 0xFB5D},
+
+ {0x0681, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0682, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0683, 0x0881, 0xFB76, 0xFB77, 0xFB78, 0xFB79},
+ {0x0684, 0x0881, 0xFB72, 0xFB73, 0xFB74, 0xFB75},
+ {0x0685, 0x000F, 0x0, 0x0, 0x0, 0x0},
+
+ {0x0686, 0x0881, 0xFB7A, 0xFB7B, 0xFB7C, 0xFB7D},
+ {0x0687, 0x0881, 0xFB7E, 0xFB7F, 0xFB80, 0xFB81},
+ {0x0688, 0x0804, 0xFB88, 0xFB89, 0xFB89, 0xFB89},
+ {0x0689, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x068A, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x068B, 0x000F, 0x0, 0x0, 0x0, 0x0},
+
+ {0x068C, 0x0804, 0xFB84, 0xFB85, 0xFB85, 0xFB85},
+ {0x068D, 0x0804, 0xFB82, 0xFB83, 0xFB83, 0xFB83},
+ {0x068E, 0x0804, 0xFB86, 0xFB87, 0xFB83, 0xFB83},
+ {0x068F, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0690, 0x000F, 0x0, 0x0, 0x0, 0x0},
+
+ {0x0691, 0x0804, 0xFB8C, 0xFB8D, 0xFB8D, 0xFB8D},
+ {0x0692, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0693, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0694, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0695, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0696, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x0697, 0x000F, 0x0, 0x0, 0x0, 0x0},
+
+ {0x0698, 0x0804, 0xFB8A, 0xFB8B, 0xFB8B, 0xFB8B},
+ {0x0699, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x069A, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x069B, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x069C, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x069D, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x069E, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x069F, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06A0, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06A1, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06A2, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06A3, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06A4, 0x0881, 0xFB6A, 0xFB6B, 0xFB6C, 0xFB6D},
+ {0x06A5, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06A6, 0x0881, 0xFB6E, 0xFB6F, 0xFB70, 0xFB71},
+ {0x06A7, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06A8, 0x000F, 0x0, 0x0, 0x0, 0x0},
+
+ {0x06A9, 0x0881, 0xFB8E, 0xFB8F, 0xFB90, 0xFB91},
+ {0x06AA, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06AB, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06AC, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06AD, 0x0881, 0xFBD3, 0xFBD4, 0xFBD5, 0xFBD6},
+ {0x06AE, 0x000F, 0x0, 0x0, 0x0, 0x0},
+
+ {0x06AF, 0x0881, 0xFB92, 0xFB93, 0xFB94, 0xFB95},
+ {0x06B0, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06B1, 0x0881, 0xFB9A, 0xFB9B, 0xFB9C, 0xFB9D},
+ {0x06B2, 0x000F, 0x0, 0x0, 0x0, 0x0},
+
+ {0x06B3, 0x0881, 0xFB96, 0xFB97, 0xFB98, 0xFB99},
+ {0x06B4, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06B5, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06B6, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06B7, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06B8, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06B9, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06BA, 0x0804, 0xFB9E, 0xFB9F, 0xFB9F, 0xFB9F},
+ {0x06BB, 0x0881, 0xFBA0, 0xFBA1, 0xFBA2, 0xFBA3},
+ {0x06BC, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06BD, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06BE, 0x0881, 0xFBAA, 0xFBAB, 0xFBAC, 0xFBAD},
+ {0x06BF, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06C0, 0x0804, 0xFBA4, 0xFBA5, 0xFBA5, 0xFBA5},
+ {0x06C1, 0x0881, 0xFBA6, 0xFBA7, 0xFBA8, 0xFBA9},
+ {0x06C2, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06C3, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06C4, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06C5, 0x0804, 0xFBE0, 0xFBE1, 0xFBE1, 0xFBE1},
+ {0x06C6, 0x0804, 0xFBD9, 0xFBDA, 0xFBDA, 0xFBDA},
+ {0x06C7, 0x0804, 0xFBD7, 0xFBD8, 0xFBD8, 0xFBD8},
+ {0x06C8, 0x0804, 0xFBDB, 0xFBDC, 0xFBDC, 0xFBDC},
+ {0x06C9, 0x0804, 0xFBE2, 0xFBE3, 0xFBE3, 0xFBE3},
+ {0x06CA, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06CB, 0x0804, 0xFBDE, 0xFBDF, 0xFBDF, 0xFBDF},
+ {0x06CC, 0x0881, 0xFBFC, 0xFBFD, 0xFBFE, 0xFBFF},
+ {0x06CD, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06CE, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06CF, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06D0, 0x0881, 0xFBE4, 0xFBE5, 0xFBE6, 0xFBE7},
+ {0x06D1, 0x000F, 0x0, 0x0, 0x0, 0x0},
+ {0x06D2, 0x0804, 0xFBAE, 0xFBAF, 0xFBAF, 0xFBAF},
+ {0x06D3, 0x0804, 0xFBB0, 0xFBB1, 0xFBB1, 0xFBB1}
+ };
+
+ /* @hide */
+ public static String reshape(String Str) {
+ String Temp = " " + Str + " ";
+ char pre, at, post;
+ StringBuilder reshapedString = new StringBuilder();
+ int i = 0;
+ int len = Str.length();
+
+ char post_post;
+ char pre_pre = ' ';
+
+ while (i < len) {
+ pre = Temp.charAt(i + 2);
+ at = Temp.charAt(i + 1);
+ post = Temp.charAt(i);
+
+ int which_case = getCase(at);
+ int what_case_post = getCase(post);
+ int what_case_pre = getCase(pre);
+ int what_case_post_post;
+ int what_case_pre_pre;
+ // which_case=0x000F&
+ // Log.v("what case"," :" +which_case);
+ int pre_step = 0;
+ if (what_case_pre == TASHKEEL) {
+ pre = pre_pre;
+ what_case_pre = getCase(pre);
+ }
+ if ((what_case_pre & LEFT_CHAR_MASK) == LEFT_CHAR_MASK) {
+ pre_step = 1;
+
+ }
+
+ switch (which_case & 0x000F) {
+
+ case NOTUSED_CHAR:
+ case NOTARABIC_CHAR:
+
+ reshapedString.append(at);
+
+ i++;
+ continue;
+ case NORIGHT_NOLEFT_CHAR:
+ case TATWEEL_CHAR:
+ reshapedString.append(getShape(at, 0));
+
+ i++;
+ continue;
+ case RIGHT_LEFT_CHAR_LAM:
+
+ if ((what_case_post & 0x000F) == RIGHT_NOLEFT_CHAR_ALEF) {
+ reshapedString.append(getShape(post, pre_step + 2));
+ i = i + 2;
+
+ continue;
+ } else if ((what_case_post & RIGHT_NOLEFT_CHAR_MASK) == RIGHT_NOLEFT_CHAR_MASK) {
+ reshapedString.append(getShape(at, 2 + pre_step));
+ i = i + 1;
+
+ continue;
+
+ } else if (what_case_post == TANWEEN) {
+ reshapedString.append(getShape(at, pre_step));
+ i = i + 1;
+ continue;
+
+ } else if (what_case_post == TASHKEEL) {
+ post_post = Temp.charAt(i + 3);
+ what_case_post_post = getCase(post_post);
+ if ((what_case_post_post & RIGHT_NOLEFT_CHAR_MASK) == RIGHT_NOLEFT_CHAR_MASK) {
+ reshapedString.append(getShape(at, 2 + pre_step));
+ i = i + 1;
+
+ continue;
+
+ } else {
+ reshapedString.append(getShape(at, pre_step));
+ i = i + 1;
+ continue;
+
+ }
+
+ } else {
+ reshapedString.append(getShape(at, pre_step));
+ i = i + 1;
+ continue;
+
+ }
+
+ case RIGHT_LEFT_CHAR:
+ if ((what_case_post & RIGHT_NOLEFT_CHAR_MASK) == RIGHT_NOLEFT_CHAR_MASK) {
+ reshapedString.append(getShape(at, 2 + pre_step));
+ i = i + 1;
+ continue;
+
+ } else if (what_case_post == TANWEEN) {
+ reshapedString.append(getShape(at, pre_step));
+ i = i + 1;
+ continue;
+
+ } else if (what_case_post == TASHKEEL) {
+ post_post = Temp.charAt(i + 3);
+ what_case_post_post = getCase(post_post);
+ if ((what_case_post_post & RIGHT_NOLEFT_CHAR_MASK) == RIGHT_NOLEFT_CHAR_MASK) {
+ reshapedString.append(getShape(at, 2 + pre_step));
+ i = i + 1;
+ continue;
+
+ } else {
+ reshapedString.append(getShape(at, pre_step));
+ i = i + 1;
+ continue;
+
+ }
+
+ } else {
+ reshapedString.append(getShape(at, pre_step));
+ i = i + 1;
+ continue;
+
+ }
+ case RIGHT_NOLEFT_CHAR_ALEF:
+ case RIGHT_NOLEFT_CHAR:
+ reshapedString.append(getShape(at, pre_step));
+ i = i + 1;
+ continue;
+ case TASHKEEL:
+ reshapedString.append(getShape(at, 0));
+ i++;
+ pre_pre = pre;
+ continue;
+ case TANWEEN:
+ reshapedString.append(getShape(at, 0));
+ i++;
+ pre_pre = pre;
+ continue;
+
+ default:
+ reshapedString.append(getShape(at, 0));
+ i++;
+
+ }
+ }
+
+ return reshapedString.toString();
+ }
+
+ static int getCase(char ch) {
+ if (ch < 0x0621 || ch > 0x06d2) {
+ return 0;
+ } else {
+ return allchar[(int) ch - 0x0621][1];
+ }
+ }
+
+ static char getShape(char ch, int which_shape) {
+ return allchar[(int) ch - 0x0621][2 + which_shape];
+ }
+
+}