Merge commit 'aa10b9fb52db88e3cc9045019f42fa83e9da9868' into eclair-mr2-plus-aosp * commit 'aa10b9fb52db88e3cc9045019f42fa83e9da9868': 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
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
<!--
|
||||||
|
This is only used by the layoutlib to display
|
||||||
|
layouts in ADT.
|
||||||
|
-->
|
||||||
<fonts>
|
<fonts>
|
||||||
<font ttf="DroidSans">
|
<font ttf="DroidSans">
|
||||||
<name>sans-serif</name>
|
<name>sans-serif</name>
|
||||||
@@ -39,5 +43,6 @@
|
|||||||
<name>courier new</name>
|
<name>courier new</name>
|
||||||
<name>monaco</name>
|
<name>monaco</name>
|
||||||
</font>
|
</font>
|
||||||
<font ttf="DroidSansFallback" />
|
<fallback ttf="DroidSansFallback" />
|
||||||
|
<fallback ttf="DroidSansJapanese" />
|
||||||
</fonts>
|
</fonts>
|
||||||
@@ -26,6 +26,7 @@ import android.graphics.RectF;
|
|||||||
import android.graphics.Region;
|
import android.graphics.Region;
|
||||||
import android.graphics.Xfermode;
|
import android.graphics.Xfermode;
|
||||||
import android.graphics.Paint.Align;
|
import android.graphics.Paint.Align;
|
||||||
|
import android.graphics.Paint.FontInfo;
|
||||||
import android.graphics.Paint.Style;
|
import android.graphics.Paint.Style;
|
||||||
import android.graphics.Region.Op;
|
import android.graphics.Region.Op;
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ import java.awt.Rectangle;
|
|||||||
import java.awt.RenderingHints;
|
import java.awt.RenderingHints;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
import javax.microedition.khronos.opengles.GL;
|
import javax.microedition.khronos.opengles.GL;
|
||||||
@@ -620,19 +622,21 @@ public class Canvas extends _Original_Canvas {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void drawText(char[] text, int index, int count, float x, float y, Paint paint) {
|
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();
|
Graphics2D g = getGraphics2d();
|
||||||
|
|
||||||
g = (Graphics2D)g.create();
|
g = (Graphics2D)g.create();
|
||||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
g.setFont(paint.getFont());
|
// set the color. because this only handles RGB, the alpha channel is handled
|
||||||
|
// as a composite.
|
||||||
// set the color. because this only handles RGB we have to handle the alpha separately
|
|
||||||
g.setColor(new Color(paint.getColor()));
|
g.setColor(new Color(paint.getColor()));
|
||||||
int alpha = paint.getAlpha();
|
int alpha = paint.getAlpha();
|
||||||
float falpha = alpha / 255.f;
|
float falpha = alpha / 255.f;
|
||||||
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
|
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha));
|
||||||
|
|
||||||
|
|
||||||
// Paint.TextAlign indicates how the text is positioned relative to X.
|
// Paint.TextAlign indicates how the text is positioned relative to X.
|
||||||
// LEFT is the default and there's nothing to do.
|
// LEFT is the default and there's nothing to do.
|
||||||
if (paint.getTextAlign() != Align.LEFT) {
|
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)
|
/* (non-Javadoc)
|
||||||
|
|||||||
@@ -26,6 +26,9 @@ import java.awt.Toolkit;
|
|||||||
import java.awt.font.FontRenderContext;
|
import java.awt.font.FontRenderContext;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
import java.awt.geom.Rectangle2D;
|
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.
|
* A paint implementation overridden by the LayoutLib bridge.
|
||||||
@@ -44,10 +47,17 @@ public class Paint extends _Original_Paint {
|
|||||||
private Join mJoin = Join.MITER;
|
private Join mJoin = Join.MITER;
|
||||||
private int mFlags = 0;
|
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(
|
private final FontRenderContext mFontContext = new FontRenderContext(
|
||||||
new AffineTransform(), true, true);
|
new AffineTransform(), true, true);
|
||||||
private java.awt.FontMetrics mMetrics;
|
|
||||||
|
|
||||||
@SuppressWarnings("hiding")
|
@SuppressWarnings("hiding")
|
||||||
public static final int ANTI_ALIAS_FLAG = _Original_Paint.ANTI_ALIAS_FLAG;
|
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() {
|
public List<FontInfo> getFonts() {
|
||||||
return mFont;
|
return mFonts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initFont() {
|
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
|
* Update the {@link Font} object from the typeface, text size and scaling
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
private void updateFontObject() {
|
private void updateFontObject() {
|
||||||
if (mTypeface != null) {
|
if (mTypeface != null) {
|
||||||
// get the typeface font object, and get our font object from it, based on the current size
|
// Get the fonts from the TypeFace object.
|
||||||
mFont = mTypeface.getFont().deriveFont(mTextSize);
|
List<Font> fonts = mTypeface.getFonts();
|
||||||
if (mScaleX != 1.0 || mSkewX != 0) {
|
|
||||||
// TODO: support skew
|
// create new font objects as well as FontMetrics, based on the current text size
|
||||||
mFont = mFont.deriveFont(new AffineTransform(
|
// and skew info.
|
||||||
mScaleX, mSkewX, 0, 0, 1, 0));
|
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.
|
* @return the font's recommended interline spacing.
|
||||||
*/
|
*/
|
||||||
public float getFontMetrics(FontMetrics metrics) {
|
public float getFontMetrics(FontMetrics metrics) {
|
||||||
if (mMetrics != null) {
|
if (mFonts.size() > 0) {
|
||||||
|
java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
|
||||||
if (metrics != null) {
|
if (metrics != null) {
|
||||||
// ascent stuff should be negatif, but awt returns them as positive.
|
// Android expects negative ascent so we invert the value from Java.
|
||||||
metrics.top = - mMetrics.getMaxAscent();
|
metrics.top = - javaMetrics.getMaxAscent();
|
||||||
metrics.ascent = - mMetrics.getAscent();
|
metrics.ascent = - javaMetrics.getAscent();
|
||||||
metrics.descent = mMetrics.getDescent();
|
metrics.descent = javaMetrics.getDescent();
|
||||||
metrics.bottom = mMetrics.getMaxDescent();
|
metrics.bottom = javaMetrics.getMaxDescent();
|
||||||
metrics.leading = mMetrics.getLeading();
|
metrics.leading = javaMetrics.getLeading();
|
||||||
}
|
}
|
||||||
|
|
||||||
return mMetrics.getHeight();
|
return javaMetrics.getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getFontMetricsInt(FontMetricsInt metrics) {
|
public int getFontMetricsInt(FontMetricsInt metrics) {
|
||||||
if (mMetrics != null) {
|
if (mFonts.size() > 0) {
|
||||||
|
java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
|
||||||
if (metrics != null) {
|
if (metrics != null) {
|
||||||
// ascent stuff should be negatif, but awt returns them as positive.
|
// Android expects negative ascent so we invert the value from Java.
|
||||||
metrics.top = - mMetrics.getMaxAscent();
|
metrics.top = - javaMetrics.getMaxAscent();
|
||||||
metrics.ascent = - mMetrics.getAscent();
|
metrics.ascent = - javaMetrics.getAscent();
|
||||||
metrics.descent = mMetrics.getDescent();
|
metrics.descent = javaMetrics.getDescent();
|
||||||
metrics.bottom = mMetrics.getMaxDescent();
|
metrics.bottom = javaMetrics.getMaxDescent();
|
||||||
metrics.leading = mMetrics.getLeading();
|
metrics.leading = javaMetrics.getLeading();
|
||||||
}
|
}
|
||||||
|
|
||||||
return mMetrics.getHeight();
|
return javaMetrics.getHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -716,9 +741,10 @@ public class Paint extends _Original_Paint {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public float ascent() {
|
public float ascent() {
|
||||||
if (mMetrics != null) {
|
if (mFonts.size() > 0) {
|
||||||
// ascent stuff should be negatif, but awt returns them as positive.
|
java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
|
||||||
return - mMetrics.getAscent();
|
// Android expects negative ascent so we invert the value from Java.
|
||||||
|
return - javaMetrics.getAscent();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -733,8 +759,9 @@ public class Paint extends _Original_Paint {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public float descent() {
|
public float descent() {
|
||||||
if (mMetrics != null) {
|
if (mFonts.size() > 0) {
|
||||||
return mMetrics.getDescent();
|
java.awt.FontMetrics javaMetrics = mFonts.get(0).mMetrics;
|
||||||
|
return javaMetrics.getDescent();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -750,10 +777,55 @@ public class Paint extends _Original_Paint {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public float measureText(char[] text, int index, int count) {
|
public float measureText(char[] text, int index, int count) {
|
||||||
if (mFont != null && text != null && text.length > 0) {
|
// WARNING: the logic in this method is similar to Canvas.drawText.
|
||||||
Rectangle2D bounds = mFont.getStringBounds(text, index, index + count, mFontContext);
|
// 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;
|
return 0;
|
||||||
@@ -919,14 +991,30 @@ public class Paint extends _Original_Paint {
|
|||||||
@Override
|
@Override
|
||||||
public int getTextWidths(char[] text, int index, int count,
|
public int getTextWidths(char[] text, int index, int count,
|
||||||
float[] widths) {
|
float[] widths) {
|
||||||
if (mMetrics != null) {
|
if (mFonts.size() > 0) {
|
||||||
if ((index | count) < 0 || index + count > text.length
|
if ((index | count) < 0 || index + count > text.length
|
||||||
|| count > widths.length) {
|
|| count > widths.length) {
|
||||||
throw new ArrayIndexOutOfBoundsException();
|
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++) {
|
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;
|
return count;
|
||||||
@@ -1070,7 +1158,8 @@ public class Paint extends _Original_Paint {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void getTextBounds(char[] text, int index, int count, Rect bounds) {
|
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) {
|
if ((index | count) < 0 || index + count > text.length) {
|
||||||
throw new ArrayIndexOutOfBoundsException();
|
throw new ArrayIndexOutOfBoundsException();
|
||||||
}
|
}
|
||||||
@@ -1078,7 +1167,9 @@ public class Paint extends _Original_Paint {
|
|||||||
throw new NullPointerException("need bounds Rect");
|
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());
|
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 android.content.res.AssetManager;
|
||||||
|
|
||||||
import java.awt.Font;
|
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 {
|
public class Typeface {
|
||||||
private static final String DEFAULT_FAMILY = "sans-serif";
|
private static final String DEFAULT_FAMILY = "sans-serif";
|
||||||
@@ -46,11 +49,11 @@ public class Typeface {
|
|||||||
|
|
||||||
private static Typeface[] sDefaults;
|
private static Typeface[] sDefaults;
|
||||||
private static FontLoader mFontLoader;
|
private static FontLoader mFontLoader;
|
||||||
|
|
||||||
private final int mStyle;
|
private final int mStyle;
|
||||||
private final Font mFont;
|
private final List<Font> mFonts;
|
||||||
private final String mFamily;
|
private final String mFamily;
|
||||||
|
|
||||||
// Style
|
// Style
|
||||||
public static final int NORMAL = _Original_Typeface.NORMAL;
|
public static final int NORMAL = _Original_Typeface.NORMAL;
|
||||||
public static final int BOLD = _Original_Typeface.BOLD;
|
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;
|
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() {
|
public List<Font> getFonts() {
|
||||||
return mFont;
|
return mFonts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the typeface's intrinsic style attributes */
|
/** Returns the typeface's intrinsic style attributes */
|
||||||
public int getStyle() {
|
public int getStyle() {
|
||||||
return mStyle;
|
return mStyle;
|
||||||
@@ -94,9 +98,12 @@ public class Typeface {
|
|||||||
styleBuffer[0] = style;
|
styleBuffer[0] = style;
|
||||||
Font font = mFontLoader.getFont(familyName, styleBuffer);
|
Font font = mFontLoader.getFont(familyName, styleBuffer);
|
||||||
if (font != null) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +122,10 @@ public class Typeface {
|
|||||||
styleBuffer[0] = style;
|
styleBuffer[0] = style;
|
||||||
Font font = mFontLoader.getFont(family.mFamily, styleBuffer);
|
Font font = mFontLoader.getFont(family.mFamily, styleBuffer);
|
||||||
if (font != null) {
|
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;
|
return null;
|
||||||
@@ -129,7 +139,7 @@ public class Typeface {
|
|||||||
public static Typeface defaultFromStyle(int style) {
|
public static Typeface defaultFromStyle(int style) {
|
||||||
return sDefaults[style];
|
return sDefaults[style];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new typeface from the specified font data.
|
* Create a new typeface from the specified font data.
|
||||||
* @param mgr The application's asset manager
|
* @param mgr The application's asset manager
|
||||||
@@ -140,17 +150,17 @@ public class Typeface {
|
|||||||
return null;
|
return null;
|
||||||
//return new Typeface(nativeCreateFromAsset(mgr, path));
|
//return new Typeface(nativeCreateFromAsset(mgr, path));
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't allow clients to call this directly
|
// 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;
|
mFamily = family;
|
||||||
mFont = f;
|
mFonts = Collections.unmodifiableList(fonts);
|
||||||
mStyle = style;
|
mStyle = style;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void init(FontLoader fontLoader) {
|
public static void init(FontLoader fontLoader) {
|
||||||
mFontLoader = fontLoader;
|
mFontLoader = fontLoader;
|
||||||
|
|
||||||
DEFAULT = create(DEFAULT_FAMILY, NORMAL);
|
DEFAULT = create(DEFAULT_FAMILY, NORMAL);
|
||||||
DEFAULT_BOLD = create(DEFAULT_FAMILY, BOLD);
|
DEFAULT_BOLD = create(DEFAULT_FAMILY, BOLD);
|
||||||
SANS_SERIF = create("sans-serif", NORMAL);
|
SANS_SERIF = create("sans-serif", NORMAL);
|
||||||
@@ -162,14 +172,14 @@ public class Typeface {
|
|||||||
create(DEFAULT_FAMILY, ITALIC),
|
create(DEFAULT_FAMILY, ITALIC),
|
||||||
create(DEFAULT_FAMILY, BOLD_ITALIC),
|
create(DEFAULT_FAMILY, BOLD_ITALIC),
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
DEFAULT = create((String)null, 0);
|
DEFAULT = create((String)null, 0);
|
||||||
DEFAULT_BOLD = create((String)null, Typeface.BOLD);
|
DEFAULT_BOLD = create((String)null, Typeface.BOLD);
|
||||||
SANS_SERIF = create("sans-serif", 0);
|
SANS_SERIF = create("sans-serif", 0);
|
||||||
SERIF = create("serif", 0);
|
SERIF = create("serif", 0);
|
||||||
MONOSPACE = create("monospace", 0);
|
MONOSPACE = create("monospace", 0);
|
||||||
|
|
||||||
sDefaults = new Typeface[] {
|
sDefaults = new Typeface[] {
|
||||||
DEFAULT,
|
DEFAULT,
|
||||||
DEFAULT_BOLD,
|
DEFAULT_BOLD,
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import java.io.FileInputStream;
|
|||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -47,14 +48,13 @@ import javax.xml.parsers.SAXParserFactory;
|
|||||||
*/
|
*/
|
||||||
public final class FontLoader {
|
public final class FontLoader {
|
||||||
private static final String FONTS_DEFINITIONS = "fonts.xml";
|
private static final String FONTS_DEFINITIONS = "fonts.xml";
|
||||||
|
|
||||||
private static final String NODE_FONTS = "fonts";
|
private static final String NODE_FONTS = "fonts";
|
||||||
private static final String NODE_FONT = "font";
|
private static final String NODE_FONT = "font";
|
||||||
private static final String NODE_NAME = "name";
|
private static final String NODE_NAME = "name";
|
||||||
|
private static final String NODE_FALLBACK = "fallback";
|
||||||
private static final String ATTR_TTF = "ttf";
|
|
||||||
|
|
||||||
private static final String[] NODE_LEVEL = { NODE_FONTS, NODE_FONT, NODE_NAME };
|
private static final String ATTR_TTF = "ttf";
|
||||||
|
|
||||||
private static final String FONT_EXT = ".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_BOLD = { "-Bold" };
|
||||||
private static final String[] FONT_STYLE_ITALIC = { "-Italic" };
|
private static final String[] FONT_STYLE_ITALIC = { "-Italic" };
|
||||||
private static final String[] FONT_STYLE_BOLDITALIC = { "-BoldItalic" };
|
private static final String[] FONT_STYLE_BOLDITALIC = { "-BoldItalic" };
|
||||||
|
|
||||||
// list of font style, in the order matching the Typeface Font style
|
// list of font style, in the order matching the Typeface Font style
|
||||||
private static final String[][] FONT_STYLES = {
|
private static final String[][] FONT_STYLES = {
|
||||||
FONT_STYLE_DEFAULT,
|
FONT_STYLE_DEFAULT,
|
||||||
@@ -70,23 +70,25 @@ public final class FontLoader {
|
|||||||
FONT_STYLE_ITALIC,
|
FONT_STYLE_ITALIC,
|
||||||
FONT_STYLE_BOLDITALIC
|
FONT_STYLE_BOLDITALIC
|
||||||
};
|
};
|
||||||
|
|
||||||
private final Map<String, String> mFamilyToTtf = new HashMap<String, String>();
|
private final Map<String, String> mFamilyToTtf = new HashMap<String, String>();
|
||||||
private final Map<String, Map<Integer, Font>> mTtfToFontMap =
|
private final Map<String, Map<Integer, Font>> mTtfToFontMap =
|
||||||
new HashMap<String, Map<Integer, Font>>();
|
new HashMap<String, Map<Integer, Font>>();
|
||||||
|
|
||||||
|
private List<Font> mFallBackFonts = null;
|
||||||
|
|
||||||
public static FontLoader create(String fontOsLocation) {
|
public static FontLoader create(String fontOsLocation) {
|
||||||
try {
|
try {
|
||||||
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
|
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
|
||||||
parserFactory.setNamespaceAware(true);
|
parserFactory.setNamespaceAware(true);
|
||||||
|
|
||||||
SAXParser parser = parserFactory.newSAXParser();
|
SAXParser parser = parserFactory.newSAXParser();
|
||||||
File f = new File(fontOsLocation + File.separator + FONTS_DEFINITIONS);
|
File f = new File(fontOsLocation + File.separator + FONTS_DEFINITIONS);
|
||||||
|
|
||||||
FontDefinitionParser definitionParser = new FontDefinitionParser(
|
FontDefinitionParser definitionParser = new FontDefinitionParser(
|
||||||
fontOsLocation + File.separator);
|
fontOsLocation + File.separator);
|
||||||
parser.parse(new FileInputStream(f), definitionParser);
|
parser.parse(new FileInputStream(f), definitionParser);
|
||||||
|
|
||||||
return definitionParser.getFontLoader();
|
return definitionParser.getFontLoader();
|
||||||
} catch (ParserConfigurationException e) {
|
} catch (ParserConfigurationException e) {
|
||||||
// return null below
|
// return null below
|
||||||
@@ -101,12 +103,35 @@ public final class FontLoader {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private FontLoader(List<FontInfo> fontList) {
|
private FontLoader(List<FontInfo> fontList, List<String> fallBackList) {
|
||||||
for (FontInfo info : fontList) {
|
for (FontInfo info : fontList) {
|
||||||
for (String family : info.families) {
|
for (String family : info.families) {
|
||||||
mFamilyToTtf.put(family, info.ttf);
|
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) {
|
public synchronized Font getFont(String family, int[] style) {
|
||||||
@@ -116,25 +141,25 @@ public final class FontLoader {
|
|||||||
|
|
||||||
// get the ttf name from the family
|
// get the ttf name from the family
|
||||||
String ttf = mFamilyToTtf.get(family);
|
String ttf = mFamilyToTtf.get(family);
|
||||||
|
|
||||||
if (ttf == null) {
|
if (ttf == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the font from the ttf
|
// get the font from the ttf
|
||||||
Map<Integer, Font> styleMap = mTtfToFontMap.get(ttf);
|
Map<Integer, Font> styleMap = mTtfToFontMap.get(ttf);
|
||||||
|
|
||||||
if (styleMap == null) {
|
if (styleMap == null) {
|
||||||
styleMap = new HashMap<Integer, Font>();
|
styleMap = new HashMap<Integer, Font>();
|
||||||
mTtfToFontMap.put(ttf, styleMap);
|
mTtfToFontMap.put(ttf, styleMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
Font f = styleMap.get(style);
|
Font f = styleMap.get(style);
|
||||||
|
|
||||||
if (f != null) {
|
if (f != null) {
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if it doesn't exist, we create it, and we can't, we try with a simpler style
|
// if it doesn't exist, we create it, and we can't, we try with a simpler style
|
||||||
switch (style[0]) {
|
switch (style[0]) {
|
||||||
case Typeface.NORMAL:
|
case Typeface.NORMAL:
|
||||||
@@ -178,7 +203,7 @@ public final class FontLoader {
|
|||||||
private Font getFont(String ttf, String[] fontFileSuffix) {
|
private Font getFont(String ttf, String[] fontFileSuffix) {
|
||||||
for (String suffix : fontFileSuffix) {
|
for (String suffix : fontFileSuffix) {
|
||||||
String name = ttf + suffix + FONT_EXT;
|
String name = ttf + suffix + FONT_EXT;
|
||||||
|
|
||||||
File f = new File(name);
|
File f = new File(name);
|
||||||
if (f.isFile()) {
|
if (f.isFile()) {
|
||||||
try {
|
try {
|
||||||
@@ -193,14 +218,14 @@ public final class FontLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static class FontInfo {
|
private final static class FontInfo {
|
||||||
String ttf;
|
String ttf;
|
||||||
final Set<String> families;
|
final Set<String> families;
|
||||||
|
|
||||||
FontInfo() {
|
FontInfo() {
|
||||||
families = new HashSet<String>();
|
families = new HashSet<String>();
|
||||||
}
|
}
|
||||||
@@ -208,19 +233,19 @@ public final class FontLoader {
|
|||||||
|
|
||||||
private final static class FontDefinitionParser extends DefaultHandler {
|
private final static class FontDefinitionParser extends DefaultHandler {
|
||||||
private final String mOsFontsLocation;
|
private final String mOsFontsLocation;
|
||||||
|
|
||||||
private int mDepth = 0;
|
|
||||||
private FontInfo mFontInfo = null;
|
private FontInfo mFontInfo = null;
|
||||||
private final StringBuilder mBuilder = new StringBuilder();
|
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) {
|
private FontDefinitionParser(String osFontsLocation) {
|
||||||
super();
|
super();
|
||||||
mOsFontsLocation = osFontsLocation;
|
mOsFontsLocation = osFontsLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
FontLoader getFontLoader() {
|
FontLoader getFontLoader() {
|
||||||
return new FontLoader(mFontList);
|
return new FontLoader(mFontList, mFallBackList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
@@ -229,10 +254,11 @@ public final class FontLoader {
|
|||||||
@Override
|
@Override
|
||||||
public void startElement(String uri, String localName, String name, Attributes attributes)
|
public void startElement(String uri, String localName, String name, Attributes attributes)
|
||||||
throws SAXException {
|
throws SAXException {
|
||||||
if (localName.equals(NODE_LEVEL[mDepth])) {
|
if (NODE_FONTS.equals(localName)) {
|
||||||
mDepth++;
|
mFontList = new ArrayList<FontInfo>();
|
||||||
|
mFallBackList = new ArrayList<String>();
|
||||||
if (mDepth == 2) { // font level.
|
} else if (NODE_FONT.equals(localName)) {
|
||||||
|
if (mFontList != null) {
|
||||||
String ttf = attributes.getValue(ATTR_TTF);
|
String ttf = attributes.getValue(ATTR_TTF);
|
||||||
if (ttf != null) {
|
if (ttf != null) {
|
||||||
mFontInfo = new FontInfo();
|
mFontInfo = new FontInfo();
|
||||||
@@ -240,42 +266,50 @@ public final class FontLoader {
|
|||||||
mFontList.add(mFontInfo);
|
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);
|
super.startElement(uri, localName, name, attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
|
* @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@Override
|
@Override
|
||||||
public void characters(char[] ch, int start, int length) throws SAXException {
|
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)
|
/* (non-Javadoc)
|
||||||
* @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
|
* @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unused")
|
|
||||||
@Override
|
@Override
|
||||||
public void endElement(String uri, String localName, String name) throws SAXException {
|
public void endElement(String uri, String localName, String name) throws SAXException {
|
||||||
if (localName.equals(NODE_LEVEL[mDepth-1])) {
|
if (NODE_FONTS.equals(localName)) {
|
||||||
mDepth--;
|
// top level, do nothing
|
||||||
if (mDepth == 2) { // end of a <name> node
|
} else if (NODE_FONT.equals(localName)) {
|
||||||
if (mFontInfo != null) {
|
mFontInfo = null;
|
||||||
String family = trimXmlWhitespaces(mBuilder.toString());
|
} else if (NODE_NAME.equals(localName)) {
|
||||||
mFontInfo.families.add(family);
|
// handle a new name for an existing Font Info
|
||||||
mBuilder.setLength(0);
|
if (mFontInfo != null) {
|
||||||
}
|
String family = trimXmlWhitespaces(mBuilder.toString());
|
||||||
} else if (mDepth == 1) { // end of a <font> node
|
mFontInfo.families.add(family);
|
||||||
mFontInfo = null;
|
|
||||||
}
|
}
|
||||||
|
} else if (NODE_FALLBACK.equals(localName)) {
|
||||||
|
// nothing to do here.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String trimXmlWhitespaces(String value) {
|
private String trimXmlWhitespaces(String value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
return 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.
|
// look for carriage return and replace all whitespace around it by just 1 space.
|
||||||
int index;
|
int index;
|
||||||
|
|
||||||
while ((index = value.indexOf('\n')) != -1) {
|
while ((index = value.indexOf('\n')) != -1) {
|
||||||
// look for whitespace on each side
|
// look for whitespace on each side
|
||||||
int left = index - 1;
|
int left = index - 1;
|
||||||
@@ -294,7 +328,7 @@ public final class FontLoader {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int right = index + 1;
|
int right = index + 1;
|
||||||
int count = value.length();
|
int count = value.length();
|
||||||
while (right < count) {
|
while (right < count) {
|
||||||
@@ -304,7 +338,7 @@ public final class FontLoader {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// remove all between left and right (non inclusive) and replace by a single space.
|
// remove all between left and right (non inclusive) and replace by a single space.
|
||||||
String leftString = null;
|
String leftString = null;
|
||||||
if (left >= 0) {
|
if (left >= 0) {
|
||||||
@@ -314,7 +348,7 @@ public final class FontLoader {
|
|||||||
if (right < count) {
|
if (right < count) {
|
||||||
rightString = value.substring(right);
|
rightString = value.substring(right);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leftString != null) {
|
if (leftString != null) {
|
||||||
value = leftString;
|
value = leftString;
|
||||||
if (rightString != null) {
|
if (rightString != null) {
|
||||||
@@ -324,24 +358,24 @@ public final class FontLoader {
|
|||||||
value = rightString != null ? rightString : "";
|
value = rightString != null ? rightString : "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// now we un-escape the string
|
// now we un-escape the string
|
||||||
int length = value.length();
|
int length = value.length();
|
||||||
char[] buffer = value.toCharArray();
|
char[] buffer = value.toCharArray();
|
||||||
|
|
||||||
for (int i = 0 ; i < length ; i++) {
|
for (int i = 0 ; i < length ; i++) {
|
||||||
if (buffer[i] == '\\') {
|
if (buffer[i] == '\\') {
|
||||||
if (buffer[i+1] == 'n') {
|
if (buffer[i+1] == 'n') {
|
||||||
// replace the char with \n
|
// replace the char with \n
|
||||||
buffer[i+1] = '\n';
|
buffer[i+1] = '\n';
|
||||||
}
|
}
|
||||||
|
|
||||||
// offset the rest of the buffer since we go from 2 to 1 char
|
// offset the rest of the buffer since we go from 2 to 1 char
|
||||||
System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
|
System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
|
||||||
length--;
|
length--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new String(buffer, 0, length);
|
return new String(buffer, 0, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user