Loading data/fonts/fonts.xml +6 −1 Original line number Diff line number Diff line Loading @@ -13,6 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. --> <!-- This is only used by the layoutlib to display layouts in ADT. --> <fonts> <font ttf="DroidSans"> <name>sans-serif</name> Loading @@ -39,5 +43,6 @@ <name>courier new</name> <name>monaco</name> </font> <font ttf="DroidSansFallback" /> <fallback ttf="DroidSansFallback" /> <fallback ttf="DroidSansJapanese" /> </fonts> No newline at end of file tools/layoutlib/bridge/src/android/graphics/Canvas.java +84 −6 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.graphics.RectF; import android.graphics.Region; import android.graphics.Xfermode; import android.graphics.Paint.Align; import android.graphics.Paint.FontInfo; import android.graphics.Paint.Style; import android.graphics.Region.Op; Loading @@ -37,6 +38,7 @@ import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.util.List; import java.util.Stack; import javax.microedition.khronos.opengles.GL; Loading Loading @@ -620,19 +622,21 @@ public class Canvas extends _Original_Canvas { */ @Override public void drawText(char[] text, int index, int count, float x, float y, Paint paint) { // WARNING: the logic in this method is similar to Paint.measureText. // Any change to this method should be reflected in Paint.measureText Graphics2D g = getGraphics2d(); g = (Graphics2D)g.create(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setFont(paint.getFont()); // set the color. because this only handles RGB we have to handle the alpha separately // set the color. because this only handles RGB, the alpha channel is handled // as a composite. g.setColor(new Color(paint.getColor())); int alpha = paint.getAlpha(); float falpha = alpha / 255.f; g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha)); // Paint.TextAlign indicates how the text is positioned relative to X. // LEFT is the default and there's nothing to do. if (paint.getTextAlign() != Align.LEFT) { Loading @@ -644,10 +648,84 @@ public class Canvas extends _Original_Canvas { } } g.drawChars(text, index, count, (int)x, (int)y); List<FontInfo> fonts = paint.getFonts(); try { if (fonts.size() > 0) { FontInfo mainFont = fonts.get(0); int i = index; int lastIndex = index + count; while (i < lastIndex) { // always start with the main font. int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); if (upTo == -1) { // draw all the rest and exit. g.setFont(mainFont.mFont); g.drawChars(text, i, lastIndex - i, (int)x, (int)y); return; } else if (upTo > 0) { // draw what's possible g.setFont(mainFont.mFont); g.drawChars(text, i, upTo - i, (int)x, (int)y); // compute the width that was drawn to increase x x += mainFont.mMetrics.charsWidth(text, i, upTo - i); // move index to the first non displayed char. i = upTo; // don't call continue at this point. Since it is certain the main font // cannot display the font a index upTo (now ==i), we move on to the // fallback fonts directly. } // no char supported, attempt to read the next char(s) with the // fallback font. In this case we only test the first character // and then go back to test with the main font. // Special test for 2-char characters. boolean foundFont = false; for (int f = 1 ; f < fonts.size() ; f++) { FontInfo fontInfo = fonts.get(f); // need to check that the font can display the character. We test // differently if the char is a high surrogate. int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); if (upTo == -1) { // draw that char g.setFont(fontInfo.mFont); g.drawChars(text, i, charCount, (int)x, (int)y); // update x x += fontInfo.mMetrics.charsWidth(text, i, charCount); // update the index in the text, and move on i += charCount; foundFont = true; break; } } // in case no font can display the char, display it with the main font. // (it'll put a square probably) if (foundFont == false) { int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; g.setFont(mainFont.mFont); g.drawChars(text, i, charCount, (int)x, (int)y); // measure it to advance x x += mainFont.mMetrics.charsWidth(text, i, charCount); // and move to the next chars. i += charCount; } } } } finally { g.dispose(); } } /* (non-Javadoc) * @see android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint) Loading tools/layoutlib/bridge/src/android/graphics/Paint.java +132 −41 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * A paint implementation overridden by the LayoutLib bridge. Loading @@ -44,10 +47,17 @@ public class Paint extends _Original_Paint { private Join mJoin = Join.MITER; private int mFlags = 0; private Font mFont; /** * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}. */ public static final class FontInfo { Font mFont; java.awt.FontMetrics mMetrics; } private List<FontInfo> mFonts; private final FontRenderContext mFontContext = new FontRenderContext( new AffineTransform(), true, true); private java.awt.FontMetrics mMetrics; @SuppressWarnings("hiding") public static final int ANTI_ALIAS_FLAG = _Original_Paint.ANTI_ALIAS_FLAG; Loading Loading @@ -201,10 +211,11 @@ public class Paint extends _Original_Paint { } /** * Returns the {@link Font} object. * Returns the list of {@link Font} objects. The first item is the main font, the rest * are fall backs for characters not present in the main font. */ public Font getFont() { return mFont; public List<FontInfo> getFonts() { return mFonts; } private void initFont() { Loading @@ -215,17 +226,29 @@ public class Paint extends _Original_Paint { /** * Update the {@link Font} object from the typeface, text size and scaling */ @SuppressWarnings("deprecation") private void updateFontObject() { if (mTypeface != null) { // get the typeface font object, and get our font object from it, based on the current size mFont = mTypeface.getFont().deriveFont(mTextSize); // Get the fonts from the TypeFace object. List<Font> fonts = mTypeface.getFonts(); // create new font objects as well as FontMetrics, based on the current text size // and skew info. ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size()); for (Font font : fonts) { FontInfo info = new FontInfo(); info.mFont = font.deriveFont(mTextSize); if (mScaleX != 1.0 || mSkewX != 0) { // TODO: support skew mFont = mFont.deriveFont(new AffineTransform( info.mFont = info.mFont.deriveFont(new AffineTransform( mScaleX, mSkewX, 0, 0, 1, 0)); } info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(mFont); infoList.add(info); } mFonts = Collections.unmodifiableList(infoList); } } Loading Loading @@ -310,34 +333,36 @@ public class Paint extends _Original_Paint { * @return the font's recommended interline spacing. */ public float getFontMetrics(FontMetrics metrics) { if (mMetrics != null) { if (mFonts.size() > 0) { java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; if (metrics != null) { // ascent stuff should be negatif, but awt returns them as positive. metrics.top = - mMetrics.getMaxAscent(); metrics.ascent = - mMetrics.getAscent(); metrics.descent = mMetrics.getDescent(); metrics.bottom = mMetrics.getMaxDescent(); metrics.leading = mMetrics.getLeading(); // Android expects negative ascent so we invert the value from Java. metrics.top = - javaMetrics.getMaxAscent(); metrics.ascent = - javaMetrics.getAscent(); metrics.descent = javaMetrics.getDescent(); metrics.bottom = javaMetrics.getMaxDescent(); metrics.leading = javaMetrics.getLeading(); } return mMetrics.getHeight(); return javaMetrics.getHeight(); } return 0; } public int getFontMetricsInt(FontMetricsInt metrics) { if (mMetrics != null) { if (mFonts.size() > 0) { java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; if (metrics != null) { // ascent stuff should be negatif, but awt returns them as positive. metrics.top = - mMetrics.getMaxAscent(); metrics.ascent = - mMetrics.getAscent(); metrics.descent = mMetrics.getDescent(); metrics.bottom = mMetrics.getMaxDescent(); metrics.leading = mMetrics.getLeading(); // Android expects negative ascent so we invert the value from Java. metrics.top = - javaMetrics.getMaxAscent(); metrics.ascent = - javaMetrics.getAscent(); metrics.descent = javaMetrics.getDescent(); metrics.bottom = javaMetrics.getMaxDescent(); metrics.leading = javaMetrics.getLeading(); } return mMetrics.getHeight(); return javaMetrics.getHeight(); } return 0; Loading Loading @@ -716,9 +741,10 @@ public class Paint extends _Original_Paint { */ @Override public float ascent() { if (mMetrics != null) { // ascent stuff should be negatif, but awt returns them as positive. return - mMetrics.getAscent(); if (mFonts.size() > 0) { java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; // Android expects negative ascent so we invert the value from Java. return - javaMetrics.getAscent(); } return 0; Loading @@ -733,8 +759,9 @@ public class Paint extends _Original_Paint { */ @Override public float descent() { if (mMetrics != null) { return mMetrics.getDescent(); if (mFonts.size() > 0) { java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; return javaMetrics.getDescent(); } return 0; Loading @@ -750,10 +777,55 @@ public class Paint extends _Original_Paint { */ @Override public float measureText(char[] text, int index, int count) { if (mFont != null && text != null && text.length > 0) { Rectangle2D bounds = mFont.getStringBounds(text, index, index + count, mFontContext); return (float)bounds.getWidth(); // WARNING: the logic in this method is similar to Canvas.drawText. // Any change to this method should be reflected in Canvas.drawText if (mFonts.size() > 0) { FontInfo mainFont = mFonts.get(0); int i = index; int lastIndex = index + count; float total = 0f; while (i < lastIndex) { // always start with the main font. int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); if (upTo == -1) { // shortcut to exit return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i); } else if (upTo > 0) { total += mainFont.mMetrics.charsWidth(text, i, upTo - i); i = upTo; // don't call continue at this point. Since it is certain the main font // cannot display the font a index upTo (now ==i), we move on to the // fallback fonts directly. } // no char supported, attempt to read the next char(s) with the // fallback font. In this case we only test the first character // and then go back to test with the main font. // Special test for 2-char characters. boolean foundFont = false; for (int f = 1 ; f < mFonts.size() ; f++) { FontInfo fontInfo = mFonts.get(f); // need to check that the font can display the character. We test // differently if the char is a high surrogate. int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); if (upTo == -1) { total += fontInfo.mMetrics.charsWidth(text, i, charCount); i += charCount; foundFont = true; break; } } // in case no font can display the char, measure it with the main font. if (foundFont == false) { int size = Character.isHighSurrogate(text[i]) ? 2 : 1; total += mainFont.mMetrics.charsWidth(text, i, size); i += size; } } } return 0; Loading Loading @@ -919,14 +991,30 @@ public class Paint extends _Original_Paint { @Override public int getTextWidths(char[] text, int index, int count, float[] widths) { if (mMetrics != null) { if (mFonts.size() > 0) { if ((index | count) < 0 || index + count > text.length || count > widths.length) { throw new ArrayIndexOutOfBoundsException(); } // FIXME: handle multi-char characters. // Need to figure out if the lengths of the width array takes into account // multi-char characters. for (int i = 0; i < count; i++) { widths[i] = mMetrics.charWidth(text[i + index]); char c = text[i + index]; boolean found = false; for (FontInfo info : mFonts) { if (info.mFont.canDisplay(c)) { widths[i] = info.mMetrics.charWidth(c); found = true; break; } } if (found == false) { // we stop there. return i; } } return count; Loading Loading @@ -1070,7 +1158,8 @@ public class Paint extends _Original_Paint { */ @Override public void getTextBounds(char[] text, int index, int count, Rect bounds) { if (mFont != null) { // FIXME if (mFonts.size() > 0) { if ((index | count) < 0 || index + count > text.length) { throw new ArrayIndexOutOfBoundsException(); } Loading @@ -1078,7 +1167,9 @@ public class Paint extends _Original_Paint { throw new NullPointerException("need bounds Rect"); } Rectangle2D rect = mFont.getStringBounds(text, index, index + count, mFontContext); FontInfo mainInfo = mFonts.get(0); Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, mFontContext); bounds.set(0, 0, (int)rect.getWidth(), (int)rect.getHeight()); } } Loading tools/layoutlib/bridge/src/android/graphics/Typeface.java +29 −19 Original line number Diff line number Diff line Loading @@ -21,6 +21,9 @@ import com.android.layoutlib.bridge.FontLoader; import android.content.res.AssetManager; import java.awt.Font; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Re-implementation of Typeface over java.awt Loading Loading @@ -48,7 +51,7 @@ public class Typeface { private static FontLoader mFontLoader; private final int mStyle; private final Font mFont; private final List<Font> mFonts; private final String mFamily; // Style Loading @@ -58,10 +61,11 @@ public class Typeface { public static final int BOLD_ITALIC = _Original_Typeface.BOLD_ITALIC; /** * Returns the underlying {@link Font} object. * Returns the underlying {@link Font} objects. The first item in the list is the real * font. Any other items are fallback fonts for characters not found in the first one. */ public Font getFont() { return mFont; public List<Font> getFonts() { return mFonts; } /** Returns the typeface's intrinsic style attributes */ Loading Loading @@ -94,7 +98,10 @@ public class Typeface { styleBuffer[0] = style; Font font = mFontLoader.getFont(familyName, styleBuffer); if (font != null) { return new Typeface(familyName, styleBuffer[0], font); ArrayList<Font> list = new ArrayList<Font>(); list.add(font); list.addAll(mFontLoader.getFallBackFonts()); return new Typeface(familyName, styleBuffer[0], list); } return null; Loading @@ -115,7 +122,10 @@ public class Typeface { styleBuffer[0] = style; Font font = mFontLoader.getFont(family.mFamily, styleBuffer); if (font != null) { return new Typeface(family.mFamily, styleBuffer[0], font); ArrayList<Font> list = new ArrayList<Font>(); list.add(font); list.addAll(mFontLoader.getFallBackFonts()); return new Typeface(family.mFamily, styleBuffer[0], list); } return null; Loading @@ -142,9 +152,9 @@ public class Typeface { } // don't allow clients to call this directly private Typeface(String family, int style, Font f) { private Typeface(String family, int style, List<Font> fonts) { mFamily = family; mFont = f; mFonts = Collections.unmodifiableList(fonts); mStyle = style; } Loading tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java +88 −54 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; Loading @@ -51,11 +52,10 @@ public final class FontLoader { private static final String NODE_FONTS = "fonts"; private static final String NODE_FONT = "font"; private static final String NODE_NAME = "name"; private static final String NODE_FALLBACK = "fallback"; private static final String ATTR_TTF = "ttf"; private static final String[] NODE_LEVEL = { NODE_FONTS, NODE_FONT, NODE_NAME }; private static final String FONT_EXT = ".ttf"; private static final String[] FONT_STYLE_DEFAULT = { "", "-Regular" }; Loading @@ -75,6 +75,8 @@ public final class FontLoader { private final Map<String, Map<Integer, Font>> mTtfToFontMap = new HashMap<String, Map<Integer, Font>>(); private List<Font> mFallBackFonts = null; public static FontLoader create(String fontOsLocation) { try { SAXParserFactory parserFactory = SAXParserFactory.newInstance(); Loading @@ -101,12 +103,35 @@ public final class FontLoader { return null; } private FontLoader(List<FontInfo> fontList) { private FontLoader(List<FontInfo> fontList, List<String> fallBackList) { for (FontInfo info : fontList) { for (String family : info.families) { mFamilyToTtf.put(family, info.ttf); } } ArrayList<Font> list = new ArrayList<Font>(); for (String path : fallBackList) { File f = new File(path + FONT_EXT); if (f.isFile()) { try { Font font = Font.createFont(Font.TRUETYPE_FONT, f); if (font != null) { list.add(font); } } catch (FontFormatException e) { // skip this font name } catch (IOException e) { // skip this font name } } } mFallBackFonts = Collections.unmodifiableList(list); } public List<Font> getFallBackFonts() { return mFallBackFonts; } public synchronized Font getFont(String family, int[] style) { Loading Loading @@ -209,10 +234,10 @@ public final class FontLoader { private final static class FontDefinitionParser extends DefaultHandler { private final String mOsFontsLocation; private int mDepth = 0; private FontInfo mFontInfo = null; private final StringBuilder mBuilder = new StringBuilder(); private final List<FontInfo> mFontList = new ArrayList<FontInfo>(); private List<FontInfo> mFontList; private List<String> mFallBackList; private FontDefinitionParser(String osFontsLocation) { super(); Loading @@ -220,7 +245,7 @@ public final class FontLoader { } FontLoader getFontLoader() { return new FontLoader(mFontList); return new FontLoader(mFontList, mFallBackList); } /* (non-Javadoc) Loading @@ -229,10 +254,11 @@ public final class FontLoader { @Override public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { if (localName.equals(NODE_LEVEL[mDepth])) { mDepth++; if (mDepth == 2) { // font level. if (NODE_FONTS.equals(localName)) { mFontList = new ArrayList<FontInfo>(); mFallBackList = new ArrayList<String>(); } else if (NODE_FONT.equals(localName)) { if (mFontList != null) { String ttf = attributes.getValue(ATTR_TTF); if (ttf != null) { mFontInfo = new FontInfo(); Loading @@ -240,39 +266,47 @@ public final class FontLoader { mFontList.add(mFontInfo); } } } else if (NODE_NAME.equals(localName)) { // do nothing, we'll handle the name in the endElement } else if (NODE_FALLBACK.equals(localName)) { if (mFallBackList != null) { String ttf = attributes.getValue(ATTR_TTF); if (ttf != null) { mFallBackList.add(mOsFontsLocation + ttf); } } } mBuilder.setLength(0); super.startElement(uri, localName, name, attributes); } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int) */ @SuppressWarnings("unused") @Override public void characters(char[] ch, int start, int length) throws SAXException { if (mFontInfo != null) { mBuilder.append(ch, start, length); } } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String) */ @SuppressWarnings("unused") @Override public void endElement(String uri, String localName, String name) throws SAXException { if (localName.equals(NODE_LEVEL[mDepth-1])) { mDepth--; if (mDepth == 2) { // end of a <name> node if (NODE_FONTS.equals(localName)) { // top level, do nothing } else if (NODE_FONT.equals(localName)) { mFontInfo = null; } else if (NODE_NAME.equals(localName)) { // handle a new name for an existing Font Info if (mFontInfo != null) { String family = trimXmlWhitespaces(mBuilder.toString()); mFontInfo.families.add(family); mBuilder.setLength(0); } } else if (mDepth == 1) { // end of a <font> node mFontInfo = null; } } else if (NODE_FALLBACK.equals(localName)) { // nothing to do here. } } Loading Loading
data/fonts/fonts.xml +6 −1 Original line number Diff line number Diff line Loading @@ -13,6 +13,10 @@ See the License for the specific language governing permissions and limitations under the License. --> <!-- This is only used by the layoutlib to display layouts in ADT. --> <fonts> <font ttf="DroidSans"> <name>sans-serif</name> Loading @@ -39,5 +43,6 @@ <name>courier new</name> <name>monaco</name> </font> <font ttf="DroidSansFallback" /> <fallback ttf="DroidSansFallback" /> <fallback ttf="DroidSansJapanese" /> </fonts> No newline at end of file
tools/layoutlib/bridge/src/android/graphics/Canvas.java +84 −6 Original line number Diff line number Diff line Loading @@ -26,6 +26,7 @@ import android.graphics.RectF; import android.graphics.Region; import android.graphics.Xfermode; import android.graphics.Paint.Align; import android.graphics.Paint.FontInfo; import android.graphics.Paint.Style; import android.graphics.Region.Op; Loading @@ -37,6 +38,7 @@ import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.util.List; import java.util.Stack; import javax.microedition.khronos.opengles.GL; Loading Loading @@ -620,19 +622,21 @@ public class Canvas extends _Original_Canvas { */ @Override public void drawText(char[] text, int index, int count, float x, float y, Paint paint) { // WARNING: the logic in this method is similar to Paint.measureText. // Any change to this method should be reflected in Paint.measureText Graphics2D g = getGraphics2d(); g = (Graphics2D)g.create(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g.setFont(paint.getFont()); // set the color. because this only handles RGB we have to handle the alpha separately // set the color. because this only handles RGB, the alpha channel is handled // as a composite. g.setColor(new Color(paint.getColor())); int alpha = paint.getAlpha(); float falpha = alpha / 255.f; g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha)); // Paint.TextAlign indicates how the text is positioned relative to X. // LEFT is the default and there's nothing to do. if (paint.getTextAlign() != Align.LEFT) { Loading @@ -644,10 +648,84 @@ public class Canvas extends _Original_Canvas { } } g.drawChars(text, index, count, (int)x, (int)y); List<FontInfo> fonts = paint.getFonts(); try { if (fonts.size() > 0) { FontInfo mainFont = fonts.get(0); int i = index; int lastIndex = index + count; while (i < lastIndex) { // always start with the main font. int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); if (upTo == -1) { // draw all the rest and exit. g.setFont(mainFont.mFont); g.drawChars(text, i, lastIndex - i, (int)x, (int)y); return; } else if (upTo > 0) { // draw what's possible g.setFont(mainFont.mFont); g.drawChars(text, i, upTo - i, (int)x, (int)y); // compute the width that was drawn to increase x x += mainFont.mMetrics.charsWidth(text, i, upTo - i); // move index to the first non displayed char. i = upTo; // don't call continue at this point. Since it is certain the main font // cannot display the font a index upTo (now ==i), we move on to the // fallback fonts directly. } // no char supported, attempt to read the next char(s) with the // fallback font. In this case we only test the first character // and then go back to test with the main font. // Special test for 2-char characters. boolean foundFont = false; for (int f = 1 ; f < fonts.size() ; f++) { FontInfo fontInfo = fonts.get(f); // need to check that the font can display the character. We test // differently if the char is a high surrogate. int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); if (upTo == -1) { // draw that char g.setFont(fontInfo.mFont); g.drawChars(text, i, charCount, (int)x, (int)y); // update x x += fontInfo.mMetrics.charsWidth(text, i, charCount); // update the index in the text, and move on i += charCount; foundFont = true; break; } } // in case no font can display the char, display it with the main font. // (it'll put a square probably) if (foundFont == false) { int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; g.setFont(mainFont.mFont); g.drawChars(text, i, charCount, (int)x, (int)y); // measure it to advance x x += mainFont.mMetrics.charsWidth(text, i, charCount); // and move to the next chars. i += charCount; } } } } finally { g.dispose(); } } /* (non-Javadoc) * @see android.graphics.Canvas#drawText(java.lang.CharSequence, int, int, float, float, android.graphics.Paint) Loading
tools/layoutlib/bridge/src/android/graphics/Paint.java +132 −41 Original line number Diff line number Diff line Loading @@ -26,6 +26,9 @@ import java.awt.Toolkit; import java.awt.font.FontRenderContext; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * A paint implementation overridden by the LayoutLib bridge. Loading @@ -44,10 +47,17 @@ public class Paint extends _Original_Paint { private Join mJoin = Join.MITER; private int mFlags = 0; private Font mFont; /** * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}. */ public static final class FontInfo { Font mFont; java.awt.FontMetrics mMetrics; } private List<FontInfo> mFonts; private final FontRenderContext mFontContext = new FontRenderContext( new AffineTransform(), true, true); private java.awt.FontMetrics mMetrics; @SuppressWarnings("hiding") public static final int ANTI_ALIAS_FLAG = _Original_Paint.ANTI_ALIAS_FLAG; Loading Loading @@ -201,10 +211,11 @@ public class Paint extends _Original_Paint { } /** * Returns the {@link Font} object. * Returns the list of {@link Font} objects. The first item is the main font, the rest * are fall backs for characters not present in the main font. */ public Font getFont() { return mFont; public List<FontInfo> getFonts() { return mFonts; } private void initFont() { Loading @@ -215,17 +226,29 @@ public class Paint extends _Original_Paint { /** * Update the {@link Font} object from the typeface, text size and scaling */ @SuppressWarnings("deprecation") private void updateFontObject() { if (mTypeface != null) { // get the typeface font object, and get our font object from it, based on the current size mFont = mTypeface.getFont().deriveFont(mTextSize); // Get the fonts from the TypeFace object. List<Font> fonts = mTypeface.getFonts(); // create new font objects as well as FontMetrics, based on the current text size // and skew info. ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size()); for (Font font : fonts) { FontInfo info = new FontInfo(); info.mFont = font.deriveFont(mTextSize); if (mScaleX != 1.0 || mSkewX != 0) { // TODO: support skew mFont = mFont.deriveFont(new AffineTransform( info.mFont = info.mFont.deriveFont(new AffineTransform( mScaleX, mSkewX, 0, 0, 1, 0)); } info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont); mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(mFont); infoList.add(info); } mFonts = Collections.unmodifiableList(infoList); } } Loading Loading @@ -310,34 +333,36 @@ public class Paint extends _Original_Paint { * @return the font's recommended interline spacing. */ public float getFontMetrics(FontMetrics metrics) { if (mMetrics != null) { if (mFonts.size() > 0) { java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; if (metrics != null) { // ascent stuff should be negatif, but awt returns them as positive. metrics.top = - mMetrics.getMaxAscent(); metrics.ascent = - mMetrics.getAscent(); metrics.descent = mMetrics.getDescent(); metrics.bottom = mMetrics.getMaxDescent(); metrics.leading = mMetrics.getLeading(); // Android expects negative ascent so we invert the value from Java. metrics.top = - javaMetrics.getMaxAscent(); metrics.ascent = - javaMetrics.getAscent(); metrics.descent = javaMetrics.getDescent(); metrics.bottom = javaMetrics.getMaxDescent(); metrics.leading = javaMetrics.getLeading(); } return mMetrics.getHeight(); return javaMetrics.getHeight(); } return 0; } public int getFontMetricsInt(FontMetricsInt metrics) { if (mMetrics != null) { if (mFonts.size() > 0) { java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; if (metrics != null) { // ascent stuff should be negatif, but awt returns them as positive. metrics.top = - mMetrics.getMaxAscent(); metrics.ascent = - mMetrics.getAscent(); metrics.descent = mMetrics.getDescent(); metrics.bottom = mMetrics.getMaxDescent(); metrics.leading = mMetrics.getLeading(); // Android expects negative ascent so we invert the value from Java. metrics.top = - javaMetrics.getMaxAscent(); metrics.ascent = - javaMetrics.getAscent(); metrics.descent = javaMetrics.getDescent(); metrics.bottom = javaMetrics.getMaxDescent(); metrics.leading = javaMetrics.getLeading(); } return mMetrics.getHeight(); return javaMetrics.getHeight(); } return 0; Loading Loading @@ -716,9 +741,10 @@ public class Paint extends _Original_Paint { */ @Override public float ascent() { if (mMetrics != null) { // ascent stuff should be negatif, but awt returns them as positive. return - mMetrics.getAscent(); if (mFonts.size() > 0) { java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; // Android expects negative ascent so we invert the value from Java. return - javaMetrics.getAscent(); } return 0; Loading @@ -733,8 +759,9 @@ public class Paint extends _Original_Paint { */ @Override public float descent() { if (mMetrics != null) { return mMetrics.getDescent(); if (mFonts.size() > 0) { java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics; return javaMetrics.getDescent(); } return 0; Loading @@ -750,10 +777,55 @@ public class Paint extends _Original_Paint { */ @Override public float measureText(char[] text, int index, int count) { if (mFont != null && text != null && text.length > 0) { Rectangle2D bounds = mFont.getStringBounds(text, index, index + count, mFontContext); return (float)bounds.getWidth(); // WARNING: the logic in this method is similar to Canvas.drawText. // Any change to this method should be reflected in Canvas.drawText if (mFonts.size() > 0) { FontInfo mainFont = mFonts.get(0); int i = index; int lastIndex = index + count; float total = 0f; while (i < lastIndex) { // always start with the main font. int upTo = mainFont.mFont.canDisplayUpTo(text, i, lastIndex); if (upTo == -1) { // shortcut to exit return total + mainFont.mMetrics.charsWidth(text, i, lastIndex - i); } else if (upTo > 0) { total += mainFont.mMetrics.charsWidth(text, i, upTo - i); i = upTo; // don't call continue at this point. Since it is certain the main font // cannot display the font a index upTo (now ==i), we move on to the // fallback fonts directly. } // no char supported, attempt to read the next char(s) with the // fallback font. In this case we only test the first character // and then go back to test with the main font. // Special test for 2-char characters. boolean foundFont = false; for (int f = 1 ; f < mFonts.size() ; f++) { FontInfo fontInfo = mFonts.get(f); // need to check that the font can display the character. We test // differently if the char is a high surrogate. int charCount = Character.isHighSurrogate(text[i]) ? 2 : 1; upTo = fontInfo.mFont.canDisplayUpTo(text, i, i + charCount); if (upTo == -1) { total += fontInfo.mMetrics.charsWidth(text, i, charCount); i += charCount; foundFont = true; break; } } // in case no font can display the char, measure it with the main font. if (foundFont == false) { int size = Character.isHighSurrogate(text[i]) ? 2 : 1; total += mainFont.mMetrics.charsWidth(text, i, size); i += size; } } } return 0; Loading Loading @@ -919,14 +991,30 @@ public class Paint extends _Original_Paint { @Override public int getTextWidths(char[] text, int index, int count, float[] widths) { if (mMetrics != null) { if (mFonts.size() > 0) { if ((index | count) < 0 || index + count > text.length || count > widths.length) { throw new ArrayIndexOutOfBoundsException(); } // FIXME: handle multi-char characters. // Need to figure out if the lengths of the width array takes into account // multi-char characters. for (int i = 0; i < count; i++) { widths[i] = mMetrics.charWidth(text[i + index]); char c = text[i + index]; boolean found = false; for (FontInfo info : mFonts) { if (info.mFont.canDisplay(c)) { widths[i] = info.mMetrics.charWidth(c); found = true; break; } } if (found == false) { // we stop there. return i; } } return count; Loading Loading @@ -1070,7 +1158,8 @@ public class Paint extends _Original_Paint { */ @Override public void getTextBounds(char[] text, int index, int count, Rect bounds) { if (mFont != null) { // FIXME if (mFonts.size() > 0) { if ((index | count) < 0 || index + count > text.length) { throw new ArrayIndexOutOfBoundsException(); } Loading @@ -1078,7 +1167,9 @@ public class Paint extends _Original_Paint { throw new NullPointerException("need bounds Rect"); } Rectangle2D rect = mFont.getStringBounds(text, index, index + count, mFontContext); FontInfo mainInfo = mFonts.get(0); Rectangle2D rect = mainInfo.mFont.getStringBounds(text, index, index + count, mFontContext); bounds.set(0, 0, (int)rect.getWidth(), (int)rect.getHeight()); } } Loading
tools/layoutlib/bridge/src/android/graphics/Typeface.java +29 −19 Original line number Diff line number Diff line Loading @@ -21,6 +21,9 @@ import com.android.layoutlib.bridge.FontLoader; import android.content.res.AssetManager; import java.awt.Font; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Re-implementation of Typeface over java.awt Loading Loading @@ -48,7 +51,7 @@ public class Typeface { private static FontLoader mFontLoader; private final int mStyle; private final Font mFont; private final List<Font> mFonts; private final String mFamily; // Style Loading @@ -58,10 +61,11 @@ public class Typeface { public static final int BOLD_ITALIC = _Original_Typeface.BOLD_ITALIC; /** * Returns the underlying {@link Font} object. * Returns the underlying {@link Font} objects. The first item in the list is the real * font. Any other items are fallback fonts for characters not found in the first one. */ public Font getFont() { return mFont; public List<Font> getFonts() { return mFonts; } /** Returns the typeface's intrinsic style attributes */ Loading Loading @@ -94,7 +98,10 @@ public class Typeface { styleBuffer[0] = style; Font font = mFontLoader.getFont(familyName, styleBuffer); if (font != null) { return new Typeface(familyName, styleBuffer[0], font); ArrayList<Font> list = new ArrayList<Font>(); list.add(font); list.addAll(mFontLoader.getFallBackFonts()); return new Typeface(familyName, styleBuffer[0], list); } return null; Loading @@ -115,7 +122,10 @@ public class Typeface { styleBuffer[0] = style; Font font = mFontLoader.getFont(family.mFamily, styleBuffer); if (font != null) { return new Typeface(family.mFamily, styleBuffer[0], font); ArrayList<Font> list = new ArrayList<Font>(); list.add(font); list.addAll(mFontLoader.getFallBackFonts()); return new Typeface(family.mFamily, styleBuffer[0], list); } return null; Loading @@ -142,9 +152,9 @@ public class Typeface { } // don't allow clients to call this directly private Typeface(String family, int style, Font f) { private Typeface(String family, int style, List<Font> fonts) { mFamily = family; mFont = f; mFonts = Collections.unmodifiableList(fonts); mStyle = style; } Loading
tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java +88 −54 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; Loading @@ -51,11 +52,10 @@ public final class FontLoader { private static final String NODE_FONTS = "fonts"; private static final String NODE_FONT = "font"; private static final String NODE_NAME = "name"; private static final String NODE_FALLBACK = "fallback"; private static final String ATTR_TTF = "ttf"; private static final String[] NODE_LEVEL = { NODE_FONTS, NODE_FONT, NODE_NAME }; private static final String FONT_EXT = ".ttf"; private static final String[] FONT_STYLE_DEFAULT = { "", "-Regular" }; Loading @@ -75,6 +75,8 @@ public final class FontLoader { private final Map<String, Map<Integer, Font>> mTtfToFontMap = new HashMap<String, Map<Integer, Font>>(); private List<Font> mFallBackFonts = null; public static FontLoader create(String fontOsLocation) { try { SAXParserFactory parserFactory = SAXParserFactory.newInstance(); Loading @@ -101,12 +103,35 @@ public final class FontLoader { return null; } private FontLoader(List<FontInfo> fontList) { private FontLoader(List<FontInfo> fontList, List<String> fallBackList) { for (FontInfo info : fontList) { for (String family : info.families) { mFamilyToTtf.put(family, info.ttf); } } ArrayList<Font> list = new ArrayList<Font>(); for (String path : fallBackList) { File f = new File(path + FONT_EXT); if (f.isFile()) { try { Font font = Font.createFont(Font.TRUETYPE_FONT, f); if (font != null) { list.add(font); } } catch (FontFormatException e) { // skip this font name } catch (IOException e) { // skip this font name } } } mFallBackFonts = Collections.unmodifiableList(list); } public List<Font> getFallBackFonts() { return mFallBackFonts; } public synchronized Font getFont(String family, int[] style) { Loading Loading @@ -209,10 +234,10 @@ public final class FontLoader { private final static class FontDefinitionParser extends DefaultHandler { private final String mOsFontsLocation; private int mDepth = 0; private FontInfo mFontInfo = null; private final StringBuilder mBuilder = new StringBuilder(); private final List<FontInfo> mFontList = new ArrayList<FontInfo>(); private List<FontInfo> mFontList; private List<String> mFallBackList; private FontDefinitionParser(String osFontsLocation) { super(); Loading @@ -220,7 +245,7 @@ public final class FontLoader { } FontLoader getFontLoader() { return new FontLoader(mFontList); return new FontLoader(mFontList, mFallBackList); } /* (non-Javadoc) Loading @@ -229,10 +254,11 @@ public final class FontLoader { @Override public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException { if (localName.equals(NODE_LEVEL[mDepth])) { mDepth++; if (mDepth == 2) { // font level. if (NODE_FONTS.equals(localName)) { mFontList = new ArrayList<FontInfo>(); mFallBackList = new ArrayList<String>(); } else if (NODE_FONT.equals(localName)) { if (mFontList != null) { String ttf = attributes.getValue(ATTR_TTF); if (ttf != null) { mFontInfo = new FontInfo(); Loading @@ -240,39 +266,47 @@ public final class FontLoader { mFontList.add(mFontInfo); } } } else if (NODE_NAME.equals(localName)) { // do nothing, we'll handle the name in the endElement } else if (NODE_FALLBACK.equals(localName)) { if (mFallBackList != null) { String ttf = attributes.getValue(ATTR_TTF); if (ttf != null) { mFallBackList.add(mOsFontsLocation + ttf); } } } mBuilder.setLength(0); super.startElement(uri, localName, name, attributes); } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int) */ @SuppressWarnings("unused") @Override public void characters(char[] ch, int start, int length) throws SAXException { if (mFontInfo != null) { mBuilder.append(ch, start, length); } } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String) */ @SuppressWarnings("unused") @Override public void endElement(String uri, String localName, String name) throws SAXException { if (localName.equals(NODE_LEVEL[mDepth-1])) { mDepth--; if (mDepth == 2) { // end of a <name> node if (NODE_FONTS.equals(localName)) { // top level, do nothing } else if (NODE_FONT.equals(localName)) { mFontInfo = null; } else if (NODE_NAME.equals(localName)) { // handle a new name for an existing Font Info if (mFontInfo != null) { String family = trimXmlWhitespaces(mBuilder.toString()); mFontInfo.families.add(family); mBuilder.setLength(0); } } else if (mDepth == 1) { // end of a <font> node mFontInfo = null; } } else if (NODE_FALLBACK.equals(localName)) { // nothing to do here. } } Loading