// Copyright 2001-2006, FreeHEP. package org.freehep.graphicsio.svg; import java.awt.Font; import java.awt.font.FontRenderContext; import java.awt.font.GlyphVector; import java.awt.font.TextAttribute; import java.awt.font.TextLayout; import java.awt.geom.AffineTransform; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.Properties; import org.freehep.graphics2d.font.FontUtilities; import org.freehep.graphicsio.font.FontTable; /** * A table to remember which glyphs were used while writing a svg file. * Entries are added by calling {@link #addGlyphs(String, java.awt.Font)}. * The final SVG tag for the section is generated using {@link #toString()}. * Use {@link #normalize(java.util.Map)} for referencing embedded glyphs * in tags. * * @author Steffen Greiffenberg * @version $Id: SVGFontTable.java 12753 2007-06-12 22:32:31Z duns $ */ public class SVGFontTable { /** * Stores fonts and a glyph-hashtable. The font key ist normalized using * {@link #untransform(java.awt.Font)} */ private Hashtable/**/ glyphs = new Hashtable/*>*/(); /** * creates a glyph for the string character * * @param c * @param font * @return unique font name */ private SVGGlyph addGlyph(int c, Font font) { // is the font stored? Hashtable/**/ glyphs = getGlyphs(font); // does a glyph allready exist? SVGGlyph result = (SVGGlyph) glyphs.get(String.valueOf(c)); // create a new one? if (result == null) { // create and store the SVG Glyph result = createGlyph(c, font); glyphs.put(String.valueOf(c), result); } return result; } /** * @param c * @param font * @return GlyphVector using a default rendering context */ private SVGGlyph createGlyph(int c, Font font) { GlyphVector glyphVector = font.createGlyphVector( // flipping is done by SVGGlyph new FontRenderContext(null, true, true), // unicode to char String.valueOf((char) c)); // create and store the SVG Glyph return new SVGGlyph( glyphVector.getGlyphOutline(0), c, glyphVector.getGlyphMetrics(0)); } /** * creates the glyph for the string * * @param string * @param font */ protected void addGlyphs(String string, Font font) { font = untransform(font); // add characters for (int i = 0; i < string.length(); i ++) { addGlyph(string.charAt(i), font); } } /** * @param font * @return glyph vectors for font */ private Hashtable/**/ getGlyphs(Font font) { // derive a default font for the font table font = untransform(font); Hashtable/**/ result = (Hashtable/**/) glyphs.get(font); if (result == null) { result = new Hashtable/**/(); glyphs.put(font, result); } return result; } /** * creates the font entry: *
     * 
     * 
     * ...
     * 
     * 
* * @return string representing the entry */ public String toString() { StringBuffer result = new StringBuffer(); Enumeration/**/ fonts = this.glyphs.keys(); while (fonts.hasMoreElements()) { Font font = (Font) fonts.nextElement(); // replace font family for svg Map /**/ attributes = FontUtilities.getAttributes(font); // Dialog -> Helvetica normalize(attributes); // familiy result.append("\n"); // font-face result.append("\n"); // missing glyph SVGGlyph glyph = createGlyph(font.getMissingGlyphCode(), font); result.append("\n"); // regular glyphs Iterator glyphs = getGlyphs(font).values().iterator(); while (glyphs.hasNext()) { result.append(glyphs.next().toString()); result.append("\n"); } // close "" result.append("\n"); } return result.toString(); } /** * creates a font based on the parameter. The size will be {@link SVGGlyph#FONT_SIZE} * and transformation will be removed. Example:
* java.awt.Font[family=SansSerif,name=SansSerif,style=plain,size=30]
* will result to:
* java.awt.Font[family=SansSerif,name=SansSerif,style=plain,size=100]

* * This method does not substitute font name or family. * * @param font * @return font based on the parameter */ private Font untransform(Font font) { // replace font family Map /**/ attributes = FontUtilities.getAttributes(font); // set default font size attributes.put(TextAttribute.SIZE, new Float(SVGGlyph.FONT_SIZE)); // remove font transformation attributes.remove(TextAttribute.TRANSFORM); attributes.remove(TextAttribute.SUPERSCRIPT); return new Font(attributes); } /** * font replacements makes SVG in AdobeViewer look better, firefox replaces * all font settings, even the family fame */ private static final Properties replaceFonts = new Properties(); static { replaceFonts.setProperty("dialog", "Helvetica"); replaceFonts.setProperty("dialoginput", "Courier New"); // FIXME: works well on windows, others? // "TimesRoman" is not valid under Firefox 1.5 replaceFonts.setProperty("serif", "Times"); replaceFonts.setProperty("timesroman", "Times"); replaceFonts.setProperty("sansserif", "Helvetica"); // FIXME: works well on windows, others? // "Courier" is not valid under Firefox 1.5 replaceFonts.setProperty("monospaced", "Courier New"); // FIXME: replacement for zapfdingbats? replaceFonts.setProperty("zapfdingbats", "Wingdings"); } /** * Replaces TextAttribute.FAMILY by values of replaceFonts. When a * font created using the result of this method the transformation would be: * * java.awt.Font[family=SansSerif,name=SansSerif,style=plain,size=30]
* will result to:
* java.awt.Font[family=SansSerif,name=Helvetica,style=plain,size=30]

* * Uses {@link FontTable#normalize(java.util.Map)} first. * * @param attributes with font name to change */ public static void normalize(Map /**/ attributes) { // dialog.bold -> Dialog with TextAttribute.WEIGHT_BOLD FontTable.normalize(attributes); // get replaced font family name (Yes it's right, not the name!) String family = replaceFonts.getProperty( ((String) attributes.get(TextAttribute.FAMILY)).toLowerCase()); if (family == null) { family = (String) attributes.get(TextAttribute.FAMILY); } // replace the family (Yes it's right, not the name!) in the attributes attributes.put(TextAttribute.FAMILY, family); } }