10d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta/*
20d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta * Copyright (C) 2013 The Android Open Source Project
30d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta *
40d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License");
50d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta * you may not use this file except in compliance with the License.
60d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta * You may obtain a copy of the License at
70d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta *
80d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta *      http://www.apache.org/licenses/LICENSE-2.0
90d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta *
100d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta * Unless required by applicable law or agreed to in writing, software
110d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS,
120d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta * See the License for the specific language governing permissions and
140d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta * limitations under the License.
150d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta */
160d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
170d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptapackage android.graphics;
180d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
19b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Guptaimport com.android.ide.common.rendering.api.LayoutLog;
20b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Guptaimport com.android.layoutlib.bridge.Bridge;
21b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta
220d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport java.awt.Font;
230d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport java.awt.Graphics2D;
24130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Guptaimport java.awt.Toolkit;
250d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport java.awt.font.FontRenderContext;
260d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport java.awt.font.GlyphVector;
275ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Guptaimport java.awt.geom.Rectangle2D;
28130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Guptaimport java.util.ArrayList;
290d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport java.util.LinkedList;
300d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport java.util.List;
310d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
320d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport com.ibm.icu.lang.UScript;
330d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport com.ibm.icu.lang.UScriptRun;
3484d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Guptaimport com.ibm.icu.text.Bidi;
3584d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Guptaimport com.ibm.icu.text.BidiRun;
360d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
370d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport android.graphics.Paint_Delegate.FontInfo;
380d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
390d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta/**
400d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta * Render the text by breaking it into various scripts and using the right font for each script.
410d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta * Can be used to measure the text without actually drawing it.
420d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta */
430d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta@SuppressWarnings("deprecation")
440d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptapublic class BidiRenderer {
450d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
4684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    private static class ScriptRun {
470d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        int start;
480d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        int limit;
490d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        boolean isRtl;
500d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        int scriptCode;
51130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        Font font;
520d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
530d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        public ScriptRun(int start, int limit, boolean isRtl) {
540d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            this.start = start;
550d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            this.limit = limit;
560d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            this.isRtl = isRtl;
570d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            this.scriptCode = UScript.INVALID_CODE;
580d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
590d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
600d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
61130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta    private final Graphics2D mGraphics;
62130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta    private final Paint_Delegate mPaint;
6345dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta    private char[] mText;
64b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta    // This List can contain nulls. A null font implies that the we weren't able to load the font
65b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta    // properly. So, if we encounter a situation where we try to use that font, log a warning.
66130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta    private List<Font> mFonts;
675ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta    // Bounds of the text drawn so far.
6845dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta    private RectF mBounds;
6945dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta    private float mBaseline;
700d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
710d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    /**
720d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param graphics May be null.
730d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param paint The Paint to use to get the fonts. Should not be null.
740d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param text Unidirectional text. Should not be null.
750d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     */
7684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    public BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
770d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        assert (paint != null);
7845dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        mGraphics = graphics;
7945dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        mPaint = paint;
8045dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        mText = text;
81130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        mFonts = new ArrayList<Font>(paint.getFonts().size());
82130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        for (FontInfo fontInfo : paint.getFonts()) {
83e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta            if (fontInfo == null) {
84e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta                mFonts.add(null);
85e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta                continue;
86e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta            }
87130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            mFonts.add(fontInfo.mFont);
88130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        }
8984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        mBounds = new RectF();
900d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
910d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
920d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    /**
930d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     *
9484d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * @param x The x-coordinate of the left edge of where the text should be drawn on the given
9584d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     *            graphics.
9684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * @param y The y-coordinate at which to draw the text on the given mGraphics.
970d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     *
9884d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     */
9984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    public BidiRenderer setRenderLocation(float x, float y) {
10084d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        mBounds = new RectF(x, y, x, y);
10184d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        mBaseline = y;
10284d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        return this;
10384d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    }
10484d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta
10584d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    /**
10684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * Perform Bidi Analysis on the text and then render it.
10784d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * <p/>
10884d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * To skip the analysis and render unidirectional text, see {@link
10984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * #renderText(int, int, boolean, float[], int, boolean)}
11084d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     */
11184d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    public RectF renderText(int start, int limit, int bidiFlags, float[] advances,
11284d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            int advancesIndex, boolean draw) {
11384d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        Bidi bidi = new Bidi(mText, start, null, 0, limit - start, getIcuFlags(bidiFlags));
11484d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        for (int i = 0; i < bidi.countRuns(); i++) {
11584d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            BidiRun visualRun = bidi.getVisualRun(i);
11684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            boolean isRtl = visualRun.getDirection() == Bidi.RTL;
11784d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            renderText(visualRun.getStart(), visualRun.getLimit(), isRtl, advances,
11884d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                    advancesIndex, draw);
11984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        }
12084d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        return mBounds;
12184d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    }
12284d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta
12384d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    /**
12484d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * Render unidirectional text.
12584d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * <p/>
12684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * This method can also be used to measure the width of the text without actually drawing it.
12784d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * <p/>
1280d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param start index of the first character
1290d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param limit index of the first character that should not be rendered.
1300d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param isRtl is the text right-to-left
1310d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param advances If not null, then advances for each character to be rendered are returned
1320d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     *            here.
1330d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param advancesIndex index into advances from where the advances need to be filled.
13445dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta     * @param draw If true and {@code graphics} is not null, draw the rendered text on the graphics
1350d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     *            at the given co-ordinates
1365ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta     * @return A rectangle specifying the bounds of the text drawn.
1370d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     */
13884d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    public RectF renderText(int start, int limit, boolean isRtl, float[] advances,
13984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            int advancesIndex, boolean draw) {
1400d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        // We break the text into scripts and then select font based on it and then render each of
1410d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        // the script runs.
142130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
1430d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
1440d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
1455ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta            renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
1460d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            advancesIndex += run.limit - run.start;
1470d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
14845dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        return mBounds;
1490d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
1500d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
1510d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    /**
1525ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta     * Render a script run to the right of the bounds passed. Use the preferred font to render as
1535ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta     * much as possible. This also implements a fallback mechanism to render characters that cannot
1545ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta     * be drawn using the preferred font.
1550d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     */
156130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta    private void renderScript(int start, int limit, Font preferredFont, int flag,
15745dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            float[] advances, int advancesIndex, boolean draw) {
158130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        if (mFonts.size() == 0 || preferredFont == null) {
1595ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta            return;
1600d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
1610d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
1620d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        while (start < limit) {
1630d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            boolean foundFont = false;
164130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit);
1650d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            if (canDisplayUpTo == -1) {
1665ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                // We can draw all characters in the text.
1675ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                render(start, limit, preferredFont, flag, advances, advancesIndex, draw);
1685ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                return;
16945dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            }
17045dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            if (canDisplayUpTo > start) {
17145dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta                // We can draw something.
1725ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, draw);
1730d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                advancesIndex += canDisplayUpTo - start;
1740d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                start = canDisplayUpTo;
1750d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            }
1760d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
1775ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta            // The current character cannot be drawn with the preferred font. Cycle through all the
1785ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta            // fonts to check which one can draw it.
17945dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
180130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            for (Font font : mFonts) {
181b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                if (font == null) {
182b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    logFontWarning();
183b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    continue;
184b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                }
185130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta                canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount);
1860d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                if (canDisplayUpTo == -1) {
1875ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                    render(start, start+charCount, font, flag, advances, advancesIndex, draw);
1880d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                    start += charCount;
1890d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                    advancesIndex += charCount;
1900d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                    foundFont = true;
1910d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                    break;
1920d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                }
1930d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            }
1940d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            if (!foundFont) {
1950d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                // No font can display this char. Use the preferred font. The char will most
1960d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                // probably appear as a box or a blank space. We could, probably, use some
1970d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                // heuristics and break the character into the base character and diacritics and
1980d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                // then draw it, but it's probably not worth the effort.
1995ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                render(start, start + charCount, preferredFont, flag, advances, advancesIndex,
2005ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                        draw);
2010d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                start += charCount;
2020d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                advancesIndex += charCount;
2030d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            }
2040d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
2050d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
2060d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
207b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta    private static void logFontWarning() {
208b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta        Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
209b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                "Some fonts could not be loaded. The rendering may not be perfect. " +
210b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                        "Try running the IDE with JRE 7.", null, null);
211b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta    }
212b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta
2130d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    /**
21445dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta     * Renders the text to the right of the bounds with the given font.
21545dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta     * @param font The font to render the text with.
2160d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     */
217130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta    private void render(int start, int limit, Font font, int flag, float[] advances,
2185ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta            int advancesIndex, boolean draw) {
2190d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
220130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        FontRenderContext frc;
221130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        if (mGraphics != null) {
222130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            frc = mGraphics.getFontRenderContext();
223130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        } else {
224130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
225130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            // Metrics obtained this way don't have anti-aliasing set. So,
226130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            // we create a new FontRenderContext with anti-aliasing set.
227130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics());
228130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        }
229130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
2300d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        int ng = gv.getNumGlyphs();
2310d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        int[] ci = gv.getGlyphCharIndices(0, ng, null);
23245dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        if (advances != null) {
23345dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            for (int i = 0; i < ng; i++) {
2340d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                int adv_idx = advancesIndex + ci[i];
23545dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta                advances[adv_idx] += gv.getGlyphMetrics(i).getAdvanceX();
2360d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            }
2370d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
23845dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        if (draw && mGraphics != null) {
23945dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            mGraphics.drawGlyphVector(gv, mBounds.right, mBaseline);
2405ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        }
24145dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta
24245dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        // Update the bounds.
24345dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        Rectangle2D awtBounds = gv.getLogicalBounds();
24445dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        RectF bounds = awtRectToAndroidRect(awtBounds, mBounds.right, mBaseline);
24545dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        // If the width of the bounds is zero, no text had been drawn earlier. Hence, use the
24645dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        // coordinates from the bounds as an offset.
24745dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        if (Math.abs(mBounds.right - mBounds.left) == 0) {
24845dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            mBounds = bounds;
2495ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        } else {
25045dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            mBounds.union(bounds);
2510d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
2525ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta    }
2535ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta
25445dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta    // --- Static helper methods ---
25545dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta
25645dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta    private static RectF awtRectToAndroidRect(Rectangle2D awtRec, float offsetX, float offsetY) {
2575ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        float left = (float) awtRec.getX();
2585ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        float top = (float) awtRec.getY();
2595ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        float right = (float) (left + awtRec.getWidth());
2605ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        float bottom = (float) (top + awtRec.getHeight());
2615ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        RectF androidRect = new RectF(left, top, right, bottom);
2625ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        androidRect.offset(offsetX, offsetY);
2635ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        return androidRect;
2640d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
2650d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
2660d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    /* package */  static List<ScriptRun> getScriptRuns(char[] text, int start, int limit,
267130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            boolean isRtl, List<Font> fonts) {
2680d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>();
2690d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
2700d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        int count = limit - start;
2710d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        UScriptRun uScriptRun = new UScriptRun(text, start, count);
2720d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        while (uScriptRun.next()) {
2730d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            int scriptStart = uScriptRun.getScriptStart();
2740d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            int scriptLimit = uScriptRun.getScriptLimit();
2750d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            ScriptRun run = new ScriptRun(scriptStart, scriptLimit, isRtl);
2760d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            run.scriptCode = uScriptRun.getScriptCode();
2770d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            setScriptFont(text, run, fonts);
2780d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            scriptRuns.add(run);
2790d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
2800d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
2810d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        return scriptRuns;
2820d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
2830d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
2840d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    // TODO: Replace this method with one which returns the font based on the scriptCode.
2850d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    private static void setScriptFont(char[] text, ScriptRun run,
286130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            List<Font> fonts) {
287130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        for (Font font : fonts) {
288b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta            if (font == null) {
289b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                logFontWarning();
290b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                continue;
291b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta            }
292130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            if (font.canDisplayUpTo(text, run.start, run.limit) == -1) {
293130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta                run.font = font;
2940d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                return;
2950d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            }
2960d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
2970d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        run.font = fonts.get(0);
2980d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
29984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta
30084d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    private static int getIcuFlags(int bidiFlag) {
30184d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        switch (bidiFlag) {
30284d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            case Paint.BIDI_LTR:
30384d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            case Paint.BIDI_FORCE_LTR:
30484d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                return Bidi.DIRECTION_LEFT_TO_RIGHT;
30584d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            case Paint.BIDI_RTL:
30684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            case Paint.BIDI_FORCE_RTL:
30784d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                return Bidi.DIRECTION_RIGHT_TO_LEFT;
30884d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            case Paint.BIDI_DEFAULT_LTR:
30984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
31084d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            case Paint.BIDI_DEFAULT_RTL:
31184d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                return Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT;
31284d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            default:
31384d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                assert false;
31484d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
31584d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        }
31684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    }
3170d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta}
318