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