001 package net.databinder.components;
002
003 import java.awt.Color;
004 import java.awt.Font;
005 import java.awt.FontMetrics;
006 import java.awt.Graphics2D;
007 import java.awt.Point;
008 import java.awt.RenderingHints;
009 import java.text.AttributedCharacterIterator;
010 import java.text.AttributedString;
011 import java.util.ArrayList;
012 import java.util.List;
013
014 import org.apache.batik.gvt.TextNode;
015 import org.apache.batik.gvt.font.AWTGVTFont;
016 import org.apache.batik.gvt.font.GVTFont;
017 import org.apache.batik.gvt.renderer.StrokingTextPainter;
018 import org.apache.batik.gvt.text.TextPaintInfo;
019 import org.apache.wicket.model.IModel;
020 import org.apache.wicket.util.string.Strings;
021
022 /**
023 * Renders text without font hinting, which can be better for some fonts
024 * at larger sizes. The standard Java pipeline does not allow hinting to
025 * be disabled; Batik does.
026 * @author Nathan Hamblen
027 */
028 public class BatikRenderedLabel extends RenderedLabel {
029 public BatikRenderedLabel(String id) {
030 super(id);
031 }
032 public BatikRenderedLabel(String id, boolean shareResource) {
033 super(id, shareResource);
034 }
035 public BatikRenderedLabel(String id, IModel model) {
036 super(id, model);
037 }
038 public BatikRenderedLabel(String id, IModel model, boolean shareResource) {
039 super(id, model, shareResource);
040 }
041
042
043 public static void loadSharedResources(String text, Font font, Color color, Color backgroundColor, Integer maxWidth) {
044 loadSharedResources(new BatikRenderedTextImageResource(), text, font, color, backgroundColor, maxWidth);
045 }
046
047 @Override
048 protected RenderedTextImageResource newRenderedTextImageResource(boolean isShared) {
049 RenderedTextImageResource res = new BatikRenderedTextImageResource();
050 res.setCacheable(isShared);
051 res.setState(this);
052 return res;
053 }
054
055
056 protected static class BatikRenderedTextImageResource extends RenderedTextImageResource {
057
058 protected List<AttributedCharacterIterator> getAttributedLines() {
059 if (Strings.isEmpty(text))
060 return null;
061 AttributedString attributedText = new AttributedString(text);
062
063 List<GVTFont> fonts = new ArrayList<GVTFont>(1);
064 fonts.add(new AWTGVTFont(font));
065 attributedText.addAttribute(StrokingTextPainter.GVT_FONTS, fonts);
066
067 TextPaintInfo tpi = new TextPaintInfo();
068 tpi.visible = true;
069 tpi.fillPaint = color;
070 attributedText.addAttribute(StrokingTextPainter.PAINT_INFO, tpi);
071
072 return splitAtNewlines(attributedText, text);
073 }
074
075 @Override
076 protected boolean render(Graphics2D graphics) {
077 final int width = getWidth(), height = getHeight();
078
079 // draw background if not null, otherwise leave transparent
080 if (backgroundColor != null) {
081 graphics.setColor(backgroundColor);
082 graphics.fillRect(0, 0, width, height);
083 }
084
085 // render as a 1x1 pixel if text is empty
086 if (Strings.isEmpty(text)) {
087 if (width == 1 && height == 1)
088 return true;
089 setWidth(1);
090 setHeight(1);
091 return false;
092 }
093
094 // Get size of text
095 graphics.setFont(font);
096 final FontMetrics fontMetrics = graphics.getFontMetrics();
097
098 List<AttributedCharacterIterator> attributedLines = getAttributedLines();
099
100 // each one of these is needed for a unhinted, anti-aliased display
101 graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
102 RenderingHints.VALUE_ANTIALIAS_ON);
103 graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
104 RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
105 graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
106 RenderingHints.VALUE_STROKE_PURE);
107
108 // TODO: maxwidth wrapping layout, format string processing
109
110 float lineHeight = fontMetrics.getHeight(),
111 spare = fontMetrics.getMaxAscent() - fontMetrics.getAscent()
112 + fontMetrics.getMaxDescent() - fontMetrics.getDescent(),
113 neededHeight = attributedLines.size() * lineHeight + spare,
114 neededWidth = 0f,
115 y = fontMetrics.getMaxAscent();
116
117 for (AttributedCharacterIterator line : attributedLines) {
118 TextNode node = new TextNode();
119 node.setLocation(new Point(0, (int) y));
120 node.setAttributedCharacterIterator(line);
121 node.getTextPainter().paint(node, graphics);
122
123 float w = (float) node.getTextPainter().getBounds2D(node).getWidth() + 4f;
124 if (w > neededWidth)
125 neededWidth = w;
126
127 y += lineHeight;
128 }
129 if (neededWidth > width || neededHeight > height) {
130 setWidth((int)Math.ceil(neededWidth));
131 setHeight((int)Math.ceil(neededHeight));
132 return false;
133 }
134
135 return true;
136 }
137 }
138
139 }