Skip to content
118 changes: 72 additions & 46 deletions src/main/java/org/bukkit/craftbukkit/TextWrapper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.bukkit.craftbukkit;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class TextWrapper {
private static final int[] characterWidths = new int[] {
1, 9, 9, 8, 8, 8, 8, 7, 9, 8, 9, 9, 8, 9, 9, 9,
Expand All @@ -20,70 +23,93 @@ public class TextWrapper {
7, 7, 7, 7, 9, 6, 7, 8, 7, 6, 6, 9, 7, 6, 7, 1
};
public static final char COLOR_CHAR = '\u00A7';
private static final char TEMP_CHAR = '\u03d5';
public static final int CHAT_WINDOW_WIDTH = 320;
public static final int CHAT_STRING_LENGTH = 119;
public static final String allowedChars = net.minecraft.server.FontAllowedCharacters.allowedCharacters;

public static String[] wrapText(final String text) {
final StringBuilder out = new StringBuilder();
char colorChar = 'f';
int lineWidth = 0;
int lineLength = 0;
int lineWidth = 0;
int tokenWidth;
int tokenLength;
// tokens are just individual words in the message
String[] tokens = text.split(" ");
Pattern p = Pattern.compile("(" + COLOR_CHAR + "[0-9a-fA-F])+");
Pattern p2 = Pattern.compile(".*(" + COLOR_CHAR + "[0-9A-Fa-f])+.*$"); // to get just the last occurrence of color format in token
String temp;
for (int i = 0; i < tokens.length; i++) {
// append space to token unless it is last token
if (i == tokens.length - 1) {
temp = tokens[i];
} else {
temp = tokens[i] + " ";
}

// Go over the message char by char.
for (int i = 0; i < text.length(); i++) {
char ch = text.charAt(i);
boolean containsColorCodes;

// Get the color
if (ch == COLOR_CHAR && i < text.length() - 1) {
// We might need a linebreak ... so ugly ;(
if (lineLength + 2 > CHAT_STRING_LENGTH) {
out.append('\n');
lineLength = 0;
if (colorChar != 'f' && colorChar != 'F') {
out.append(COLOR_CHAR).append(colorChar);
lineLength += 2;
}
}
colorChar = text.charAt(++i);
out.append(COLOR_CHAR).append(colorChar);
lineLength += 2;
continue;
}
tokenLength = temp.length();
lineLength += tokenLength;
tokenWidth = widthInPixels(temp);
lineWidth += tokenWidth;
Matcher m = p2.matcher(tokens[i]);

// Figure out if it's allowed
int index = allowedChars.indexOf(ch);
if (index == -1) {
// Invalid character .. skip it.
continue;
} else {
// Sadly needed as the allowedChars string misses the first
index += 32;
}
String lastColorChar = m.matches() ? m.group(1) : COLOR_CHAR + "f";

// Find the width
final int width = characterWidths[index];
if (tokenLength >= CHAT_STRING_LENGTH || tokenWidth >= CHAT_WINDOW_WIDTH) {
// this token is too big for one line so split it

// See if we need a linebreak
if (lineLength + 1 > CHAT_STRING_LENGTH || lineWidth + width >= CHAT_WINDOW_WIDTH) {
out.append('\n');
// reset the line accumulators since we will be taking up the whole line
lineWidth = 0;
lineLength = 0;

// Re-apply the last color if it isn't the default
if (colorChar != 'f' && colorChar != 'F') {
out.append(COLOR_CHAR).append(colorChar);
lineLength += 2;
for (int j = 0; j < temp.length(); j++) {
// color format checking
if (j < temp.length() - 1 && p.matcher(temp.substring(j, j + 1)).matches()) {
// we have a color format, so increment length by 2 but this will not contribute to width
lineLength += 2;
containsColorCodes = true;
} else {
// no color format, so normal increments
lineWidth += characterWidths[temp.charAt(j)];
lineLength++;
containsColorCodes = false;
}

if (lineLength >= CHAT_STRING_LENGTH || lineWidth >= CHAT_WINDOW_WIDTH) {
out.append(TEMP_CHAR);
out.append(lastColorChar);
lineLength = 2;
lineWidth = 0;

if (containsColorCodes) {
// we need to move both the color char and the color specifier to the next line
j -= 2;
} else {
// not a color format, so just move one char to the next line
j--;
}
continue;
}
out.append(temp.charAt(j));
}
lineWidth = width;
} else {
lineWidth += width;
continue;
}
// we have a smaller token than the max size so wrap based on token instead of char
if (lineLength >= CHAT_STRING_LENGTH || lineWidth >= CHAT_WINDOW_WIDTH) {
// try again on a new line
out.append(TEMP_CHAR);
out.append(lastColorChar);
lineWidth = 2;
lineLength = 0;
i--;
continue;
}
out.append(ch);
lineLength++;
out.append(temp);
}

// Return it split
return out.toString().split("\n");
return out.toString().split(String.valueOf(TEMP_CHAR));
}

/**
Expand Down