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