1/*
2 * Copyright (C) 2011 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
17package com.android.bidi;
18
19import android.content.Context;
20import android.graphics.Canvas;
21import android.graphics.Color;
22import android.graphics.Paint;
23import android.graphics.Rect;
24import android.text.TextPaint;
25import android.util.AttributeSet;
26import android.util.Log;
27import android.view.View;
28
29public class BiDiTestView extends View {
30
31    private static final String TAG = "BiDiTestView";
32
33    private static final int BORDER_PADDING = 4;
34    private static final int TEXT_PADDING = 16;
35    private static final int TEXT_SIZE = 16;
36    private static final int ORIGIN = 80;
37
38    private static final float DEFAULT_ITALIC_SKEW_X = -0.25f;
39
40    private Rect rect = new Rect();
41
42    private String NORMAL_TEXT;
43    private String NORMAL_LONG_TEXT;
44    private String NORMAL_LONG_TEXT_2;
45    private String NORMAL_LONG_TEXT_3;
46    private String ITALIC_TEXT;
47    private String BOLD_TEXT;
48    private String BOLD_ITALIC_TEXT;
49    private String ARABIC_TEXT;
50    private String CHINESE_TEXT;
51    private String MIXED_TEXT_1;
52    private String HEBREW_TEXT;
53    private String RTL_TEXT;
54    private String THAI_TEXT;
55
56    private int currentTextSize;
57
58    public BiDiTestView(Context context) {
59        super(context);
60        init(context);
61    }
62
63    public BiDiTestView(Context context, AttributeSet attrs) {
64        super(context, attrs);
65        init(context);
66    }
67
68    public BiDiTestView(Context context, AttributeSet attrs, int defStyle) {
69        super(context, attrs, defStyle);
70        init(context);
71    }
72
73    private void init(Context context) {
74        NORMAL_TEXT = context.getString(R.string.normal_text);
75        NORMAL_LONG_TEXT = context.getString(R.string.normal_long_text);
76        NORMAL_LONG_TEXT_2 = context.getString(R.string.normal_long_text_2);
77        NORMAL_LONG_TEXT_3 = context.getString(R.string.normal_long_text_3);
78        ITALIC_TEXT = context.getString(R.string.italic_text);
79        BOLD_TEXT = context.getString(R.string.bold_text);
80        BOLD_ITALIC_TEXT = context.getString(R.string.bold_italic_text);
81        ARABIC_TEXT = context.getString(R.string.arabic_text);
82        CHINESE_TEXT = context.getString(R.string.chinese_text);
83        MIXED_TEXT_1 = context.getString(R.string.mixed_text_1);
84        HEBREW_TEXT = context.getString(R.string.hebrew_text);
85        RTL_TEXT = context.getString(R.string.rtl);
86        THAI_TEXT = context.getString(R.string.pointer_location);
87    }
88
89    public void setCurrentTextSize(int size) {
90        currentTextSize = size;
91        invalidate();
92    }
93
94    @Override
95    public void onDraw(Canvas canvas) {
96        drawInsideRect(canvas, new Paint(), Color.BLACK);
97
98        int deltaX = 0;
99
100        deltaX  = testString(canvas, NORMAL_TEXT, ORIGIN, ORIGIN,
101                false, false,  Paint.DIRECTION_LTR, currentTextSize);
102
103        deltaX += testString(canvas, ITALIC_TEXT, ORIGIN + deltaX, ORIGIN,
104                true, false,  Paint.DIRECTION_LTR, currentTextSize);
105
106        deltaX += testString(canvas, BOLD_TEXT, ORIGIN + deltaX, ORIGIN,
107                false, true,  Paint.DIRECTION_LTR, currentTextSize);
108
109        deltaX += testString(canvas, BOLD_ITALIC_TEXT, ORIGIN + deltaX, ORIGIN,
110                true, true,  Paint.DIRECTION_LTR, currentTextSize);
111
112        // Test with a long string
113        deltaX = testString(canvas, NORMAL_LONG_TEXT, ORIGIN, ORIGIN + 2 * currentTextSize,
114                false, false,  Paint.DIRECTION_LTR, currentTextSize);
115
116        // Test with a long string
117        deltaX = testString(canvas, NORMAL_LONG_TEXT_2, ORIGIN, ORIGIN + 4 * currentTextSize,
118                false, false,  Paint.DIRECTION_LTR, currentTextSize);
119
120        // Test with a long string
121        deltaX = testString(canvas, NORMAL_LONG_TEXT_3, ORIGIN, ORIGIN + 6 * currentTextSize,
122                false, false,  Paint.DIRECTION_LTR, currentTextSize);
123
124        // Test Arabic ligature
125        deltaX = testString(canvas, ARABIC_TEXT, ORIGIN, ORIGIN + 8 * currentTextSize,
126                false, false,  Paint.DIRECTION_RTL, currentTextSize);
127
128        // Test Chinese
129        deltaX = testString(canvas, CHINESE_TEXT, ORIGIN, ORIGIN + 10 * currentTextSize,
130                false, false,  Paint.DIRECTION_LTR, currentTextSize);
131
132        // Test Mixed (English and Arabic)
133        deltaX = testString(canvas, MIXED_TEXT_1, ORIGIN, ORIGIN + 12 * currentTextSize,
134                false, false,  Paint.DIRECTION_LTR, currentTextSize);
135
136        // Test Hebrew
137        deltaX = testString(canvas, RTL_TEXT, ORIGIN, ORIGIN + 14 * currentTextSize,
138                false, false,  Paint.DIRECTION_RTL, currentTextSize);
139
140        // Test Thai
141        deltaX = testString(canvas, THAI_TEXT, ORIGIN, ORIGIN + 16 * currentTextSize,
142                false, false,  Paint.DIRECTION_LTR, currentTextSize);
143    }
144
145    private int testString(Canvas canvas, String text, int x, int y,
146            boolean isItalic, boolean isBold, int dir, int textSize) {
147
148        TextPaint paint = new TextPaint();
149        paint.setAntiAlias(true);
150
151        // Set paint properties
152        boolean oldFakeBold = paint.isFakeBoldText();
153        paint.setFakeBoldText(isBold);
154
155        float oldTextSkewX = paint.getTextSkewX();
156        if (isItalic) {
157            paint.setTextSkewX(DEFAULT_ITALIC_SKEW_X);
158        }
159
160        paint.setTextSize(textSize);
161        paint.setColor(Color.WHITE);
162        canvas.drawText(text, x, y, paint);
163
164        int length = text.length();
165        float[] advances = new float[length];
166        float textWidthHB = paint.getTextRunAdvances(text, 0, length, 0, length, dir, advances, 0);
167        setPaintDir(paint, dir);
168        float textWidthICU = paint.getTextRunAdvances(text, 0, length, 0, length, dir, advances, 0,
169                1 /* use ICU */);
170
171        logAdvances(text, textWidthHB, textWidthICU, advances);
172        drawMetricsAroundText(canvas, x, y, textWidthHB, textWidthICU, textSize, Color.RED, Color.GREEN);
173
174        // Restore old paint properties
175        paint.setFakeBoldText(oldFakeBold);
176        paint.setTextSkewX(oldTextSkewX);
177
178        return (int) Math.ceil(textWidthHB) + TEXT_PADDING;
179    }
180
181    private void setPaintDir(Paint paint, int dir) {
182        Log.v(TAG, "Setting Paint dir=" + dir);
183        paint.setBidiFlags(dir);
184    }
185
186    private void drawInsideRect(Canvas canvas, Paint paint, int color) {
187        paint.setColor(color);
188        int width = getWidth();
189        int height = getHeight();
190        rect.set(BORDER_PADDING, BORDER_PADDING, width - BORDER_PADDING, height - BORDER_PADDING);
191        canvas.drawRect(rect, paint);
192    }
193
194    private void drawMetricsAroundText(Canvas canvas, int x, int y, float textWidthHB,
195            float textWidthICU, int textSize, int color, int colorICU) {
196        Paint paint = new Paint();
197        paint.setColor(color);
198        canvas.drawLine(x, y - textSize, x, y + 8, paint);
199        canvas.drawLine(x, y + 8, x + textWidthHB, y + 8, paint);
200        canvas.drawLine(x + textWidthHB, y - textSize, x + textWidthHB, y + 8, paint);
201        paint.setColor(colorICU);
202        canvas.drawLine(x + textWidthICU, y - textSize, x + textWidthICU, y + 8, paint);
203    }
204
205    private void logAdvances(String text, float textWidth, float textWidthICU, float[] advances) {
206        Log.v(TAG, "Advances for text: " + text + " total= " + textWidth + " - totalICU= " + textWidthICU);
207//        int length = advances.length;
208//        for(int n=0; n<length; n++){
209//            Log.v(TAG, "adv[" + n + "]=" + advances[n]);
210//        }
211    }
212}
213