Merge "Layoutlib fixes for L [DO NOT MERGE]" into lmp-preview-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
dc925857fc
@@ -30,7 +30,6 @@ public class FontFamily {
|
||||
public long mNativePtr;
|
||||
|
||||
public FontFamily() {
|
||||
mNativePtr = nCreateFamily();
|
||||
mNativePtr = nCreateFamily();
|
||||
if (mNativePtr == 0) {
|
||||
throw new RuntimeException();
|
||||
|
||||
@@ -66,6 +66,9 @@ public class Typeface {
|
||||
static Map<String, Typeface> sSystemFontMap;
|
||||
static FontFamily[] sFallbackFonts;
|
||||
|
||||
static final String SYSTEM_FONTS_CONFIG = "system_fonts.xml";
|
||||
static final String FALLBACK_FONTS_CONFIG = "fallback_fonts.xml";
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -249,10 +252,16 @@ public class Typeface {
|
||||
return fontFamily;
|
||||
}
|
||||
|
||||
static {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* This should only be called once, from the static class initializer block.
|
||||
*/
|
||||
private static void init() {
|
||||
// Load font config and initialize Minikin state
|
||||
String systemConfigFilename = "/system/etc/system_fonts.xml";
|
||||
String configFilename = "/system/etc/fallback_fonts.xml";
|
||||
File systemFontConfigLocation = getSystemFontConfigLocation();
|
||||
File systemConfigFilename = new File(systemFontConfigLocation, SYSTEM_FONTS_CONFIG);
|
||||
File configFilename = new File(systemFontConfigLocation, FALLBACK_FONTS_CONFIG);
|
||||
try {
|
||||
// TODO: throws an exception non-Minikin builds, to fail early;
|
||||
// remove when Minikin-only
|
||||
@@ -301,7 +310,10 @@ public class Typeface {
|
||||
} catch (XmlPullParserException e) {
|
||||
Log.e(TAG, "XML parse exception for " + configFilename);
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
init();
|
||||
// Set up defaults and typefaces exposed in public API
|
||||
DEFAULT = create((String) null, 0);
|
||||
DEFAULT_BOLD = create((String) null, Typeface.BOLD);
|
||||
@@ -315,6 +327,11 @@ public class Typeface {
|
||||
create((String) null, Typeface.ITALIC),
|
||||
create((String) null, Typeface.BOLD_ITALIC),
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private static File getSystemFontConfigLocation() {
|
||||
return new File("/system/etc/");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -97,6 +97,13 @@ public class Resources_Theme_Delegate {
|
||||
return found;
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static TypedArray resolveAttributes(Resources thisResources, Theme thisTheme,
|
||||
int[] values, int[] attrs) {
|
||||
// FIXME
|
||||
return null;
|
||||
}
|
||||
|
||||
// ---- private helper methods ----
|
||||
|
||||
private static boolean setupResources(Theme thisTheme) {
|
||||
|
||||
@@ -27,4 +27,10 @@ public class TypedArray_Delegate {
|
||||
// pass
|
||||
return false;
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static TypedArray obtain(Resources res, int len) {
|
||||
// FIXME
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,11 @@ package android.graphics;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.GlyphVector;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -36,12 +38,12 @@ import android.graphics.Paint_Delegate.FontInfo;
|
||||
@SuppressWarnings("deprecation")
|
||||
public class BidiRenderer {
|
||||
|
||||
/* package */ static class ScriptRun {
|
||||
/*package*/ static class ScriptRun {
|
||||
int start;
|
||||
int limit;
|
||||
boolean isRtl;
|
||||
int scriptCode;
|
||||
FontInfo font;
|
||||
Font font;
|
||||
|
||||
public ScriptRun(int start, int limit, boolean isRtl) {
|
||||
this.start = start;
|
||||
@@ -51,9 +53,10 @@ public class BidiRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
private Graphics2D mGraphics;
|
||||
private Paint_Delegate mPaint;
|
||||
private final Graphics2D mGraphics;
|
||||
private final Paint_Delegate mPaint;
|
||||
private char[] mText;
|
||||
private List<Font> mFonts;
|
||||
// Bounds of the text drawn so far.
|
||||
private RectF mBounds;
|
||||
private float mBaseline;
|
||||
@@ -63,11 +66,15 @@ public class BidiRenderer {
|
||||
* @param paint The Paint to use to get the fonts. Should not be null.
|
||||
* @param text Unidirectional text. Should not be null.
|
||||
*/
|
||||
/* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
|
||||
/*package*/ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
|
||||
assert (paint != null);
|
||||
mGraphics = graphics;
|
||||
mPaint = paint;
|
||||
mText = text;
|
||||
mFonts = new ArrayList<Font>(paint.getFonts().size());
|
||||
for (FontInfo fontInfo : paint.getFonts()) {
|
||||
mFonts.add(fontInfo.mFont);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -94,7 +101,7 @@ public class BidiRenderer {
|
||||
// the script runs.
|
||||
mBounds = new RectF(x, y, x, y);
|
||||
mBaseline = y;
|
||||
for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mPaint.getFonts())) {
|
||||
for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
|
||||
int flag = Font.LAYOUT_NO_LIMIT_CONTEXT | Font.LAYOUT_NO_START_CONTEXT;
|
||||
flag |= isRtl ? Font.LAYOUT_RIGHT_TO_LEFT : Font.LAYOUT_LEFT_TO_RIGHT;
|
||||
renderScript(run.start, run.limit, run.font, flag, advances, advancesIndex, draw);
|
||||
@@ -108,16 +115,15 @@ public class BidiRenderer {
|
||||
* much as possible. This also implements a fallback mechanism to render characters that cannot
|
||||
* be drawn using the preferred font.
|
||||
*/
|
||||
private void renderScript(int start, int limit, FontInfo preferredFont, int flag,
|
||||
private void renderScript(int start, int limit, Font preferredFont, int flag,
|
||||
float[] advances, int advancesIndex, boolean draw) {
|
||||
List<FontInfo> fonts = mPaint.getFonts();
|
||||
if (fonts == null || preferredFont == null) {
|
||||
if (mFonts.size() == 0 || preferredFont == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (start < limit) {
|
||||
boolean foundFont = false;
|
||||
int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(mText, start, limit);
|
||||
int canDisplayUpTo = preferredFont.canDisplayUpTo(mText, start, limit);
|
||||
if (canDisplayUpTo == -1) {
|
||||
// We can draw all characters in the text.
|
||||
render(start, limit, preferredFont, flag, advances, advancesIndex, draw);
|
||||
@@ -133,8 +139,8 @@ public class BidiRenderer {
|
||||
// The current character cannot be drawn with the preferred font. Cycle through all the
|
||||
// fonts to check which one can draw it.
|
||||
int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
|
||||
for (FontInfo font : fonts) {
|
||||
canDisplayUpTo = font.mFont.canDisplayUpTo(mText, start, start + charCount);
|
||||
for (Font font : mFonts) {
|
||||
canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount);
|
||||
if (canDisplayUpTo == -1) {
|
||||
render(start, start+charCount, font, flag, advances, advancesIndex, draw);
|
||||
start += charCount;
|
||||
@@ -160,15 +166,19 @@ public class BidiRenderer {
|
||||
* Renders the text to the right of the bounds with the given font.
|
||||
* @param font The font to render the text with.
|
||||
*/
|
||||
private void render(int start, int limit, FontInfo font, int flag, float[] advances,
|
||||
private void render(int start, int limit, Font font, int flag, float[] advances,
|
||||
int advancesIndex, boolean draw) {
|
||||
|
||||
// Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with
|
||||
// the anti-aliasing set.
|
||||
FontRenderContext f = font.mMetrics.getFontRenderContext();
|
||||
FontRenderContext frc = new FontRenderContext(f.getTransform(), mPaint.isAntiAliased(),
|
||||
f.usesFractionalMetrics());
|
||||
GlyphVector gv = font.mFont.layoutGlyphVector(frc, mText, start, limit, flag);
|
||||
FontRenderContext frc;
|
||||
if (mGraphics != null) {
|
||||
frc = mGraphics.getFontRenderContext();
|
||||
} else {
|
||||
frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
|
||||
// Metrics obtained this way don't have anti-aliasing set. So,
|
||||
// we create a new FontRenderContext with anti-aliasing set.
|
||||
frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), frc.usesFractionalMetrics());
|
||||
}
|
||||
GlyphVector gv = font.layoutGlyphVector(frc, mText, start, limit, flag);
|
||||
int ng = gv.getNumGlyphs();
|
||||
int[] ci = gv.getGlyphCharIndices(0, ng, null);
|
||||
if (advances != null) {
|
||||
@@ -206,7 +216,7 @@ public class BidiRenderer {
|
||||
}
|
||||
|
||||
/* package */ static List<ScriptRun> getScriptRuns(char[] text, int start, int limit,
|
||||
boolean isRtl, List<FontInfo> fonts) {
|
||||
boolean isRtl, List<Font> fonts) {
|
||||
LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>();
|
||||
|
||||
int count = limit - start;
|
||||
@@ -225,10 +235,10 @@ public class BidiRenderer {
|
||||
|
||||
// TODO: Replace this method with one which returns the font based on the scriptCode.
|
||||
private static void setScriptFont(char[] text, ScriptRun run,
|
||||
List<FontInfo> fonts) {
|
||||
for (FontInfo fontInfo : fonts) {
|
||||
if (fontInfo.mFont.canDisplayUpTo(text, run.start, run.limit) == -1) {
|
||||
run.font = fontInfo;
|
||||
List<Font> fonts) {
|
||||
for (Font font : fonts) {
|
||||
if (font.canDisplayUpTo(text, run.start, run.limit) == -1) {
|
||||
run.font = font;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -979,7 +979,6 @@ public final class Canvas_Delegate {
|
||||
final float startX, final float startY, final int flags, long paint,
|
||||
long typeface) {
|
||||
|
||||
// TODO: use typeface.
|
||||
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
|
||||
new GcSnapshot.Drawable() {
|
||||
@Override
|
||||
@@ -1097,7 +1096,7 @@ public final class Canvas_Delegate {
|
||||
/**
|
||||
* Executes a {@link GcSnapshot.Drawable} with a given canvas and paint.
|
||||
* <p>Note that the drawable may actually be executed several times if there are
|
||||
* layers involved (see {@link #saveLayer(RectF, int, int)}.
|
||||
* layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
|
||||
*/
|
||||
private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
|
||||
GcSnapshot.Drawable drawable) {
|
||||
@@ -1117,7 +1116,7 @@ public final class Canvas_Delegate {
|
||||
* Executes a {@link GcSnapshot.Drawable} with a given canvas. No paint object will be provided
|
||||
* to {@link GcSnapshot.Drawable#draw(Graphics2D, Paint_Delegate)}.
|
||||
* <p>Note that the drawable may actually be executed several times if there are
|
||||
* layers involved (see {@link #saveLayer(RectF, int, int)}.
|
||||
* layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
|
||||
*/
|
||||
private static void draw(long nCanvas, GcSnapshot.Drawable drawable) {
|
||||
// get the delegate from the native int.
|
||||
@@ -1190,12 +1189,6 @@ public final class Canvas_Delegate {
|
||||
return mSnapshot.clipRect(left, top, right, bottom, regionOp);
|
||||
}
|
||||
|
||||
private void setBitmap(Bitmap_Delegate bitmap) {
|
||||
mBitmap = bitmap;
|
||||
assert mSnapshot.size() == 1;
|
||||
mSnapshot.setBitmap(mBitmap);
|
||||
}
|
||||
|
||||
private static void drawBitmap(
|
||||
long nativeCanvas,
|
||||
Bitmap_Delegate bitmap,
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.graphics;
|
||||
|
||||
import com.android.ide.common.rendering.api.LayoutLog;
|
||||
import com.android.layoutlib.bridge.Bridge;
|
||||
import com.android.layoutlib.bridge.impl.DelegateManager;
|
||||
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static android.graphics.Typeface_Delegate.SYSTEM_FONTS;
|
||||
|
||||
/**
|
||||
* Delegate implementing the native methods of android.graphics.FontFamily
|
||||
*
|
||||
* Through the layoutlib_create tool, the original native methods of FontFamily have been replaced
|
||||
* by calls to methods of the same name in this delegate class.
|
||||
*
|
||||
* This class behaves like the original native implementation, but in Java, keeping previously
|
||||
* native data into its own objects and mapping them to int that are sent back and forth between
|
||||
* it and the original FontFamily class.
|
||||
*
|
||||
* @see DelegateManager
|
||||
*/
|
||||
public class FontFamily_Delegate {
|
||||
|
||||
// FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
|
||||
// separately.
|
||||
private static final String FONT_SUFFIX_BOLDITALIC = "BoldItalic.ttf";
|
||||
private static final String FONT_SUFFIX_BOLD = "Bold.ttf";
|
||||
private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
|
||||
private static final String FONT_SUBSTRING_COMPACT = "UI";
|
||||
|
||||
/**
|
||||
* A class associating {@link Font} with its metadata.
|
||||
*/
|
||||
private static final class FontInfo {
|
||||
Font mFont;
|
||||
/** Regular, Bold, Italic, or BoldItalic. */
|
||||
int mStyle;
|
||||
/**
|
||||
* The variant of the Font - compact or elegant.
|
||||
* @see Paint#setElegantTextHeight(boolean)
|
||||
*/
|
||||
boolean mIsCompact;
|
||||
}
|
||||
|
||||
// ---- delegate manager ----
|
||||
private static final DelegateManager<FontFamily_Delegate> sManager =
|
||||
new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
|
||||
|
||||
// ---- delegate helper data ----
|
||||
private static String sFontLocation;
|
||||
private static final List<FontFamily_Delegate> sPostInitDelegate = new
|
||||
ArrayList<FontFamily_Delegate>();
|
||||
|
||||
|
||||
// ---- delegate data ----
|
||||
private List<FontInfo> mFonts = new ArrayList<FontInfo>();
|
||||
// Path of fonts that haven't been created since sFontLoader hasn't been initialized.
|
||||
private List<String> mPath = new ArrayList<String>();
|
||||
|
||||
|
||||
// ---- Public Helper methods ----
|
||||
|
||||
public static FontFamily_Delegate getDelegate(long nativeFontFamily) {
|
||||
return sManager.getDelegate(nativeFontFamily);
|
||||
}
|
||||
|
||||
public static synchronized void setFontLocation(String fontLocation) {
|
||||
sFontLocation = fontLocation;
|
||||
for (FontFamily_Delegate fontFamily : sPostInitDelegate) {
|
||||
fontFamily.init();
|
||||
}
|
||||
sPostInitDelegate.clear();
|
||||
}
|
||||
|
||||
public Font getFont(int style, boolean isCompact) {
|
||||
FontInfo plainFont = null;
|
||||
FontInfo styledFont = null; // Font matching the style but not isCompact
|
||||
for (FontInfo font : mFonts) {
|
||||
if (font.mStyle == style) {
|
||||
if (font.mIsCompact == isCompact) {
|
||||
return font.mFont;
|
||||
}
|
||||
styledFont = font;
|
||||
}
|
||||
if (font.mStyle == Font.PLAIN) {
|
||||
if (plainFont == null) {
|
||||
plainFont = font;
|
||||
continue;
|
||||
}
|
||||
if (font.mIsCompact == isCompact) {
|
||||
// Override the previous selection of plain font since we've found a better one.
|
||||
plainFont = font;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (styledFont != null) {
|
||||
return styledFont.mFont;
|
||||
}
|
||||
|
||||
// No font with the mentioned style is found. Try to derive one.
|
||||
if (plainFont != null && style > 0 && style < 4) {
|
||||
styledFont = new FontInfo();
|
||||
styledFont.mFont = plainFont.mFont.deriveFont(style);
|
||||
styledFont.mStyle = style;
|
||||
styledFont.mIsCompact = plainFont.mIsCompact;
|
||||
// Add the font to the list of fonts so that we don't have to derive it the next time.
|
||||
mFonts.add(styledFont);
|
||||
return styledFont.mFont;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ---- native methods ----
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static long nCreateFamily() {
|
||||
FontFamily_Delegate delegate = new FontFamily_Delegate();
|
||||
if (sFontLocation != null) {
|
||||
delegate.init();
|
||||
} else {
|
||||
sPostInitDelegate.add(delegate);
|
||||
}
|
||||
return sManager.addNewDelegate(delegate);
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static void nUnrefFamily(long nativePtr) {
|
||||
sManager.removeJavaReferenceFor(nativePtr);
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static boolean nAddFont(long nativeFamily, String path) {
|
||||
FontFamily_Delegate delegate = getDelegate(nativeFamily);
|
||||
if (delegate != null) {
|
||||
if (sFontLocation == null) {
|
||||
delegate.mPath.add(path);
|
||||
return true;
|
||||
}
|
||||
return delegate.addFont(path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void init() {
|
||||
for (String path : mPath) {
|
||||
addFont(path);
|
||||
}
|
||||
mPath = null;
|
||||
}
|
||||
|
||||
private boolean addFont(String path) {
|
||||
Font font = loadFont(path);
|
||||
if (font == null) {
|
||||
return false;
|
||||
}
|
||||
FontInfo fontInfo = new FontInfo();
|
||||
fontInfo.mFont = font;
|
||||
addFontMetadata(fontInfo, path);
|
||||
// TODO ensure that mFonts doesn't have the font with this style already.
|
||||
mFonts.add(fontInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void addFontMetadata(FontInfo fontInfo, String path) {
|
||||
int style = Font.PLAIN;
|
||||
String fontName = path.substring(path.lastIndexOf('/'), path.length());
|
||||
if (fontName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
|
||||
style = Font.BOLD | Font.ITALIC;
|
||||
} else if (fontName.endsWith(FONT_SUFFIX_BOLD)) {
|
||||
style = Font.BOLD;
|
||||
} else if (fontName.endsWith(FONT_SUFFIX_ITALIC)) {
|
||||
style = Font.ITALIC;
|
||||
}
|
||||
fontInfo.mStyle = style;
|
||||
|
||||
// Names of compact fonts end with UI-<style>.ttf. For example, NotoNakshUI-Regular.ttf.
|
||||
// This should go away when this info is passed on by nAddFont().
|
||||
int hyphenIndex = fontName.lastIndexOf('-');
|
||||
fontInfo.mIsCompact = hyphenIndex > 0 &&
|
||||
fontName.substring(0, hyphenIndex).endsWith(FONT_SUBSTRING_COMPACT);
|
||||
|
||||
}
|
||||
|
||||
private static Font loadFont(String path) {
|
||||
if (path.startsWith(SYSTEM_FONTS) ) {
|
||||
String relativePath = path.substring(SYSTEM_FONTS.length());
|
||||
File f = new File(sFontLocation, relativePath);
|
||||
|
||||
try {
|
||||
return Font.createFont(Font.TRUETYPE_FONT, f);
|
||||
} catch (Exception e) {
|
||||
Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
|
||||
String.format("Unable to load font %1$s", relativePath),
|
||||
null /*throwable*/, null /*data*/);
|
||||
}
|
||||
} else {
|
||||
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
|
||||
"Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
|
||||
null /*throwable*/, null /*data*/);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -202,6 +202,16 @@ public final class Matrix_Delegate {
|
||||
return d.isIdentity();
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static boolean native_isAffine(long native_object) {
|
||||
Matrix_Delegate d = sManager.getDelegate(native_object);
|
||||
if (d == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (d.computeTypeMask() & kPerspective_Mask) == 0;
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static boolean native_rectStaysRect(long native_object) {
|
||||
Matrix_Delegate d = sManager.getDelegate(native_object);
|
||||
|
||||
@@ -53,7 +53,7 @@ import java.util.Locale;
|
||||
public class Paint_Delegate {
|
||||
|
||||
/**
|
||||
* Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
|
||||
* Class associating a {@link Font} and its {@link java.awt.FontMetrics}.
|
||||
*/
|
||||
/*package*/ static final class FontInfo {
|
||||
Font mFont;
|
||||
@@ -66,8 +66,6 @@ public class Paint_Delegate {
|
||||
|
||||
// ---- delegate helper data ----
|
||||
private List<FontInfo> mFonts;
|
||||
private final FontRenderContext mFontContext = new FontRenderContext(
|
||||
new AffineTransform(), true, true);
|
||||
|
||||
// ---- delegate data ----
|
||||
private int mFlags;
|
||||
@@ -83,6 +81,7 @@ public class Paint_Delegate {
|
||||
private float mTextScaleX;
|
||||
private float mTextSkewX;
|
||||
private int mHintingMode = Paint.HINTING_ON;
|
||||
private boolean mIsCompact = true;
|
||||
|
||||
private Xfermode_Delegate mXfermode;
|
||||
private ColorFilter_Delegate mColorFilter;
|
||||
@@ -101,8 +100,7 @@ public class Paint_Delegate {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Returns the list of {@link Font} objects.
|
||||
*/
|
||||
public List<FontInfo> getFonts() {
|
||||
return mFonts;
|
||||
@@ -437,12 +435,20 @@ public class Paint_Delegate {
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static boolean isElegantTextHeight(Paint thisPaint) {
|
||||
return false;
|
||||
// get the delegate from the native int.
|
||||
Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
|
||||
return delegate != null && !delegate.mIsCompact;
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) {
|
||||
// TODO
|
||||
// get the delegate from the native int.
|
||||
Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
|
||||
if (delegate == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
delegate.mIsCompact = !elegant;
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
@@ -621,7 +627,6 @@ public class Paint_Delegate {
|
||||
int inc = count > 0 ? 1 : -1;
|
||||
|
||||
int measureIndex = 0;
|
||||
float measureAcc = 0;
|
||||
for (int i = index; i != index + count; i += inc, measureIndex++) {
|
||||
int start, end;
|
||||
if (i < index) {
|
||||
@@ -640,7 +645,6 @@ public class Paint_Delegate {
|
||||
measuredWidth[measureIndex] = res;
|
||||
}
|
||||
|
||||
measureAcc += res;
|
||||
if (res > maxWidth) {
|
||||
// we should not return this char index, but since it's 0-based
|
||||
// and we need to return a count, we simply return measureIndex;
|
||||
@@ -818,7 +822,7 @@ public class Paint_Delegate {
|
||||
return filter;
|
||||
}
|
||||
|
||||
delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);;
|
||||
delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
|
||||
|
||||
// since none of those are supported, display a fidelity warning right away
|
||||
if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
|
||||
@@ -940,52 +944,17 @@ public class Paint_Delegate {
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static int native_getTextWidths(long native_object, char[] text, int index,
|
||||
int count, int bidiFlags, float[] widths) {
|
||||
// get the delegate from the native int.
|
||||
Paint_Delegate delegate = sManager.getDelegate(native_object);
|
||||
if (delegate == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (delegate.mFonts.size() > 0) {
|
||||
// FIXME: handle multi-char characters (see measureText)
|
||||
float totalAdvance = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
char c = text[i + index];
|
||||
boolean found = false;
|
||||
for (FontInfo info : delegate.mFonts) {
|
||||
if (info.mFont.canDisplay(c)) {
|
||||
float adv = info.mMetrics.charWidth(c);
|
||||
totalAdvance += adv;
|
||||
if (widths != null) {
|
||||
widths[i] = adv;
|
||||
}
|
||||
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (found == false) {
|
||||
// no advance for this char.
|
||||
if (widths != null) {
|
||||
widths[i] = 0.f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (int) totalAdvance;
|
||||
}
|
||||
|
||||
return 0;
|
||||
/*package*/ static int native_getTextWidths(long native_object, long native_typeface,
|
||||
char[] text, int index, int count, int bidiFlags, float[] widths) {
|
||||
return (int) native_getTextRunAdvances(native_object, native_typeface, text, index, count,
|
||||
index, count, bidiFlags, widths, 0);
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static int native_getTextWidths(long native_object, String text, int start,
|
||||
int end, int bidiFlags, float[] widths) {
|
||||
return native_getTextWidths(native_object, text.toCharArray(), start, end - start,
|
||||
bidiFlags, widths);
|
||||
/*package*/ static int native_getTextWidths(long native_object, long native_typeface,
|
||||
String text, int start, int end, int bidiFlags, float[] widths) {
|
||||
return native_getTextWidths(native_object, native_typeface, text.toCharArray(), start,
|
||||
end - start, bidiFlags, widths);
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
@@ -997,15 +966,20 @@ public class Paint_Delegate {
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static float native_getTextRunAdvances(long native_object,
|
||||
long native_typeface /*ignored*/,
|
||||
char[] text, int index, int count, int contextIndex, int contextCount,
|
||||
int flags, float[] advances, int advancesIndex) {
|
||||
|
||||
// native_typeface is passed here since Framework's old implementation did not have the
|
||||
// typeface object associated with the Paint. Since, we follow the new framework way,
|
||||
// we store the typeface with the paint and use it directly.
|
||||
|
||||
if (advances != null)
|
||||
for (int i = advancesIndex; i< advancesIndex+count; i++)
|
||||
advances[i]=0;
|
||||
// get the delegate from the native int.
|
||||
Paint_Delegate delegate = sManager.getDelegate(native_object);
|
||||
if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
|
||||
if (delegate == null) {
|
||||
return 0.f;
|
||||
}
|
||||
boolean isRtl = isRtl(flags);
|
||||
@@ -1017,7 +991,7 @@ public class Paint_Delegate {
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static float native_getTextRunAdvances(long native_object,
|
||||
/*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
|
||||
String text, int start, int end, int contextStart, int contextEnd,
|
||||
int flags, float[] advances, int advancesIndex) {
|
||||
// FIXME: support contextStart and contextEnd
|
||||
@@ -1025,8 +999,8 @@ public class Paint_Delegate {
|
||||
char[] buffer = TemporaryBuffer.obtain(count);
|
||||
TextUtils.getChars(text, start, end, buffer, 0);
|
||||
|
||||
return native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
|
||||
contextEnd - contextStart, flags, advances, advancesIndex);
|
||||
return native_getTextRunAdvances(native_object, native_typeface, buffer, 0, count,
|
||||
contextStart, contextEnd - contextStart, flags, advances, advancesIndex);
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
@@ -1076,7 +1050,7 @@ public class Paint_Delegate {
|
||||
|
||||
// get the delegate from the native int.
|
||||
Paint_Delegate delegate = sManager.getDelegate(nativePaint);
|
||||
if (delegate == null || delegate.mFonts == null || delegate.mFonts.size() == 0) {
|
||||
if (delegate == null) {
|
||||
return;
|
||||
}
|
||||
delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds);
|
||||
@@ -1150,7 +1124,7 @@ public class Paint_Delegate {
|
||||
private void updateFontObject() {
|
||||
if (mTypeface != null) {
|
||||
// Get the fonts from the TypeFace object.
|
||||
List<Font> fonts = mTypeface.getFonts();
|
||||
List<Font> fonts = mTypeface.getFonts(mIsCompact);
|
||||
|
||||
// create new font objects as well as FontMetrics, based on the current text size
|
||||
// and skew info.
|
||||
|
||||
@@ -19,7 +19,6 @@ package android.graphics;
|
||||
import com.android.ide.common.rendering.api.LayoutLog;
|
||||
import com.android.layoutlib.bridge.Bridge;
|
||||
import com.android.layoutlib.bridge.impl.DelegateManager;
|
||||
import com.android.layoutlib.bridge.impl.FontLoader;
|
||||
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
|
||||
|
||||
import android.content.res.AssetManager;
|
||||
@@ -44,100 +43,67 @@ import java.util.List;
|
||||
*/
|
||||
public final class Typeface_Delegate {
|
||||
|
||||
private static final String SYSTEM_FONTS = "/system/fonts/";
|
||||
public static final String SYSTEM_FONTS = "/system/fonts/";
|
||||
|
||||
// ---- delegate manager ----
|
||||
private static final DelegateManager<Typeface_Delegate> sManager =
|
||||
new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class);
|
||||
|
||||
// ---- delegate helper data ----
|
||||
private static final String DEFAULT_FAMILY = "sans-serif";
|
||||
|
||||
private static FontLoader sFontLoader;
|
||||
private static final List<Typeface_Delegate> sPostInitDelegate =
|
||||
new ArrayList<Typeface_Delegate>();
|
||||
private static String sFontLocation;
|
||||
|
||||
// ---- delegate data ----
|
||||
|
||||
private final String mFamily;
|
||||
private final long[] mFontFamilies; // the reference to FontFamily_Delegate.
|
||||
private int mStyle;
|
||||
private List<Font> mFonts;
|
||||
|
||||
private static long sDefaultTypeface;
|
||||
|
||||
// ---- Public Helper methods ----
|
||||
|
||||
public static synchronized void init(FontLoader fontLoader) {
|
||||
sFontLoader = fontLoader;
|
||||
|
||||
for (Typeface_Delegate delegate : sPostInitDelegate) {
|
||||
delegate.init();
|
||||
}
|
||||
sPostInitDelegate.clear();
|
||||
public static synchronized void setFontLocation(String fontLocation) {
|
||||
sFontLocation = fontLocation;
|
||||
FontFamily_Delegate.setFontLocation(fontLocation);
|
||||
}
|
||||
|
||||
public static Typeface_Delegate getDelegate(long nativeTypeface) {
|
||||
return sManager.getDelegate(nativeTypeface);
|
||||
}
|
||||
|
||||
public static List<Font> getFonts(Typeface typeface) {
|
||||
return getFonts(typeface.native_instance);
|
||||
}
|
||||
|
||||
public static List<Font> getFonts(long native_int) {
|
||||
Typeface_Delegate delegate = sManager.getDelegate(native_int);
|
||||
if (delegate == null) {
|
||||
return null;
|
||||
public List<Font> getFonts(boolean compact) {
|
||||
List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
|
||||
for (long fontFamily : mFontFamilies) {
|
||||
FontFamily_Delegate ffd = FontFamily_Delegate.getDelegate(fontFamily);
|
||||
if (ffd != null) {
|
||||
Font font = ffd.getFont(mStyle, compact);
|
||||
if (font != null) {
|
||||
fonts.add(font);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return delegate.getFonts();
|
||||
}
|
||||
|
||||
public List<Font> getFonts() {
|
||||
return mFonts;
|
||||
return fonts;
|
||||
}
|
||||
|
||||
// ---- native methods ----
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static synchronized long nativeCreate(String familyName, int style) {
|
||||
if (familyName == null) {
|
||||
familyName = DEFAULT_FAMILY;
|
||||
}
|
||||
if (style < 0) {
|
||||
style = Typeface.NORMAL;
|
||||
}
|
||||
|
||||
Typeface_Delegate newDelegate = new Typeface_Delegate(familyName, style);
|
||||
if (sFontLoader != null) {
|
||||
newDelegate.init();
|
||||
} else {
|
||||
// font loader has not been initialized yet, add the delegate to a list of delegates
|
||||
// to init when the font loader is initialized.
|
||||
// There won't be any rendering before this happens anyway.
|
||||
sPostInitDelegate.add(newDelegate);
|
||||
}
|
||||
|
||||
return sManager.addNewDelegate(newDelegate);
|
||||
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
|
||||
"Could not find font with family \"" + familyName + "\".",
|
||||
null /*throwable*/, null /*data*/);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) {
|
||||
Typeface_Delegate delegate = sManager.getDelegate(native_instance);
|
||||
if (delegate == null) {
|
||||
delegate = sManager.getDelegate(sDefaultTypeface);
|
||||
}
|
||||
if (delegate == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Typeface_Delegate newDelegate = new Typeface_Delegate(delegate.mFamily, style);
|
||||
if (sFontLoader != null) {
|
||||
newDelegate.init();
|
||||
} else {
|
||||
// font loader has not been initialized yet, add the delegate to a list of delegates
|
||||
// to init when the font loader is initialized.
|
||||
// There won't be any rendering before this happens anyway.
|
||||
sPostInitDelegate.add(newDelegate);
|
||||
}
|
||||
|
||||
return sManager.addNewDelegate(newDelegate);
|
||||
return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style));
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
@@ -149,31 +115,15 @@ public final class Typeface_Delegate {
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static synchronized long nativeCreateFromFile(String path) {
|
||||
if (path.startsWith(SYSTEM_FONTS) ) {
|
||||
String relativePath = path.substring(SYSTEM_FONTS.length());
|
||||
File f = new File(sFontLoader.getOsFontsLocation(), relativePath);
|
||||
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
|
||||
"Typeface.createFromFile() is not supported.,", null, null);
|
||||
return 0;
|
||||
}
|
||||
|
||||
try {
|
||||
Font font = Font.createFont(Font.TRUETYPE_FONT, f);
|
||||
if (font != null) {
|
||||
Typeface_Delegate newDelegate = new Typeface_Delegate(font);
|
||||
return sManager.addNewDelegate(newDelegate);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
|
||||
String.format("Unable to load font %1$s", relativePath),
|
||||
null /*throwable*/, null /*data*/);
|
||||
}
|
||||
} else {
|
||||
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
|
||||
"Typeface.createFromFile() can only work with platform fonts located in " +
|
||||
SYSTEM_FONTS,
|
||||
null /*throwable*/, null /*data*/);
|
||||
}
|
||||
|
||||
|
||||
// return a copy of the base font
|
||||
return nativeCreate(null, 0);
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) {
|
||||
Typeface_Delegate delegate = new Typeface_Delegate(familyArray, Typeface.NORMAL);
|
||||
return sManager.addNewDelegate(delegate);
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
@@ -191,24 +141,21 @@ public final class Typeface_Delegate {
|
||||
return delegate.mStyle;
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static void nativeSetDefault(long native_instance) {
|
||||
sDefaultTypeface = native_instance;
|
||||
}
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static File getSystemFontConfigLocation() {
|
||||
return new File(sFontLocation);
|
||||
}
|
||||
|
||||
// ---- Private delegate/helper methods ----
|
||||
|
||||
private Typeface_Delegate(String family, int style) {
|
||||
mFamily = family;
|
||||
private Typeface_Delegate(long[] fontFamilies, int style) {
|
||||
mFontFamilies = fontFamilies;
|
||||
mStyle = style;
|
||||
}
|
||||
|
||||
private Typeface_Delegate(Font font) {
|
||||
mFamily = font.getFamily();
|
||||
mStyle = Typeface.NORMAL;
|
||||
|
||||
mFonts = sFontLoader.getFallbackFonts(mStyle);
|
||||
|
||||
// insert the font glyph first.
|
||||
mFonts.add(0, font);
|
||||
}
|
||||
|
||||
private void init() {
|
||||
mFonts = sFontLoader.getFont(mFamily, mStyle);
|
||||
}
|
||||
}
|
||||
|
||||
49
tools/layoutlib/bridge/src/android/util/Xml_Delegate.java
Normal file
49
tools/layoutlib/bridge/src/android/util/Xml_Delegate.java
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.util;
|
||||
|
||||
import com.android.layoutlib.bridge.impl.DelegateManager;
|
||||
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
|
||||
|
||||
import org.kxml2.io.KXmlParser;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
/**
|
||||
* Delegate overriding some methods of android.util.Xml
|
||||
*
|
||||
* Through the layoutlib_create tool, the original methods of Xml have been replaced
|
||||
* by calls to methods of the same name in this delegate class.
|
||||
*
|
||||
* Because it's a stateless class to start with, there's no need to keep a {@link DelegateManager}
|
||||
* around to map int to instance of the delegate.
|
||||
*/
|
||||
public class Xml_Delegate {
|
||||
|
||||
@LayoutlibDelegate
|
||||
/*package*/ static XmlPullParser newPullParser() {
|
||||
try {
|
||||
KXmlParser parser = new KXmlParser();
|
||||
// The prebuilt kxml2 library with the IDE doesn't support DOCECL.
|
||||
// parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
|
||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
|
||||
return parser;
|
||||
} catch (XmlPullParserException e) {
|
||||
throw new AssertionError();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -26,7 +26,6 @@ import com.android.ide.common.rendering.api.RenderSession;
|
||||
import com.android.ide.common.rendering.api.Result;
|
||||
import com.android.ide.common.rendering.api.Result.Status;
|
||||
import com.android.ide.common.rendering.api.SessionParams;
|
||||
import com.android.layoutlib.bridge.impl.FontLoader;
|
||||
import com.android.layoutlib.bridge.impl.RenderDrawable;
|
||||
import com.android.layoutlib.bridge.impl.RenderSessionImpl;
|
||||
import com.android.layoutlib.bridge.util.DynamicIdMap;
|
||||
@@ -61,7 +60,7 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
/**
|
||||
* Main entry point of the LayoutLib Bridge.
|
||||
* <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call
|
||||
* {@link #createScene(SceneParams)}
|
||||
* {@link #createSession(SessionParams)}
|
||||
*/
|
||||
public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
|
||||
|
||||
@@ -147,8 +146,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
|
||||
IntArray other = (IntArray) obj;
|
||||
if (!Arrays.equals(mArray, other.mArray)) return false;
|
||||
return true;
|
||||
return Arrays.equals(mArray, other.mArray);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,14 +249,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
|
||||
}
|
||||
|
||||
// load the fonts.
|
||||
FontLoader fontLoader = FontLoader.create(fontLocation.getAbsolutePath());
|
||||
if (fontLoader != null) {
|
||||
Typeface_Delegate.init(fontLoader);
|
||||
} else {
|
||||
log.error(LayoutLog.TAG_BROKEN,
|
||||
"Failed create FontLoader in layout lib.", null);
|
||||
return false;
|
||||
}
|
||||
Typeface_Delegate.setFontLocation(fontLocation.getAbsolutePath());
|
||||
|
||||
// now parse com.android.internal.R (and only this one as android.R is a subset of
|
||||
// the internal version), and put the content in the maps.
|
||||
|
||||
@@ -1,398 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.layoutlib.bridge.impl;
|
||||
|
||||
import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import android.graphics.Typeface;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
|
||||
/**
|
||||
* Provides {@link Font} object to the layout lib.
|
||||
* <p/>
|
||||
* The fonts are loaded from the SDK directory. Family/style mapping is done by parsing the
|
||||
* fonts.xml file located alongside the ttf files.
|
||||
*/
|
||||
public final class FontLoader {
|
||||
private static final String FONTS_SYSTEM = "system_fonts.xml";
|
||||
private static final String FONTS_VENDOR = "vendor_fonts.xml";
|
||||
private static final String FONTS_FALLBACK = "fallback_fonts.xml";
|
||||
|
||||
private static final String NODE_FAMILYSET = "familyset";
|
||||
private static final String NODE_FAMILY = "family";
|
||||
private static final String NODE_NAME = "name";
|
||||
private static final String NODE_FILE = "file";
|
||||
|
||||
private static final String ATTRIBUTE_VARIANT = "variant";
|
||||
private static final String ATTRIBUTE_VALUE_ELEGANT = "elegant";
|
||||
private static final String FONT_SUFFIX_NONE = ".ttf";
|
||||
private static final String FONT_SUFFIX_REGULAR = "-Regular.ttf";
|
||||
private static final String FONT_SUFFIX_BOLD = "-Bold.ttf";
|
||||
// FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
|
||||
// separately.
|
||||
private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
|
||||
private static final String FONT_SUFFIX_BOLDITALIC = "-BoldItalic.ttf";
|
||||
|
||||
// This must match the values of Typeface styles so that we can use them for indices in this
|
||||
// array.
|
||||
private static final int[] AWT_STYLES = new int[] {
|
||||
Font.PLAIN,
|
||||
Font.BOLD,
|
||||
Font.ITALIC,
|
||||
Font.BOLD | Font.ITALIC
|
||||
};
|
||||
private static int[] DERIVE_BOLD_ITALIC = new int[] {
|
||||
Typeface.ITALIC, Typeface.BOLD, Typeface.NORMAL
|
||||
};
|
||||
private static int[] DERIVE_ITALIC = new int[] { Typeface.NORMAL };
|
||||
private static int[] DERIVE_BOLD = new int[] { Typeface.NORMAL };
|
||||
|
||||
private static final List<FontInfo> mMainFonts = new ArrayList<FontInfo>();
|
||||
private static final List<FontInfo> mFallbackFonts = new ArrayList<FontInfo>();
|
||||
|
||||
private final String mOsFontsLocation;
|
||||
|
||||
public static FontLoader create(String fontOsLocation) {
|
||||
try {
|
||||
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
|
||||
parserFactory.setNamespaceAware(true);
|
||||
|
||||
// parse the system fonts
|
||||
FontHandler handler = parseFontFile(parserFactory, fontOsLocation, FONTS_SYSTEM);
|
||||
List<FontInfo> systemFonts = handler.getFontList();
|
||||
|
||||
|
||||
// parse the fallback fonts
|
||||
handler = parseFontFile(parserFactory, fontOsLocation, FONTS_FALLBACK);
|
||||
List<FontInfo> fallbackFonts = handler.getFontList();
|
||||
|
||||
return new FontLoader(fontOsLocation, systemFonts, fallbackFonts);
|
||||
} catch (ParserConfigurationException e) {
|
||||
// return null below
|
||||
} catch (SAXException e) {
|
||||
// return null below
|
||||
} catch (FileNotFoundException e) {
|
||||
// return null below
|
||||
} catch (IOException e) {
|
||||
// return null below
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static FontHandler parseFontFile(SAXParserFactory parserFactory,
|
||||
String fontOsLocation, String fontFileName)
|
||||
throws ParserConfigurationException, SAXException, IOException, FileNotFoundException {
|
||||
|
||||
SAXParser parser = parserFactory.newSAXParser();
|
||||
File f = new File(fontOsLocation, fontFileName);
|
||||
|
||||
FontHandler definitionParser = new FontHandler(
|
||||
fontOsLocation + File.separator);
|
||||
parser.parse(new FileInputStream(f), definitionParser);
|
||||
return definitionParser;
|
||||
}
|
||||
|
||||
private FontLoader(String fontOsLocation,
|
||||
List<FontInfo> fontList, List<FontInfo> fallBackList) {
|
||||
mOsFontsLocation = fontOsLocation;
|
||||
mMainFonts.addAll(fontList);
|
||||
mFallbackFonts.addAll(fallBackList);
|
||||
}
|
||||
|
||||
|
||||
public String getOsFontsLocation() {
|
||||
return mOsFontsLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Font} object given a family name and a style value (constant in
|
||||
* {@link Typeface}).
|
||||
* @param family the family name
|
||||
* @param style a 1-item array containing the requested style. Based on the font being read
|
||||
* the actual style may be different. The array contains the actual style after
|
||||
* the method returns.
|
||||
* @return the font object or null if no match could be found.
|
||||
*/
|
||||
public synchronized List<Font> getFont(String family, int style) {
|
||||
List<Font> result = new ArrayList<Font>();
|
||||
|
||||
if (family == null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// get the font objects from the main list based on family.
|
||||
for (FontInfo info : mMainFonts) {
|
||||
if (info.families.contains(family)) {
|
||||
result.add(info.font[style]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add all the fallback fonts for the given style
|
||||
for (FontInfo info : mFallbackFonts) {
|
||||
result.add(info.font[style]);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
public synchronized List<Font> getFallbackFonts(int style) {
|
||||
List<Font> result = new ArrayList<Font>();
|
||||
// add all the fallback fonts
|
||||
for (FontInfo info : mFallbackFonts) {
|
||||
result.add(info.font[style]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private final static class FontInfo {
|
||||
final Font[] font = new Font[4]; // Matches the 4 type-face styles.
|
||||
final Set<String> families;
|
||||
|
||||
FontInfo() {
|
||||
families = new HashSet<String>();
|
||||
}
|
||||
}
|
||||
|
||||
private final static class FontHandler extends DefaultHandler {
|
||||
private final String mOsFontsLocation;
|
||||
|
||||
private FontInfo mFontInfo = null;
|
||||
private final StringBuilder mBuilder = new StringBuilder();
|
||||
private List<FontInfo> mFontList = new ArrayList<FontInfo>();
|
||||
private boolean isCompactFont = true;
|
||||
|
||||
private FontHandler(String osFontsLocation) {
|
||||
super();
|
||||
mOsFontsLocation = osFontsLocation;
|
||||
}
|
||||
|
||||
public List<FontInfo> getFontList() {
|
||||
return mFontList;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
|
||||
*/
|
||||
@Override
|
||||
public void startElement(String uri, String localName, String name, Attributes attributes)
|
||||
throws SAXException {
|
||||
if (NODE_FAMILYSET.equals(localName)) {
|
||||
mFontList = new ArrayList<FontInfo>();
|
||||
} else if (NODE_FAMILY.equals(localName)) {
|
||||
if (mFontList != null) {
|
||||
mFontInfo = null;
|
||||
}
|
||||
} else if (NODE_NAME.equals(localName)) {
|
||||
if (mFontList != null && mFontInfo == null) {
|
||||
mFontInfo = new FontInfo();
|
||||
}
|
||||
} else if (NODE_FILE.equals(localName)) {
|
||||
if (mFontList != null && mFontInfo == null) {
|
||||
mFontInfo = new FontInfo();
|
||||
}
|
||||
if (ATTRIBUTE_VALUE_ELEGANT.equals(attributes.getValue(ATTRIBUTE_VARIANT))) {
|
||||
isCompactFont = false;
|
||||
} else {
|
||||
isCompactFont = true;
|
||||
}
|
||||
}
|
||||
|
||||
mBuilder.setLength(0);
|
||||
|
||||
super.startElement(uri, localName, name, attributes);
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
|
||||
*/
|
||||
@Override
|
||||
public void characters(char[] ch, int start, int length) throws SAXException {
|
||||
if (isCompactFont) {
|
||||
mBuilder.append(ch, start, length);
|
||||
}
|
||||
}
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void endElement(String uri, String localName, String name) throws SAXException {
|
||||
if (NODE_FAMILY.equals(localName)) {
|
||||
if (mFontInfo != null) {
|
||||
// if has a normal font file, add to the list
|
||||
if (mFontInfo.font[Typeface.NORMAL] != null) {
|
||||
mFontList.add(mFontInfo);
|
||||
|
||||
// create missing font styles, order is important.
|
||||
if (mFontInfo.font[Typeface.BOLD_ITALIC] == null) {
|
||||
computeDerivedFont(Typeface.BOLD_ITALIC, DERIVE_BOLD_ITALIC);
|
||||
}
|
||||
if (mFontInfo.font[Typeface.ITALIC] == null) {
|
||||
computeDerivedFont(Typeface.ITALIC, DERIVE_ITALIC);
|
||||
}
|
||||
if (mFontInfo.font[Typeface.BOLD] == null) {
|
||||
computeDerivedFont(Typeface.BOLD, DERIVE_BOLD);
|
||||
}
|
||||
}
|
||||
|
||||
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_FILE.equals(localName)) {
|
||||
// handle a new file for an existing Font Info
|
||||
if (isCompactFont && mFontInfo != null) {
|
||||
String fileName = trimXmlWhitespaces(mBuilder.toString());
|
||||
Font font = getFont(fileName);
|
||||
if (font != null) {
|
||||
if (fileName.endsWith(FONT_SUFFIX_REGULAR)) {
|
||||
mFontInfo.font[Typeface.NORMAL] = font;
|
||||
} else if (fileName.endsWith(FONT_SUFFIX_BOLD)) {
|
||||
mFontInfo.font[Typeface.BOLD] = font;
|
||||
} else if (fileName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
|
||||
mFontInfo.font[Typeface.BOLD_ITALIC] = font;
|
||||
} else if (fileName.endsWith(FONT_SUFFIX_ITALIC)) {
|
||||
mFontInfo.font[Typeface.ITALIC] = font;
|
||||
} else if (fileName.endsWith(FONT_SUFFIX_NONE)) {
|
||||
mFontInfo.font[Typeface.NORMAL] = font;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Font getFont(String fileName) {
|
||||
try {
|
||||
File file = new File(mOsFontsLocation, fileName);
|
||||
if (file.exists()) {
|
||||
return Font.createFont(Font.TRUETYPE_FONT, file);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void computeDerivedFont( int toCompute, int[] basedOnList) {
|
||||
for (int basedOn : basedOnList) {
|
||||
if (mFontInfo.font[basedOn] != null) {
|
||||
mFontInfo.font[toCompute] =
|
||||
mFontInfo.font[basedOn].deriveFont(AWT_STYLES[toCompute]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// we really shouldn't stop there. This means we don't have a NORMAL font...
|
||||
assert false;
|
||||
}
|
||||
|
||||
private String trimXmlWhitespaces(String value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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;
|
||||
while (left >= 0) {
|
||||
if (Character.isWhitespace(value.charAt(left))) {
|
||||
left--;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int right = index + 1;
|
||||
int count = value.length();
|
||||
while (right < count) {
|
||||
if (Character.isWhitespace(value.charAt(right))) {
|
||||
right++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// remove all between left and right (non inclusive) and replace by a single space.
|
||||
String leftString = null;
|
||||
if (left >= 0) {
|
||||
leftString = value.substring(0, left + 1);
|
||||
}
|
||||
String rightString = null;
|
||||
if (right < count) {
|
||||
rightString = value.substring(right);
|
||||
}
|
||||
|
||||
if (leftString != null) {
|
||||
value = leftString;
|
||||
if (rightString != null) {
|
||||
value += " " + rightString;
|
||||
}
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -125,15 +125,19 @@ public final class CreateInfo implements ICreateInfo {
|
||||
"android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
|
||||
"android.content.res.Resources$Theme#obtainStyledAttributes",
|
||||
"android.content.res.Resources$Theme#resolveAttribute",
|
||||
"android.content.res.Resources$Theme#resolveAttributes",
|
||||
"android.content.res.Resources#localeToLanguageTag",
|
||||
"android.content.res.AssetManager#newTheme",
|
||||
"android.content.res.AssetManager#deleteTheme",
|
||||
"android.content.res.AssetManager#applyThemeStyle",
|
||||
"android.content.res.TypedArray#getValueAt",
|
||||
"android.content.res.TypedArray#obtain",
|
||||
"android.graphics.BitmapFactory#finishDecode",
|
||||
"android.graphics.Typeface#getSystemFontConfigLocation",
|
||||
"android.os.Handler#sendMessageAtTime",
|
||||
"android.os.HandlerThread#run",
|
||||
"android.text.format.DateFormat#is24HourFormat",
|
||||
"android.util.Xml#newPullParser",
|
||||
"android.view.Choreographer#getRefreshRate",
|
||||
"android.view.Display#updateDisplayInfoLocked",
|
||||
"android.view.Display#getWindowManager",
|
||||
@@ -170,6 +174,7 @@ public final class CreateInfo implements ICreateInfo {
|
||||
"android.graphics.DiscretePathEffect",
|
||||
"android.graphics.DrawFilter",
|
||||
"android.graphics.EmbossMaskFilter",
|
||||
"android.graphics.FontFamily",
|
||||
"android.graphics.LayerRasterizer",
|
||||
"android.graphics.LightingColorFilter",
|
||||
"android.graphics.LinearGradient",
|
||||
|
||||
Reference in New Issue
Block a user