package loon.font; import loon.HorizontalAlign; import loon.canvas.LColor; import loon.opengl.GLEx; import loon.utils.StringUtils; import loon.utils.TArray; public class FontUtils { private static final int UNSPECIFIED = -1; public static void drawLeft(GLEx g, String s, int x, int y) { drawString(g, g.getFont(), s, HorizontalAlign.LEFT, x, y, 0, LColor.white); } public static void drawLeft(GLEx g, IFont font, String s, int x, int y) { drawString(g, font, s, HorizontalAlign.LEFT, x, y, 0, LColor.white); } public static void drawCenter(GLEx g, String s, int x, int y, int width) { drawString(g, g.getFont(), s, HorizontalAlign.CENTER, x, y, width, LColor.white); } public static void drawCenter(GLEx g, IFont font, String s, int x, int y, int width) { drawString(g, font, s, HorizontalAlign.CENTER, x, y, width, LColor.white); } public static void drawCenter(GLEx g, String s, int x, int y, int width, LColor color) { drawString(g, g.getFont(), s, HorizontalAlign.CENTER, x, y, width, color); } public static void drawCenter(GLEx g, IFont font, String s, int x, int y, int width, LColor color) { drawString(g, font, s, HorizontalAlign.CENTER, x, y, width, color); } public static void drawRight(GLEx g, String s, int x, int y, int width) { drawString(g, g.getFont(), s, HorizontalAlign.RIGHT, x, y, width, LColor.white); } public static void drawRight(GLEx g, IFont font, String s, int x, int y, int width) { drawString(g, font, s, HorizontalAlign.RIGHT, x, y, width, LColor.white); } public static void drawRight(GLEx g, String s, int x, int y, int width, LColor color) { drawString(g, g.getFont(), s, HorizontalAlign.RIGHT, x, y, width, color); } public static void drawRight(GLEx g, IFont font, String s, int x, int y, int width, LColor color) { drawString(g, font, s, HorizontalAlign.RIGHT, x, y, width, color); } public static final void drawString(GLEx g, IFont font, final String s, final HorizontalAlign align, final int x, final int y, final int width, LColor color) { if (align == HorizontalAlign.LEFT) { font.drawString(g, s, x, y, color); } else if (align == HorizontalAlign.CENTER) { font.drawString(g, s, x + (width / 2) - (font.stringWidth(s) / 2), y, color); } else if (align == HorizontalAlign.RIGHT) { font.drawString(g, s, x + width - font.stringWidth(s), y, color); } } public static float measureText(final IFont font, final CharSequence chars) { return FontUtils.measureText(font, chars, 0, chars.length()); } public static float measureText(final IFont font, final CharSequence chars, final int start, final int end) { final int textLength = end - start; if (textLength <= 0) { return 0; } else if (textLength == 1) { return font.charWidth(chars.charAt(start)); } if (chars instanceof String) { return font.stringWidth(((String) chars).substring(start, end).toString()); } else { return font.stringWidth(new StringBuffer(chars).substring(start, end).toString()); } } public static <T extends TArray<CharSequence>> T splitLines(final CharSequence chars, final T result) { return StringUtils.splitArray(chars, '\n', result); } public static <T extends TArray<CharSequence>> T splitLines(final IFont font, final CharSequence chars, final T result, final AutoWrap autoWrap, final float autoWrapWidth) { switch (autoWrap) { case VERTICAL: return FontUtils.splitLinesByLetters(font, chars, result, autoWrapWidth); case HORIZONTAL: return FontUtils.splitLinesByWords(font, chars, result, autoWrapWidth); default: case NONE: case CJK: return FontUtils.splitLinesByCJK(font, chars, result, autoWrapWidth); } } private static <T extends TArray<CharSequence>> T splitLinesByLetters(final IFont font, final CharSequence chars, final T result, final float autoWrapWidth) { final int textLength = chars.length(); int lineStart = 0; int lineEnd = 0; int lastNonWhitespace = 0; boolean charsAvailable = false; for (int i = 0; i < textLength; i++) { final char character = chars.charAt(i); if (character != ' ') { if (charsAvailable) { lastNonWhitespace = i + 1; } else { charsAvailable = true; lineStart = i; lastNonWhitespace = lineStart + 1; lineEnd = lastNonWhitespace; } } if (charsAvailable) { final float lookaheadLineWidth = FontUtils.measureText(font, chars, lineStart, lastNonWhitespace); final boolean isEndReached = (i == (textLength - 1)); if (isEndReached) { if (lookaheadLineWidth <= autoWrapWidth) { result.add(chars.subSequence(lineStart, lastNonWhitespace)); } else { result.add(chars.subSequence(lineStart, lineEnd)); if (lineStart != i) { result.add(chars.subSequence(i, lastNonWhitespace)); } } } else { if (lookaheadLineWidth <= autoWrapWidth) { lineEnd = lastNonWhitespace; } else { result.add(chars.subSequence(lineStart, lineEnd)); i = lineEnd - 1; charsAvailable = false; } } } } return result; } private static <T extends TArray<CharSequence>> T splitLinesByWords(final IFont font, final CharSequence chars, final T result, final float autoWrapWidth) { final int textLength = chars.length(); if (textLength == 0) { return result; } final float spaceWidth = font.charWidth(' '); int lastWordEnd = FontUtils.UNSPECIFIED; int lineStart = FontUtils.UNSPECIFIED; int lineEnd = FontUtils.UNSPECIFIED; float lineWidthRemaining = autoWrapWidth; boolean firstWordInLine = true; int i = 0; while (i < textLength) { int spacesSkipped = 0; while ((i < textLength) && (chars.charAt(i) == ' ')) { i++; spacesSkipped++; } final int wordStart = i; if (lineStart == FontUtils.UNSPECIFIED) { lineStart = wordStart; } while ((i < textLength) && (chars.charAt(i) != ' ')) { i++; } final int wordEnd = i; if (wordStart == wordEnd) { if (!firstWordInLine) { result.add(chars.subSequence(lineStart, lineEnd)); } break; } final float wordWidth = FontUtils.measureText(font, chars, wordStart, wordEnd); final float widthNeeded; if (firstWordInLine) { widthNeeded = wordWidth; } else { widthNeeded = (spacesSkipped * spaceWidth) + wordWidth; } if (widthNeeded <= lineWidthRemaining) { if (firstWordInLine) { firstWordInLine = false; } else { lineWidthRemaining -= FontUtils.getAdvance(font, chars, lastWordEnd - 1); } lineWidthRemaining -= widthNeeded; lastWordEnd = wordEnd; lineEnd = wordEnd; if (wordEnd == textLength) { result.add(chars.subSequence(lineStart, lineEnd)); break; } } else { if (firstWordInLine) { if (wordWidth >= autoWrapWidth) { result.add(chars.subSequence(wordStart, wordEnd)); lineWidthRemaining = autoWrapWidth; } else { lineWidthRemaining = autoWrapWidth - wordWidth; if (wordEnd == textLength) { result.add(chars.subSequence(wordStart, wordEnd)); break; } } firstWordInLine = true; lastWordEnd = FontUtils.UNSPECIFIED; lineStart = FontUtils.UNSPECIFIED; lineEnd = FontUtils.UNSPECIFIED; } else { result.add(chars.subSequence(lineStart, lineEnd)); if (wordEnd == textLength) { result.add(chars.subSequence(wordStart, wordEnd)); break; } else { lineWidthRemaining = autoWrapWidth - wordWidth; firstWordInLine = false; lastWordEnd = wordEnd; lineStart = wordStart; lineEnd = wordEnd; } } } } return result; } private static <T extends TArray<CharSequence>> T splitLinesByCJK(final IFont font, final CharSequence chars, final T result, final float autoWrapWidth) { final int textLength = chars.length(); int lineStart = 0; int lineEnd = 0; while ((lineStart < textLength) && (chars.charAt(lineStart) == ' ')) { lineStart++; lineEnd++; } int i = lineEnd; while (i < textLength) { lineStart = lineEnd; boolean charsAvailable = true; while (i < textLength) { int j = lineEnd; while (j < textLength) { if (chars.charAt(j) == ' ') { j++; } else { break; } } if (j == textLength) { if (lineStart == lineEnd) { charsAvailable = false; } i = textLength; break; } lineEnd++; final float lineWidth = FontUtils.measureText(font, chars, lineStart, lineEnd); if (lineWidth > autoWrapWidth) { if (lineStart < lineEnd - 1) { lineEnd--; } result.add(chars.subSequence(lineStart, lineEnd)); charsAvailable = false; i = lineEnd; break; } i = lineEnd; } if (charsAvailable) { result.add(chars.subSequence(lineStart, lineEnd)); } } return result; } private static float getAdvance(final IFont font, final CharSequence chars, final int idx) { return -font.charWidth(chars.charAt(idx)); } }