host.anzo.commons.graphics.text.TextRenderer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of commons-core Show documentation
Show all versions of commons-core Show documentation
Commons library to make me happy.
package host.anzo.commons.graphics.text;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.awt.geom.Rectangle2D;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A class which provides static methods for rendering text using alignment.
*
* @author Chris Copeland
* @version 1.0
*/
public final class TextRenderer
{
/**
* Initialize a new instance of the {@link TextRenderer} class.
*/
private TextRenderer() {
}
/**
* Draws a string onto a Graphics
handle, using a Font
, Color
and target bounds to calculate
* the location and automatic wrapping of text. The align property determines where the text will be positioned.
*
* @param g A Graphics
handle which is the target of the draw operation
* @param text A String
containing the text to draw
* @param font The Font
to use when drawing the text
* @param color The Color
to use when drawing the text
* @param bounds A Rectangle
representing the bounds of the text
* @return A Rectangle
representing the bounds consumed by the text
*/
public static Rectangle drawString(Graphics g, String text, Font font, Color color, Rectangle bounds) {
return drawString(g, text, font, color, bounds, TextAlignment.TOP_LEFT, TextFormat.NONE);
}
/**
* Draws a string onto a Graphics
handle, using a Font
, Color
and target bounds to calculate
* the location and automatic wrapping of text. The align property determines where the text will be positioned.
*
* @param g A Graphics
handle which is the target of the draw operation
* @param text A String
containing the text to draw
* @param font The Font
to use when drawing the text
* @param color The Color
to use when drawing the text
* @param bounds A Rectangle
representing the bounds of the text
* @param align A TextAlignment
value representing the location to draw the text, relative to the bounds
* @return A Rectangle
representing the bounds consumed by the text
*/
public static Rectangle drawString(Graphics g, String text, Font font, Color color, Rectangle bounds, TextAlignment align) {
return drawString(g, text, font, color, bounds, align, TextFormat.NONE);
}
/**
* Draws a string onto a Graphics
handle, using a Font
, Color
and target bounds to calculate
* the location and automatic wrapping of text. The align property determines where the text will be positioned.
*
* @param g A Graphics
handle which is the target of the draw operation
* @param text A String
containing the text to draw
* @param font The Font
to use when drawing the text
* @param color The Color
to use when drawing the text
* @param bounds A Rectangle
representing the bounds of the text
* @param align A TextAlignment
value representing the location to draw the text, relative to the bounds
* @param format Additional formatting flags to use when drawing (see TextFormat
class)
* @return A Rectangle
representing the bounds consumed by the text
*/
public static Rectangle drawString(Graphics g, String text, Font font, Color color, Rectangle bounds, TextAlignment align, int format)
{
if (g == null)
throw new NullPointerException("The graphics handle cannot be null.");
if (text == null)
throw new NullPointerException("The text cannot be null.");
if (font == null)
throw new NullPointerException("The font cannot be null.");
if (color == null)
throw new NullPointerException("The text color cannot be null.");
if (bounds == null)
throw new NullPointerException("The text bounds cannot be null.");
if (align == null)
throw new NullPointerException("The text alignment cannot be null.");
if (text.length() == 0)
return new Rectangle(bounds.x, bounds.y, 0, 0);
Graphics2D g2D = (Graphics2D)g;
final AttributedString attributedString = new AttributedString(text);
attributedString.addAttribute(TextAttribute.FOREGROUND, color);
attributedString.addAttribute(TextAttribute.FONT, font);
if (text.contains("[") && text.contains("]")) {
final Pattern pattern = Pattern.compile("\\[(.*?)\\]");
final Matcher matcher = pattern.matcher(text);
while(matcher.find()) {
attributedString.addAttribute(TextAttribute.FOREGROUND, Color.decode("#8FDF5F"), matcher.start(), matcher.end());
}
}
// TODO: Implement Markdown
/*else if (text.contains("***")) {
final Pattern pattern = Pattern.compile("\\*\\*\\*(.*?)\\*\\*\\*");
final Matcher matcher = pattern.matcher(text);
while(matcher.find()) {
attributedString.addAttribute(TextAttribute.WEIGHT, TextAttribute.WEIGHT_EXTRABOLD, matcher.start(), matcher.end());
}
}*/
AttributedCharacterIterator attributedCharIterator = attributedString.getIterator();
FontRenderContext fontContext = new FontRenderContext(null, !TextFormat.isEnabled(format, TextFormat.NO_ANTI_ALIASING), false);
LineBreakMeasurer lineMeasurer = new LineBreakMeasurer(attributedCharIterator, fontContext);
Point targetLocation = new Point(bounds.x, bounds.y);
int nextOffset = 0;
if (align.isMiddle() || align.isBottom())
{
if (align.isMiddle())
targetLocation.y = bounds.y + (bounds.height / 2);
if (align.isBottom())
targetLocation.y = bounds.y + bounds.height;
while (lineMeasurer.getPosition() < text.length())
{
nextOffset = lineMeasurer.nextOffset(bounds.width);
nextOffset = nextTextIndex(nextOffset, lineMeasurer.getPosition(), text);
TextLayout textLayout = lineMeasurer.nextLayout(bounds.width, nextOffset, false);
if (align.isMiddle())
targetLocation.y -= (textLayout.getAscent() + textLayout.getLeading() + textLayout.getDescent()) / 2;
if (align.isBottom())
targetLocation.y -= (textLayout.getAscent() + textLayout.getLeading() + textLayout.getDescent());
}
if (TextFormat.isEnabled(format, TextFormat.FIRST_LINE_VISIBLE))
targetLocation.y = Math.max(0, targetLocation.y);
lineMeasurer.setPosition(0);
}
if (align.isRight() || align.isCenter())
targetLocation.x = bounds.x + bounds.width;
Rectangle consumedBounds = new Rectangle(targetLocation.x, targetLocation.y, 0, 0);
while (lineMeasurer.getPosition() < text.length())
{
nextOffset = lineMeasurer.nextOffset(bounds.width);
nextOffset = nextTextIndex(nextOffset, lineMeasurer.getPosition(), text);
TextLayout textLayout = lineMeasurer.nextLayout(bounds.width, nextOffset, false);
Rectangle2D textBounds = textLayout.getBounds();
targetLocation.y += textLayout.getAscent();
consumedBounds.width = Math.max(consumedBounds.width, (int)textBounds.getWidth());
switch (align)
{
case TOP_LEFT:
case MIDDLE_LEFT:
case BOTTOM_LEFT:
textLayout.draw(g2D, targetLocation.x, targetLocation.y);
break;
case TOP:
case MIDDLE:
case BOTTOM:
targetLocation.x = bounds.x + (bounds.width / 2) - (int)(textBounds.getWidth() / 2);
consumedBounds.x = Math.min(consumedBounds.x, targetLocation.x);
textLayout.draw(g2D, targetLocation.x, targetLocation.y);
break;
case TOP_RIGHT:
case MIDDLE_RIGHT:
case BOTTOM_RIGHT:
targetLocation.x = bounds.x + bounds.width - (int)textBounds.getWidth();
textLayout.draw(g2D, targetLocation.x, targetLocation.y);
consumedBounds.x = Math.min(consumedBounds.x, targetLocation.x);
break;
}
targetLocation.y += textLayout.getLeading() + textLayout.getDescent();
}
consumedBounds.height = targetLocation.y - consumedBounds.y;
return consumedBounds;
}
/**
* Calculates the next maximum index of the string that will be displayed.
*
* @param nextOffset The index calculated using a LineBreakMeasurer
nextOffset method
* @param measurerPosition The position within a LineBreakMeasurer
* @param text The text being rendered
* @return The next maximum index within the string
*/
private static int nextTextIndex(int nextOffset, int measurerPosition, String text)
{
for (int i = measurerPosition + 1; i < nextOffset; ++i)
{
if (text.charAt(i) == '\n')
return i;
}
return nextOffset;
}
}