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
22d77b9ed7dcc42efca33b225c4594a30aab9e709cDeepanshu Guptaimport android.graphics.Paint_Delegate.FontInfo;
23d77b9ed7dcc42efca33b225c4594a30aab9e709cDeepanshu Guptaimport android.icu.lang.UScript;
24d77b9ed7dcc42efca33b225c4594a30aab9e709cDeepanshu Guptaimport android.icu.lang.UScriptRun;
25d77b9ed7dcc42efca33b225c4594a30aab9e709cDeepanshu Guptaimport android.icu.text.Bidi;
26d77b9ed7dcc42efca33b225c4594a30aab9e709cDeepanshu Guptaimport android.icu.text.BidiRun;
27d77b9ed7dcc42efca33b225c4594a30aab9e709cDeepanshu Gupta
280d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport java.awt.Font;
290d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport java.awt.Graphics2D;
30130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Guptaimport java.awt.Toolkit;
310d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport java.awt.font.FontRenderContext;
320d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport java.awt.font.GlyphVector;
33f31c1e98d4fda5a1296a4dde4bc1ff6f73c5db64Diego Perezimport java.awt.geom.AffineTransform;
345ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Guptaimport java.awt.geom.Rectangle2D;
35130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Guptaimport java.util.ArrayList;
360d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport java.util.LinkedList;
370d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Guptaimport java.util.List;
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 {
45f31c1e98d4fda5a1296a4dde4bc1ff6f73c5db64Diego Perez    private static String JAVA_VENDOR = System.getProperty("java.vendor");
460d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
4784d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    private static class ScriptRun {
480d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        int start;
490d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        int limit;
500d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        boolean isRtl;
510d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        int scriptCode;
52130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        Font font;
530d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
540d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        public ScriptRun(int start, int limit, boolean isRtl) {
550d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            this.start = start;
560d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            this.limit = limit;
570d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            this.isRtl = isRtl;
580d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            this.scriptCode = UScript.INVALID_CODE;
590d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
600d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
610d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
62130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta    private final Graphics2D mGraphics;
63130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta    private final Paint_Delegate mPaint;
6445dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta    private char[] mText;
65b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta    // This List can contain nulls. A null font implies that the we weren't able to load the font
66b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta    // properly. So, if we encounter a situation where we try to use that font, log a warning.
67130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta    private List<Font> mFonts;
685ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta    // Bounds of the text drawn so far.
6945dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta    private RectF mBounds;
7045dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta    private float mBaseline;
710d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
720d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    /**
730d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param graphics May be null.
740d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param paint The Paint to use to get the fonts. Should not be null.
750d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param text Unidirectional text. Should not be null.
760d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     */
7784d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    public BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
780d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        assert (paint != null);
7945dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        mGraphics = graphics;
8045dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        mPaint = paint;
8145dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        mText = text;
82130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        mFonts = new ArrayList<Font>(paint.getFonts().size());
83130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        for (FontInfo fontInfo : paint.getFonts()) {
84e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta            if (fontInfo == null) {
85e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta                mFonts.add(null);
86e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta                continue;
87e644ff8d92ba040d11636be0fb6c433b52bcc6c2Deepanshu Gupta            }
88130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            mFonts.add(fontInfo.mFont);
89130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        }
9084d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        mBounds = new RectF();
910d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
920d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
930d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    /**
940d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     *
9584d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * @param x The x-coordinate of the left edge of where the text should be drawn on the given
9684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     *            graphics.
9784d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * @param y The y-coordinate at which to draw the text on the given mGraphics.
980d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     *
9984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     */
10084d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    public BidiRenderer setRenderLocation(float x, float y) {
10184d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        mBounds = new RectF(x, y, x, y);
10284d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        mBaseline = y;
10384d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        return this;
10484d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    }
10584d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta
10684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    /**
10784d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * Perform Bidi Analysis on the text and then render it.
10884d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * <p/>
10984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * To skip the analysis and render unidirectional text, see {@link
11084d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * #renderText(int, int, boolean, float[], int, boolean)}
11184d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     */
11284d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    public RectF renderText(int start, int limit, int bidiFlags, float[] advances,
11384d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            int advancesIndex, boolean draw) {
11484d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        Bidi bidi = new Bidi(mText, start, null, 0, limit - start, getIcuFlags(bidiFlags));
11584d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        for (int i = 0; i < bidi.countRuns(); i++) {
11684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            BidiRun visualRun = bidi.getVisualRun(i);
11784d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            boolean isRtl = visualRun.getDirection() == Bidi.RTL;
11884d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            renderText(visualRun.getStart(), visualRun.getLimit(), isRtl, advances,
11984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                    advancesIndex, draw);
12084d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        }
12184d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        return mBounds;
12284d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    }
12384d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta
12484d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    /**
12584d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * Render unidirectional text.
12684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * <p/>
12784d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * This method can also be used to measure the width of the text without actually drawing it.
12884d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta     * <p/>
1290d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param start index of the first character
1300d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param limit index of the first character that should not be rendered.
1310d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param isRtl is the text right-to-left
1320d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param advances If not null, then advances for each character to be rendered are returned
1330d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     *            here.
1340d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     * @param advancesIndex index into advances from where the advances need to be filled.
13545dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta     * @param draw If true and {@code graphics} is not null, draw the rendered text on the graphics
1360d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     *            at the given co-ordinates
1375ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta     * @return A rectangle specifying the bounds of the text drawn.
1380d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     */
13984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    public RectF renderText(int start, int limit, boolean isRtl, float[] advances,
14084d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            int advancesIndex, boolean draw) {
1410d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        // We break the text into scripts and then select font based on it and then render each of
1420d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        // the script runs.
143130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
1440d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
1450d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
1465ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta            renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
1470d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            advancesIndex += run.limit - run.start;
1480d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
14945dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        return mBounds;
1500d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
1510d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
1520d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    /**
1535ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta     * Render a script run to the right of the bounds passed. Use the preferred font to render as
1545ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta     * much as possible. This also implements a fallback mechanism to render characters that cannot
1555ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta     * be drawn using the preferred font.
1560d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     */
157130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta    private void renderScript(int start, int limit, Font preferredFont, int flag,
15845dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            float[] advances, int advancesIndex, boolean draw) {
159130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        if (mFonts.size() == 0 || preferredFont == null) {
1605ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta            return;
1610d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
1620d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
1630d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        while (start < limit) {
1640d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            boolean foundFont = false;
165130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit);
1660d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            if (canDisplayUpTo == -1) {
1675ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                // We can draw all characters in the text.
1685ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                render(start, limit, preferredFont, flag, advances, advancesIndex, draw);
1695ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                return;
17045dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            }
17145dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            if (canDisplayUpTo > start) {
17245dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta                // We can draw something.
1735ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                render(start, canDisplayUpTo, preferredFont, flag, advances, advancesIndex, draw);
1740d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                advancesIndex += canDisplayUpTo - start;
1750d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                start = canDisplayUpTo;
1760d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            }
1770d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
1785ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta            // The current character cannot be drawn with the preferred font. Cycle through all the
1795ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta            // fonts to check which one can draw it.
18045dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
181130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            for (Font font : mFonts) {
182b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                if (font == null) {
183b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    logFontWarning();
184b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                    continue;
185b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                }
186130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta                canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount);
1870d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                if (canDisplayUpTo == -1) {
1885ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                    render(start, start+charCount, font, flag, advances, advancesIndex, draw);
1890d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                    start += charCount;
1900d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                    advancesIndex += charCount;
1910d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                    foundFont = true;
1920d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                    break;
1930d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                }
1940d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            }
1950d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            if (!foundFont) {
1960d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                // No font can display this char. Use the preferred font. The char will most
1970d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                // probably appear as a box or a blank space. We could, probably, use some
1980d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                // heuristics and break the character into the base character and diacritics and
1990d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                // then draw it, but it's probably not worth the effort.
2005ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                render(start, start + charCount, preferredFont, flag, advances, advancesIndex,
2015ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta                        draw);
2020d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                start += charCount;
2030d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                advancesIndex += charCount;
2040d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            }
2050d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
2060d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
2070d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
208b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta    private static void logFontWarning() {
209b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta        Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
210b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                "Some fonts could not be loaded. The rendering may not be perfect. " +
211b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                        "Try running the IDE with JRE 7.", null, null);
212b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta    }
213b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta
2140d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    /**
21545dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta     * Renders the text to the right of the bounds with the given font.
21645dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta     * @param font The font to render the text with.
2170d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta     */
218130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta    private void render(int start, int limit, Font font, int flag, float[] advances,
2195ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta            int advancesIndex, boolean draw) {
2200d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
221130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        FontRenderContext frc;
222130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        if (mGraphics != null) {
223130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            frc = mGraphics.getFontRenderContext();
224130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        } else {
225130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
226f31c1e98d4fda5a1296a4dde4bc1ff6f73c5db64Diego Perez
227130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            // Metrics obtained this way don't have anti-aliasing set. So,
228130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            // we create a new FontRenderContext with anti-aliasing set.
229f31c1e98d4fda5a1296a4dde4bc1ff6f73c5db64Diego Perez            AffineTransform transform = font.getTransform();
230f31c1e98d4fda5a1296a4dde4bc1ff6f73c5db64Diego Perez            if (mPaint.isAntiAliased() &&
231f31c1e98d4fda5a1296a4dde4bc1ff6f73c5db64Diego Perez                    // Workaround for http://b.android.com/211659
232f31c1e98d4fda5a1296a4dde4bc1ff6f73c5db64Diego Perez                    (transform.getScaleX() <= 9.9 ||
233f31c1e98d4fda5a1296a4dde4bc1ff6f73c5db64Diego Perez                    !"JetBrains s.r.o".equals(JAVA_VENDOR))) {
234f31c1e98d4fda5a1296a4dde4bc1ff6f73c5db64Diego Perez                frc = new FontRenderContext(transform, true, frc.usesFractionalMetrics());
235f31c1e98d4fda5a1296a4dde4bc1ff6f73c5db64Diego Perez            }
236130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        }
237130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
2380d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        int ng = gv.getNumGlyphs();
2390d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        int[] ci = gv.getGlyphCharIndices(0, ng, null);
24045dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        if (advances != null) {
24145dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            for (int i = 0; i < ng; i++) {
2420d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                int adv_idx = advancesIndex + ci[i];
24345dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta                advances[adv_idx] += gv.getGlyphMetrics(i).getAdvanceX();
2440d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            }
2450d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
24645dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        if (draw && mGraphics != null) {
24745dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            mGraphics.drawGlyphVector(gv, mBounds.right, mBaseline);
2485ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        }
24945dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta
25045dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        // Update the bounds.
25145dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        Rectangle2D awtBounds = gv.getLogicalBounds();
25245dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        RectF bounds = awtRectToAndroidRect(awtBounds, mBounds.right, mBaseline);
25345dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        // If the width of the bounds is zero, no text had been drawn earlier. Hence, use the
25445dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        // coordinates from the bounds as an offset.
25545dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta        if (Math.abs(mBounds.right - mBounds.left) == 0) {
25645dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            mBounds = bounds;
2575ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        } else {
25845dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta            mBounds.union(bounds);
2590d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
2605ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta    }
2615ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta
26245dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta    // --- Static helper methods ---
26345dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta
26445dbfcc781a3926d22571b6ccfa3f27ec896f119Deepanshu Gupta    private static RectF awtRectToAndroidRect(Rectangle2D awtRec, float offsetX, float offsetY) {
2655ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        float left = (float) awtRec.getX();
2665ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        float top = (float) awtRec.getY();
2675ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        float right = (float) (left + awtRec.getWidth());
2685ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        float bottom = (float) (top + awtRec.getHeight());
2695ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        RectF androidRect = new RectF(left, top, right, bottom);
2705ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        androidRect.offset(offsetX, offsetY);
2715ad7c183f39df43562c69aba21ea422ad69bdae0Deepanshu Gupta        return androidRect;
2720d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
2730d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
2740d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    /* package */  static List<ScriptRun> getScriptRuns(char[] text, int start, int limit,
275130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            boolean isRtl, List<Font> fonts) {
2760d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>();
2770d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
2780d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        int count = limit - start;
2790d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        UScriptRun uScriptRun = new UScriptRun(text, start, count);
2800d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        while (uScriptRun.next()) {
2810d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            int scriptStart = uScriptRun.getScriptStart();
2820d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            int scriptLimit = uScriptRun.getScriptLimit();
2830d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            ScriptRun run = new ScriptRun(scriptStart, scriptLimit, isRtl);
2840d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            run.scriptCode = uScriptRun.getScriptCode();
2850d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            setScriptFont(text, run, fonts);
2860d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            scriptRuns.add(run);
2870d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
2880d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
2890d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        return scriptRuns;
2900d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
2910d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta
2920d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    // TODO: Replace this method with one which returns the font based on the scriptCode.
2930d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    private static void setScriptFont(char[] text, ScriptRun run,
294130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            List<Font> fonts) {
295130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta        for (Font font : fonts) {
296b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta            if (font == null) {
297b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                logFontWarning();
298b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta                continue;
299b0d767dcf17dd393646b8fc3e8c9f46dfe12e230Deepanshu Gupta            }
300130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta            if (font.canDisplayUpTo(text, run.start, run.limit) == -1) {
301130d2353edda445b8e36a6b5e4b176fd748035b0Deepanshu Gupta                run.font = font;
3020d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta                return;
3030d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta            }
3040d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        }
3050d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta        run.font = fonts.get(0);
3060d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta    }
30784d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta
30884d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    private static int getIcuFlags(int bidiFlag) {
30984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        switch (bidiFlag) {
31084d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            case Paint.BIDI_LTR:
31184d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            case Paint.BIDI_FORCE_LTR:
31284d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                return Bidi.DIRECTION_LEFT_TO_RIGHT;
31384d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            case Paint.BIDI_RTL:
31484d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            case Paint.BIDI_FORCE_RTL:
31584d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                return Bidi.DIRECTION_RIGHT_TO_LEFT;
31684d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            case Paint.BIDI_DEFAULT_LTR:
31784d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
31884d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            case Paint.BIDI_DEFAULT_RTL:
31984d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                return Bidi.DIRECTION_DEFAULT_RIGHT_TO_LEFT;
32084d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta            default:
32184d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                assert false;
32284d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta                return Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
32384d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta        }
32484d1d431cfe3e66029380fa038f8816b06da120aDeepanshu Gupta    }
3250d9c922c9614147d1277cb36cfb7296774415d15Deepanshu Gupta}
326