Merge change Ib12bcb7f into eclair
* changes: Support for fallback fonts in layoutlib.
This commit is contained in:
@@ -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>
|
||||
@@ -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);
|
||||
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);
|
||||
|
||||
g.dispose();
|
||||
// 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)
|
||||
|
||||
@@ -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.
|
||||
@@ -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;
|
||||
@@ -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() {
|
||||
@@ -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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
// 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.
|
||||
}
|
||||
|
||||
return (float)bounds.getWidth();
|
||||
// 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;
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user