summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXavier Ducrohet <xav@android.com>2009-11-08 15:15:01 -0800
committerXavier Ducrohet <xav@android.com>2009-11-10 11:47:08 -0800
commitb5046342562aff58af33ae25cfcd1cc3abe27fc8 (patch)
treed17a4eb458afc5299eaf203e0d7890efc963166b
parentc06c1d2c5743b3a7ba0f763873eea78aa4a6f6ed (diff)
downloadbase-donut-release.tar.gz
Support for fallback fonts in layoutlib. (do not merge)donut-release
BUG 2041229 This is integrated from Eclair.
-rw-r--r--data/fonts/fonts.xml7
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas.java90
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Paint.java185
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Typeface.java48
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java142
5 files changed, 345 insertions, 127 deletions
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index 2bce8469cfda..6bcaaaf9059a 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -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>
@@ -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
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
index 3fa1d1d6cf0c..4986c77b2795 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
@@ -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;
@@ -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;
@@ -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) {
@@ -644,9 +648,83 @@ public class Canvas extends _Original_Canvas {
}
}
- g.drawChars(text, index, count, (int)x, (int)y);
-
- g.dispose();
+ 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)
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java
index ade07d603402..4311c2b3262f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint.java
@@ -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.
@@ -39,11 +42,18 @@ public class Paint extends _Original_Paint {
private Align mAlign = Align.LEFT;
private Style mStyle = Style.FILL;
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;
@@ -192,10 +202,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() {
@@ -206,17 +217,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);
- if (mScaleX != 1.0 || mSkewX != 0) {
- // TODO: support skew
- mFont = mFont.deriveFont(new AffineTransform(
- mScaleX, mSkewX, 0, 0, 1, 0));
+ // 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
+ info.mFont = info.mFont.deriveFont(new AffineTransform(
+ mScaleX, mSkewX, 0, 0, 1, 0));
+ }
+ info.mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(info.mFont);
+
+ infoList.add(info);
}
-
- mMetrics = Toolkit.getDefaultToolkit().getFontMetrics(mFont);
+
+ mFonts = Collections.unmodifiableList(infoList);
}
}
@@ -256,34 +279,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;
@@ -521,9 +546,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;
@@ -538,8 +564,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;
@@ -555,10 +582,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;
@@ -702,14 +774,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;
@@ -849,15 +937,18 @@ 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();
}
if (bounds == null) {
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());
}
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface.java b/tools/layoutlib/bridge/src/android/graphics/Typeface.java
index e878b047841c..af3adb510d3b 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface.java
@@ -21,9 +21,12 @@ 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
+ * Re-implementation of Typeface over java.awt
*/
public class Typeface {
private static final String DEFAULT_FAMILY = "sans-serif";
@@ -46,11 +49,11 @@ public class Typeface {
private static Typeface[] sDefaults;
private static FontLoader mFontLoader;
-
+
private final int mStyle;
- private final Font mFont;
+ private final List<Font> mFonts;
private final String mFamily;
-
+
// Style
public static final int NORMAL = _Original_Typeface.NORMAL;
public static final int BOLD = _Original_Typeface.BOLD;
@@ -58,12 +61,13 @@ 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 */
public int getStyle() {
return mStyle;
@@ -94,9 +98,12 @@ 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;
}
@@ -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;
@@ -129,7 +139,7 @@ public class Typeface {
public static Typeface defaultFromStyle(int style) {
return sDefaults[style];
}
-
+
/**
* Create a new typeface from the specified font data.
* @param mgr The application's asset manager
@@ -140,17 +150,17 @@ public class Typeface {
return null;
//return new Typeface(nativeCreateFromAsset(mgr, path));
}
-
+
// 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;
}
-
+
public static void init(FontLoader fontLoader) {
mFontLoader = fontLoader;
-
+
DEFAULT = create(DEFAULT_FAMILY, NORMAL);
DEFAULT_BOLD = create(DEFAULT_FAMILY, BOLD);
SANS_SERIF = create("sans-serif", NORMAL);
@@ -162,14 +172,14 @@ public class Typeface {
create(DEFAULT_FAMILY, ITALIC),
create(DEFAULT_FAMILY, BOLD_ITALIC),
};
-
+
/*
DEFAULT = create((String)null, 0);
DEFAULT_BOLD = create((String)null, Typeface.BOLD);
SANS_SERIF = create("sans-serif", 0);
SERIF = create("serif", 0);
MONOSPACE = create("monospace", 0);
-
+
sDefaults = new Typeface[] {
DEFAULT,
DEFAULT_BOLD,
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java
index 1bdd1ccead0e..801503b89e0d 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java
@@ -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;
@@ -47,14 +48,13 @@ import javax.xml.parsers.SAXParserFactory;
*/
public final class FontLoader {
private static final String FONTS_DEFINITIONS = "fonts.xml";
-
+
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 ATTR_TTF = "ttf";
+ private static final String NODE_FALLBACK = "fallback";
- private static final String[] NODE_LEVEL = { NODE_FONTS, NODE_FONT, NODE_NAME };
+ private static final String ATTR_TTF = "ttf";
private static final String FONT_EXT = ".ttf";
@@ -62,7 +62,7 @@ public final class FontLoader {
private static final String[] FONT_STYLE_BOLD = { "-Bold" };
private static final String[] FONT_STYLE_ITALIC = { "-Italic" };
private static final String[] FONT_STYLE_BOLDITALIC = { "-BoldItalic" };
-
+
// list of font style, in the order matching the Typeface Font style
private static final String[][] FONT_STYLES = {
FONT_STYLE_DEFAULT,
@@ -70,23 +70,25 @@ public final class FontLoader {
FONT_STYLE_ITALIC,
FONT_STYLE_BOLDITALIC
};
-
+
private final Map<String, String> mFamilyToTtf = new HashMap<String, String>();
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();
parserFactory.setNamespaceAware(true);
-
+
SAXParser parser = parserFactory.newSAXParser();
File f = new File(fontOsLocation + File.separator + FONTS_DEFINITIONS);
-
+
FontDefinitionParser definitionParser = new FontDefinitionParser(
fontOsLocation + File.separator);
parser.parse(new FileInputStream(f), definitionParser);
-
+
return definitionParser.getFontLoader();
} catch (ParserConfigurationException e) {
// return null below
@@ -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) {
@@ -116,25 +141,25 @@ public final class FontLoader {
// get the ttf name from the family
String ttf = mFamilyToTtf.get(family);
-
+
if (ttf == null) {
return null;
}
-
+
// get the font from the ttf
Map<Integer, Font> styleMap = mTtfToFontMap.get(ttf);
-
+
if (styleMap == null) {
styleMap = new HashMap<Integer, Font>();
mTtfToFontMap.put(ttf, styleMap);
}
-
+
Font f = styleMap.get(style);
-
+
if (f != null) {
return f;
}
-
+
// if it doesn't exist, we create it, and we can't, we try with a simpler style
switch (style[0]) {
case Typeface.NORMAL:
@@ -178,7 +203,7 @@ public final class FontLoader {
private Font getFont(String ttf, String[] fontFileSuffix) {
for (String suffix : fontFileSuffix) {
String name = ttf + suffix + FONT_EXT;
-
+
File f = new File(name);
if (f.isFile()) {
try {
@@ -193,14 +218,14 @@ public final class FontLoader {
}
}
}
-
+
return null;
}
private final static class FontInfo {
String ttf;
final Set<String> families;
-
+
FontInfo() {
families = new HashSet<String>();
}
@@ -208,19 +233,19 @@ 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();
mOsFontsLocation = osFontsLocation;
}
-
+
FontLoader getFontLoader() {
- return new FontLoader(mFontList);
+ return new FontLoader(mFontList, mFallBackList);
}
/* (non-Javadoc)
@@ -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();
@@ -240,42 +266,50 @@ 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);
- }
+ 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 (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;
+ 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);
}
+ } else if (NODE_FALLBACK.equals(localName)) {
+ // nothing to do here.
}
}
-
+
private String trimXmlWhitespaces(String value) {
if (value == null) {
return null;
@@ -283,7 +317,7 @@ public final class FontLoader {
// look for carriage return and replace all whitespace around it by just 1 space.
int index;
-
+
while ((index = value.indexOf('\n')) != -1) {
// look for whitespace on each side
int left = index - 1;
@@ -294,7 +328,7 @@ public final class FontLoader {
break;
}
}
-
+
int right = index + 1;
int count = value.length();
while (right < count) {
@@ -304,7 +338,7 @@ public final class FontLoader {
break;
}
}
-
+
// remove all between left and right (non inclusive) and replace by a single space.
String leftString = null;
if (left >= 0) {
@@ -314,7 +348,7 @@ public final class FontLoader {
if (right < count) {
rightString = value.substring(right);
}
-
+
if (leftString != null) {
value = leftString;
if (rightString != null) {
@@ -324,24 +358,24 @@ public final class FontLoader {
value = rightString != null ? rightString : "";
}
}
-
+
// now we un-escape the string
int length = value.length();
char[] buffer = value.toCharArray();
-
+
for (int i = 0 ; i < length ; i++) {
if (buffer[i] == '\\') {
if (buffer[i+1] == 'n') {
// replace the char with \n
buffer[i+1] = '\n';
}
-
+
// offset the rest of the buffer since we go from 2 to 1 char
System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
length--;
}
}
-
+
return new String(buffer, 0, length);
}