![JAR search and dependency download from the Maven repository](/logo.png)
playn.android.AndroidTextLayout Maven / Gradle / Ivy
/**
* Copyright 2013 The PlayN Authors
*
* 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 playn.android;
import java.util.ArrayList;
import java.util.List;
import pythagoras.f.Rectangle;
import playn.core.TextFormat;
import playn.core.TextLayout;
import playn.core.TextWrap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
class AndroidTextLayout extends TextLayout {
private final AndroidFont font;
private final Paint.FontMetrics metrics;
public static TextLayout layoutText(AndroidGraphics gfx, String text, TextFormat format) {
AndroidFont font = gfx.resolveFont(format.font);
Paint paint = new Paint(format.antialias ? Paint.ANTI_ALIAS_FLAG : 0);
paint.setTypeface(font.typeface);
paint.setTextSize(font.size);
paint.setSubpixelText(true);
paint.setLinearText(true);
Paint.FontMetrics metrics = paint.getFontMetrics();
return new AndroidTextLayout(text, format, font, metrics, paint.measureText(text));
}
public static TextLayout[] layoutText(AndroidGraphics gfx, String text, TextFormat format,
TextWrap wrap) {
AndroidFont font = gfx.resolveFont(format.font);
Paint paint = new Paint(format.antialias ? Paint.ANTI_ALIAS_FLAG : 0);
paint.setTypeface(font.typeface);
paint.setTextSize(font.size);
paint.setSubpixelText(true);
paint.setLinearText(true);
Paint.FontMetrics metrics = paint.getFontMetrics();
List layouts = new ArrayList();
float[] measuredWidth = new float[1];
for (String ltext : normalizeEOL(text).split("\\n")) {
// if we're only wrapping on newlines, then just add the whole line now
if (wrap.width <= 0 || wrap.width == Float.MAX_VALUE) {
layouts.add(new AndroidTextLayout(ltext, format, font, metrics, paint.measureText(ltext)));
} else {
int start = 0, end = ltext.length();
while (start < end) {
// breakText only breaks on characters; we want to break on word boundaries
int count = paint.breakText(ltext, start, end, true, wrap.width, measuredWidth);
// breakText exhibits a bug where ligaturized text sequences (e.g. "fi") are counted as a
// single character in the returned count when in reality they consume multiple
// characters of the source text; so we use a hacky table of known ligatures for the font
// in question to adjust the count if the text passed to breakText contains any known
// ligatures
int lineEnd = start+count;
if (lineEnd < end && font.ligatureHacks.length > 0) {
int adjust = accountForLigatures(ltext, start, count, font.ligatureHacks);
count += adjust;
lineEnd += adjust;
}
// if we matched the rest of the line, things are simple
if (lineEnd == end) {
layouts.add(new AndroidTextLayout(ltext.substring(start, lineEnd), format, font,
metrics, measuredWidth[0]));
start += count;
} else {
// if we ended in the middle of a word, back up until we hit whitespace
if (!Character.isWhitespace(ltext.charAt(lineEnd-1)) &&
!Character.isWhitespace(ltext.charAt(lineEnd))) {
do {
--lineEnd;
} while (lineEnd > start && !Character.isWhitespace(ltext.charAt(lineEnd)));
}
// if there is no whitespace on the line, then we hard-break in the middle of the word
if (lineEnd == start) {
layouts.add(new AndroidTextLayout(ltext.substring(start, start+count), format, font,
metrics, measuredWidth[0]));
start += count;
} else {
// otherwise we're now positioned on some sort of whitespace; trim it
while (Character.isWhitespace(ltext.charAt(lineEnd-1))) {
--lineEnd;
}
String line = ltext.substring(start, lineEnd);
float size = paint.measureText(line);
layouts.add(new AndroidTextLayout(line, format, font, metrics, size));
start = lineEnd;
}
// now trim any whitespace from start to the first non-whitespace character
while (start < end && Character.isWhitespace(ltext.charAt(start))) {
start++;
}
}
}
}
}
return layouts.toArray(new TextLayout[layouts.size()]);
}
@Override public float ascent() { return -metrics.ascent; }
@Override public float descent() { return metrics.descent; }
@Override public float leading() { return metrics.leading; }
AndroidTextLayout(String text, TextFormat format, AndroidFont font, Paint.FontMetrics metrics,
float width) {
this(text, format, font, metrics, width, -metrics.ascent+metrics.descent);
}
AndroidTextLayout(String text, TextFormat format, AndroidFont font, Paint.FontMetrics metrics,
float width, float height) {
// Android doesn't provide a way to get precise text bounds, so we half-ass it, woo!
super(text, format, new Rectangle(0, 0, width, height), height);
this.font = font;
this.metrics = metrics;
}
void draw(Canvas canvas, float x, float y, Paint paint) {
boolean oldAA = paint.isAntiAlias();
paint.setAntiAlias(format.antialias);
try {
paint.setTypeface(font.typeface);
paint.setTextSize(font.size);
paint.setSubpixelText(true);
paint.setLinearText(true);
// if we're drawing REALLY BIG TEXT, draw to a path rather than directly drawing text to work
// around KitKat bug: https://code.google.com/p/android/issues/detail?id=62800
if (font.size > 250) {
Path path = new Path();
paint.getTextPath(text, 0, text.length(), x, y - metrics.ascent, path);
canvas.drawPath(path, paint);
} else {
canvas.drawText(text, x, y-metrics.ascent, paint);
}
} finally {
paint.setAntiAlias(oldAA);
}
}
static int accountForLigatures (String text, int start, int count, String[] ligatures) {
int adjust = 0;
for (String lig : ligatures) {
// for every instance of this ligature, add its extra characters to the adjustment
int llen = lig.length(), idx = start;
while ((idx = text.indexOf(lig, idx)) != -1) {
if (idx+1 > start+count) break;
int extra = llen-1;
adjust += extra;
count += extra;
idx += llen;
}
}
return adjust;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy