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    }