/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2002-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.renderer.style; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.util.logging.Logger; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.WindowConstants; import org.geotools.renderer.style.shape.ExplicitBoundsShape; import org.opengis.feature.Feature; import org.opengis.filter.expression.Expression; /** * This factory accepts mark paths in the ttf://fontName#code * format, where fontName is the name of a TrueType font installed in the * system, or a URL to a TTF file, and the code is the character code, which may * be expressed in decimal, hexadecimal (e.g. 0x10) octal (e.g. * 045) form, as well as Unicode codes (e.g. U+F054 * or \uF054). * * @author Andrea Aime - TOPP * * * * * @source $URL$ */ public class TTFMarkFactory implements MarkFactory { /** The logger for the rendering module. */ private static final Logger LOGGER = org.geotools.util.logging.Logging .getLogger("org.geotools.rendering.style.TTFMarkFactory"); private static FontRenderContext FONT_RENDER_CONTEXT = new FontRenderContext( new AffineTransform(), false, false); public Shape getShape(Graphics2D graphics, Expression symbolUrl, Feature feature) throws Exception { String markUrl = symbolUrl.evaluate(feature, String.class); // if it does not start with the right prefix, it's not our business if (!markUrl.startsWith("ttf://")) return null; // if it does not match the expected format, complain before exiting if (!markUrl.matches("ttf://.+#.+")) { throw new IllegalArgumentException( "Mark URL font found, but does not match the required " + "structure font://#, e.g., ttf://wingdigs#0x7B. You specified " + markUrl); } String[] fontElements = markUrl.substring(6).split("#"); // look up the font Font font = FontCache.getDefaultInsance().getFont(fontElements[0]); if (font == null) { throw new IllegalArgumentException("Unkown font " + fontElements[0]); } // get the symbol number String code = fontElements[1]; char character; try { // see if a unicode escape sequence has been used if (code.startsWith("U+") || code.startsWith("\\u")) code = "0x" + code.substring(2); // this will handle most numeric formats like decimal, hex and octal character = (char) Integer.decode(code).intValue(); // handle charmap code reporting issues if(!font.canDisplay(character)) character = (char) (0xF000 | character); } catch (NumberFormatException e) { throw new IllegalArgumentException( "Invalid character specification " + fontElements[1], e); } // build the shape out of the font GlyphVector textGlyphVector = font.createGlyphVector(FONT_RENDER_CONTEXT, new char[] { (char) character }); Shape s = textGlyphVector.getOutline(); // have the shape be centered in the origin, and sitting in a square of side 1 Rectangle2D bounds = s.getBounds2D(); AffineTransform tx = new AffineTransform(); double max = Math.max(bounds.getWidth(), bounds.getHeight()); // all shapes are defined looking "upwards" (see ShapeMarkFactory or WellKnownMarkFactory) // but the fonts ones are flipped to compensate for the fact the y coords grow from top // to bottom on the screen. We have to flip the symbol so that it conforms to the // other marks convention tx.scale(1 / max, -1 / max); tx.translate(-bounds.getCenterX(), -bounds.getCenterY()); ExplicitBoundsShape shape = new ExplicitBoundsShape(tx.createTransformedShape(s)); shape.setBounds(new Rectangle2D.Double(-0.5,0.5,1.0,1.0)); return shape; } public static void main(String[] args) { BufferedImage image = new BufferedImage(300, 300, BufferedImage.TYPE_4BYTE_ABGR); Graphics2D g2d = (Graphics2D) image.getGraphics(); g2d.setColor(Color.BLACK); char c = 0xF041; System.out.println((int) c); Font font = new Font("Wingdings", Font.PLAIN, 60); for (int i = 0; i < 65536; i++) if (font.canDisplay(i)) System.out.println(((int) i) + ": " + Long.toHexString(i)); GlyphVector textGlyphVector = font.createGlyphVector(FONT_RENDER_CONTEXT, new char[] { c }); Shape shape = textGlyphVector.getOutline(); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.translate(150, 150); g2d.setColor(Color.BLUE); g2d.fill(shape); g2d.setColor(Color.BLACK); g2d.setFont(font); g2d.drawString(new String(new char[] { c }), 0, 50); g2d.dispose(); JFrame frame = new JFrame("Test"); frame.setContentPane(new JLabel(new ImageIcon(image))); frame.pack(); frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE); frame.setVisible(true); } }