package net.databinder.components; import java.awt.Color; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Point; import java.awt.RenderingHints; import java.text.AttributedCharacterIterator; import java.text.AttributedString; import java.util.ArrayList; import java.util.List; import org.apache.batik.gvt.TextNode; import org.apache.batik.gvt.font.AWTGVTFont; import org.apache.batik.gvt.font.GVTFont; import org.apache.batik.gvt.renderer.StrokingTextPainter; import org.apache.batik.gvt.text.TextPaintInfo; import org.apache.wicket.model.IModel; import org.apache.wicket.util.string.Strings; /** * Renders text without font hinting, which can be better for some fonts * at larger sizes. The standard Java pipeline does not allow hinting to * be disabled; Batik does. * @author Nathan Hamblen */ public class BatikRenderedLabel extends RenderedLabel { public BatikRenderedLabel(String id) { super(id); } public BatikRenderedLabel(String id, boolean shareResource) { super(id, shareResource); } public BatikRenderedLabel(String id, IModel model) { super(id, model); } public BatikRenderedLabel(String id, IModel model, boolean shareResource) { super(id, model, shareResource); } public static void loadSharedResources(String text, Font font, Color color, Color backgroundColor, Integer maxWidth) { loadSharedResources(new BatikRenderedTextImageResource(), text, font, color, backgroundColor, maxWidth); } @Override protected RenderedTextImageResource newRenderedTextImageResource(boolean isShared) { RenderedTextImageResource res = new BatikRenderedTextImageResource(); res.setCacheable(isShared); res.setState(this); return res; } protected static class BatikRenderedTextImageResource extends RenderedTextImageResource { protected List<AttributedCharacterIterator> getAttributedLines() { if (Strings.isEmpty(text)) return null; AttributedString attributedText = new AttributedString(text); List<GVTFont> fonts = new ArrayList<GVTFont>(1); fonts.add(new AWTGVTFont(font)); attributedText.addAttribute(StrokingTextPainter.GVT_FONTS, fonts); TextPaintInfo tpi = new TextPaintInfo(); tpi.visible = true; tpi.fillPaint = color; attributedText.addAttribute(StrokingTextPainter.PAINT_INFO, tpi); return splitAtNewlines(attributedText, text); } @Override protected boolean render(Graphics2D graphics) { final int width = getWidth(), height = getHeight(); // draw background if not null, otherwise leave transparent if (backgroundColor != null) { graphics.setColor(backgroundColor); graphics.fillRect(0, 0, width, height); } // render as a 1x1 pixel if text is empty if (Strings.isEmpty(text)) { if (width == 1 && height == 1) return true; setWidth(1); setHeight(1); return false; } // Get size of text graphics.setFont(font); final FontMetrics fontMetrics = graphics.getFontMetrics(); List<AttributedCharacterIterator> attributedLines = getAttributedLines(); // each one of these is needed for a unhinted, anti-aliased display graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON); graphics.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); // TODO: maxwidth wrapping layout, format string processing float lineHeight = fontMetrics.getHeight(), spare = fontMetrics.getMaxAscent() - fontMetrics.getAscent() + fontMetrics.getMaxDescent() - fontMetrics.getDescent(), neededHeight = attributedLines.size() * lineHeight + spare, neededWidth = 0f, y = fontMetrics.getMaxAscent(); for (AttributedCharacterIterator line : attributedLines) { TextNode node = new TextNode(); node.setLocation(new Point(0, (int) y)); node.setAttributedCharacterIterator(line); node.getTextPainter().paint(node, graphics); float w = (float) node.getTextPainter().getBounds2D(node).getWidth() + 4f; if (w > neededWidth) neededWidth = w; y += lineHeight; } if (neededWidth > width || neededHeight > height) { setWidth((int)Math.ceil(neededWidth)); setHeight((int)Math.ceil(neededHeight)); return false; } return true; } } }