/** * This file Copyright (c) 2012 Magnolia International * Ltd. (http://www.magnolia-cms.com). All rights reserved. * * * This file is dual-licensed under both the Magnolia * Network Agreement and the GNU General Public License. * You may elect to use one or the other of these licenses. * * This file is distributed in the hope that it will be * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the * implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT. * Redistribution, except as permitted by whichever of the GPL * or MNA you select, is prohibited. * * 1. For the GPL license (GPL), you can redistribute and/or * modify this file under the terms of the GNU General * Public License, Version 3, as published by the Free Software * Foundation. You should have received a copy of the GNU * General Public License, Version 3 along with this program; * if not, write to the Free Software Foundation, Inc., 51 * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * 2. For the Magnolia Network Agreement (MNA), this file * and the accompanying materials are made available under the * terms of the MNA which accompanies this distribution, and * is available at http://www.magnolia-cms.com/mna.html * * Any modifications to this file must keep this entire header * intact. * */ package info.magnolia.templating.jsp.taglib; import java.awt.Color; import java.awt.Font; import java.awt.FontFormatException; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.font.FontRenderContext; import java.awt.font.TextLayout; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.RenderedImage; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import javax.imageio.ImageIO; /** * Class to generate PNG images from TrueType font strings. Originally by Philip McCarthy - http://chimpen.com * http://chimpen.com/things/archives/001139.php * @author Philip McCarthy * @author Patrick Janssen */ public class Text2PngFactory { /** Font name. */ private String fontname; /** Font size. */ private int fontsize; /** Text to render. */ private String text = ""; /** Text color. */ private int r = 0; private int g = 0; private int b = 0; /** Background colour. */ private int br = 0xff; private int bg = 0xff; private int bb = 0xff; /** Used to obtain fontmetrics for given fontname. */ private Graphics2D g2; /** Cached Font object. */ private Font cachedFont; /** * Construct factory without setting font. */ public Text2PngFactory() { // Create a single-pixel buffered image to do font stuff with this.g2 = new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR).createGraphics(); // Set same hints as used for final render setOptimalRenderQuality(this.g2); } /** * Construct factory with given font face and size. * @param fontname Name of TrueType font * @param fontsize Point size of font * @throws IOException if font can't be loaded * @throws FontFormatException if font is not a valid TTF */ public Text2PngFactory(String fontname, int fontsize) throws IOException, FontFormatException { this(fontname, fontsize, ""); } /** * Construct factory with given font face and size. * @param fontname Name of TrueType font * @param fontsize Point size of font * @param text The text to render * @throws IOException if font can't be loaded * @throws FontFormatException if font is not a valid TTF */ public Text2PngFactory(String fontname, int fontsize, String text) throws IOException, FontFormatException { // Create a single-pixel buffered image to get font sizes etc from. this.g2 = new BufferedImage(1, 1, BufferedImage.TYPE_3BYTE_BGR).createGraphics(); // Set same hints as used for final render setOptimalRenderQuality(this.g2); this.setFontFace(fontname); this.setFontSize(fontsize); this.setText(text); } /** * Renders the current text to a .png file. * @param location Location to write the file out to * @throws IOException if file cannot be created */ public void createPngFile(String location) throws IOException { createPngFile(new File(location)); } /** * Renders the current text to a .png file. * @param location Location to write the file out to * @throws IOException if file cannot be created */ public void createPngFile(File location) throws IOException { ImageIO.write(createImage(), "png", location); } /** * Renders the current text in the current font fontname, fontsize and color. * @return Image containing rendered text * @throws IOException if no font name has been specified yet */ public RenderedImage createImage() throws IOException { if (this.fontname == null) { throw new IOException("No font name given!"); } // Get the bounds needed to render the text FontRenderContext frc = this.g2.getFontRenderContext(); TextLayout layout = new TextLayout(this.text, this.cachedFont, frc); Rectangle2D bounds = layout.getBounds(); // Get the width needed to render this piece of text // 2 pixels were added here due to problem with cutting of end of text int stringWidth = (int) (Math.ceil(bounds.getWidth())) + 2; // Get the height from generic font info // This way, all strings in this font will have same height // and vertical alignment FontMetrics fm = this.g2.getFontMetrics(); int stringHeight = fm.getHeight(); // Make an image to contain string BufferedImage im = new BufferedImage(stringWidth, stringHeight, BufferedImage.TYPE_3BYTE_BGR); // Set the font and colours on the image Graphics2D graphics = im.createGraphics(); // Setup best-quality rendering setOptimalRenderQuality(graphics); // Set colours and clear rectangle graphics.setBackground(new Color(this.br, this.bg, this.bb)); graphics.setColor(new Color(this.r, this.g, this.b)); graphics.clearRect(0, 0, stringWidth, stringHeight); // Set the font to use graphics.setFont(getFont()); // Position text on baseline, with first character exactly against // left margin layout.draw(graphics, -(float) Math.floor(bounds.getX()), fm.getMaxAscent()); // Return the image return im; } /** * Set the text to be rendered by the Txt2PngFactory. * @param text The text to render */ public void setText(String text) { this.text = text; } /** * Set 8-bit RGB values for text colour. * @param r Red component (0-255) * @param g Green component (0-255) * @param b Blue component (0-255) */ public void setTextRGB(int r, int g, int b) { this.r = r; this.g = g; this.b = b; } /** * Set 8-bit RGB values for background colour. * @param r Red component (0-255) * @param g Green component (0-255) * @param b Blue component (0-255) */ public void setBackgroundRGB(int r, int g, int b) { this.br = r; this.bg = g; this.bb = b; } /** * Set the TrueType font to render with. * @param fontname The name of the font to use */ public void setFontFace(String fontname) throws IOException, FontFormatException { if (!fontname.equals(this.fontname)) { this.fontname = fontname; updateFace(); } } /** * Set the point size of the font. * @param fontsize The point size of the font */ public void setFontSize(int fontsize) { if (fontsize != this.fontsize) { this.fontsize = fontsize; updateSize(); } } /** * Updates the cached font object. * @throws IOException if the font can't be loaded * @throws FontFormatException if font is not a valid TTF */ private void updateFace() throws IOException, FontFormatException { Font createdFont = null; // Attempt to load font from /fonts under classloader String fontpath = "fonts/" + this.fontname + ".ttf"; InputStream fontStream = this.getClass().getClassLoader().getResourceAsStream(fontpath); if (fontStream != null) { createdFont = Font.createFont(Font.TRUETYPE_FONT, fontStream); fontStream.close(); } // Next try to get it from fontpath if (createdFont == null) { Font tempFont = new Font(this.fontname, Font.PLAIN, 1); // Check we got correct font, not a fallback if (tempFont.getFamily().equals(this.fontname)) { // It's the correct font, set it createdFont = tempFont; } } // Last resort, treat as a path to font if (createdFont == null) { fontStream = new FileInputStream(this.fontname); if (fontStream != null) { createdFont = Font.createFont(Font.TRUETYPE_FONT, fontStream); fontStream.close(); } } // If we still don't have a font, throw exception if (createdFont == null) { throw new IOException("Can't locate font: " + this.fontname); } // Derive font of correct fontsize this.cachedFont = createdFont.deriveFont((float) this.fontsize); // Set on prototype image this.g2.setFont(this.cachedFont); } /** * Updates the cached font to new font derived with new size. */ private void updateSize() { if (this.cachedFont == null) { return; } // Derive font of correct fontsize this.cachedFont = this.cachedFont.deriveFont((float) this.fontsize); // Set on Graphics object so we can get FontMetrics this.g2.setFont(this.cachedFont); } /** * Get the FontMetrics object for the current font. * @return FontMetrics object for current font */ private FontMetrics getFontMetrics() { return this.g2.getFontMetrics(); } /** * Get a Font object for the current fontname and fontsize. * @return Font object for current name and size */ private Font getFont() { return this.cachedFont; } /** * Sets rendering hints for optimal rendering quality. * @param graphics Graphics2D object to set rendering options on */ private void setOptimalRenderQuality(Graphics2D graphics) { graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC); } @Override public String toString() { return this.fontname + ", " + this.fontsize + "pt: " + this.text; } }