bella 01/08/02 21:21:54 Modified: sources/org/apache/batik/svggen SVGFont.java SVGFontDescriptor.java SVGGraphicContextConverter.java SVGGraphics2D.java SVGGraphics2DUnitTester.java SVGSyntax.java Log: SVGGraphics2D now generates SVG fonts Revision Changes Path 1.13 +233 -5 xml-batik/sources/org/apache/batik/svggen/SVGFont.java Index: SVGFont.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/SVGFont.java,v retrieving revision 1.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- SVGFont.java 2001/04/26 14:17:19 1.12 +++ SVGFont.java 2001/08/03 04:21:53 1.13 @@ -12,6 +12,16 @@ import java.util.HashMap; import java.util.Map; import java.awt.font.TextAttribute; +import java.awt.font.GlyphVector; +import java.awt.font.GlyphMetrics; +import java.awt.font.LineMetrics; +import java.awt.font.FontRenderContext; +import java.awt.geom.AffineTransform; +import java.awt.Shape; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; import org.apache.batik.ext.awt.g2d.GraphicContext; @@ -21,7 +31,7 @@ * * @author <a href="mailto:[EMAIL PROTECTED]">Christophe Jolif</a> * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: SVGFont.java,v 1.12 2001/04/26 14:17:19 cjolif Exp $ + * @version $Id: SVGFont.java,v 1.13 2001/08/03 04:21:53 bella Exp $ */ public class SVGFont extends AbstractSVGConverter { public static final float EXTRA_LIGHT = @@ -120,6 +130,61 @@ } /** + * The common font size to use when generating all SVG fonts. + */ + static final int COMMON_FONT_SIZE = 100; + + /** + * Used to keep track of which characters have been rendered by each font + * used. + */ + static Map fontStringMap = new HashMap(); + + /** + * @param generatorContext used to build Elements + */ + public SVGFont(SVGGeneratorContext generatorContext) { + super(generatorContext); + fontStringMap = new HashMap(); + } + + /** + * Records that the specified font has been used to draw the text string. + * This is so we can keep track of which glyphs are required for each + * SVG font that is generated. + */ + public static void recordFontUsage(String string, Font font) { + + Font commonSizeFont = createCommonSizeFont(font); + String fontKey = commonSizeFont.getFamily() + commonSizeFont.getStyle(); + String textUsingFont = (String)fontStringMap.get(fontKey); + if (textUsingFont == null) { + // font has not been used before + textUsingFont = ""; + } + // append any new characters to textUsingFont + char ch; + for (int i = 0; i < string.length(); i++) { + ch = string.charAt(i); + if (textUsingFont.indexOf(ch) == -1) { + textUsingFont += ch; + } + } + fontStringMap.put(fontKey, textUsingFont); + } + + /** + * Creates a new Font that is of the common font size used for generating + * SVG fonts. The new Font will be the same as the specified font, with + * only its size attribute modified. + */ + private static Font createCommonSizeFont(Font font) { + HashMap attributes = new HashMap(font.getAttributes()); + attributes.put(TextAttribute.SIZE, new Float(COMMON_FONT_SIZE)); + return new Font(attributes); + } + + /** * Converts part or all of the input GraphicContext into * a set of attribute/value pairs and related definitions * @@ -130,21 +195,184 @@ * @see org.apache.batik.svggen.SVGDescriptor */ public SVGDescriptor toSVG(GraphicContext gc) { - return toSVG(gc.getFont()); + return toSVG(gc.getFont(), gc.getFontRenderContext()); } /** * @param font Font object which should be converted to a set * of SVG attributes + * @param frc The FontRenderContext which will be used to generate glyph + * elements for the SVGFont definition element * @return description of attribute values that describe the font */ - public static SVGFontDescriptor toSVG(Font font) { + public SVGFontDescriptor toSVG(Font font, FontRenderContext frc) { + String fontSize = "" + font.getSize(); String fontWeight = weightToSVG(font); String fontStyle = styleToSVG(font); String fontFamilyStr = familyToSVG(font); - return new SVGFontDescriptor(fontSize, fontWeight, - fontStyle, fontFamilyStr); + + Font commonSizeFont = createCommonSizeFont(font); + String fontKey = commonSizeFont.getFamily() + commonSizeFont.getStyle(); + + String textUsingFont = (String)fontStringMap.get(fontKey); + + if (textUsingFont == null) { + // this font hasn't been used by any text yet, + // so don't create an SVG Font element for it + return new SVGFontDescriptor(fontSize, fontWeight, + fontStyle, fontFamilyStr, + null); + } + + Document domFactory = generatorContext.domFactory; + + // see if a description already exists for this font + SVGFontDescriptor fontDesc = + (SVGFontDescriptor)descMap.get(fontKey); + + Element fontDef; + + if (fontDesc != null) { + + // use the SVG Font element that has already been created + fontDef = fontDesc.getDef(); + + } else { + + // create a new SVG Font element + fontDef = domFactory.createElementNS(SVG_NAMESPACE_URI, + SVG_FONT_TAG); + + // + // create the font-face element + // + Element fontFace = domFactory.createElementNS(SVG_NAMESPACE_URI, + SVG_FONT_FACE_TAG); + String svgFontFamilyString = fontFamilyStr; + if (fontFamilyStr.startsWith("'") && fontFamilyStr.endsWith("'")) { + // get rid of the quotes + svgFontFamilyString + = fontFamilyStr.substring(1, fontFamilyStr.length()-1); + } + fontFace.setAttributeNS(null, SVG_FONT_FAMILY_ATTRIBUTE, + svgFontFamilyString); + fontFace.setAttributeNS(null, SVG_FONT_WEIGHT_ATTRIBUTE, + fontWeight); + fontFace.setAttributeNS(null, SVG_FONT_STYLE_ATTRIBUTE, + fontStyle); + fontFace.setAttributeNS(null, SVG_UNITS_PER_EM_ATTRIBUTE, + ""+COMMON_FONT_SIZE); + fontDef.appendChild(fontFace); + + // + // create missing glyph element + // + Element missingGlyphElement + = domFactory.createElementNS(SVG_NAMESPACE_URI, + SVG_MISSING_GLYPH_TAG); + + int missingGlyphCode[] = new int[1]; + missingGlyphCode[0] = commonSizeFont.getMissingGlyphCode(); + GlyphVector gv = commonSizeFont.createGlyphVector(frc, missingGlyphCode); + Shape missingGlyphShape = gv.getGlyphOutline(0); + GlyphMetrics gm = gv.getGlyphMetrics(0); + + // need to turn the missing glyph upside down to be in the font + // coordinate system (i.e Y axis up) + AffineTransform at = AffineTransform.getScaleInstance(1, -1); + missingGlyphShape = at.createTransformedShape(missingGlyphShape); + + missingGlyphElement.setAttributeNS(null, SVG_D_ATTRIBUTE, + SVGPath.toSVGPathData(missingGlyphShape)); + missingGlyphElement.setAttributeNS(null, SVG_HORIZ_ADV_X_ATTRIBUTE, + "" + gm.getAdvance()); + fontDef.appendChild(missingGlyphElement); + + // set the font's default horizontal advance to be the same as + // the missing glyph + fontDef.setAttributeNS(null, SVG_HORIZ_ADV_X_ATTRIBUTE, "" + gm.getAdvance()); + + // set the ascent and descent attributes + LineMetrics lm = commonSizeFont.getLineMetrics("By", frc); + fontFace.setAttributeNS(null, SVG_ASCENT_ATTRIBUTE, "" + lm.getAscent()); + fontFace.setAttributeNS(null, SVG_DESCENT_ATTRIBUTE, "" + lm.getDescent()); + + // + // Font ID + // + fontDef.setAttributeNS(null, ATTR_ID, + generatorContext.idGenerator. + generateID(ID_PREFIX_FONT)); + } + + // + // add any new glyphs to the fontDef here + // + + // process the characters in textUsingFont backwards since the new chars + // are at the end, can stop when find a char that already has a glyph + for (int i = textUsingFont.length()-1; i >= 0; i--) { + char c = textUsingFont.charAt(i); + boolean foundGlyph = false; + NodeList fontChildren = fontDef.getChildNodes(); + for (int j = 0; j < fontChildren.getLength(); j++) { + if (fontChildren.item(j) instanceof Element) { + Element childElement = (Element)fontChildren.item(j); + if (childElement.getAttributeNS(null, + SVG_UNICODE_ATTRIBUTE).equals(""+c)) { + foundGlyph = true; + break; + } + } + } + if (!foundGlyph) { + // need to create one + Element glyphElement + = domFactory.createElementNS(SVG_NAMESPACE_URI, + SVG_GLYPH_TAG); + + GlyphVector gv = commonSizeFont.createGlyphVector(frc, ""+c); + Shape glyphShape = gv.getGlyphOutline(0); + GlyphMetrics gm = gv.getGlyphMetrics(0); + + // need to turn the glyph upside down to be in the font + // coordinate system (i.e Y axis up) + AffineTransform at = AffineTransform.getScaleInstance(1, -1); + glyphShape = at.createTransformedShape(glyphShape); + + glyphElement.setAttributeNS(null, SVG_D_ATTRIBUTE, + SVGPath.toSVGPathData(glyphShape)); + glyphElement.setAttributeNS(null, SVG_HORIZ_ADV_X_ATTRIBUTE, + "" + gm.getAdvance()); + glyphElement.setAttributeNS(null, SVG_UNICODE_ATTRIBUTE, + "" + c); + + fontDef.appendChild(glyphElement); + } else { + // have reached the chars in textUsingFont that already + // have glyphs, don't need to process any further + break; + } + } + + // + // create a new font description for this instance of the font usage + // + SVGFontDescriptor newFontDesc + = new SVGFontDescriptor(fontSize, fontWeight, + fontStyle, fontFamilyStr, + fontDef); + + // + // Update maps so that the font def can be reused if needed + // + if (fontDesc == null) { + descMap.put(fontKey, newFontDesc); + defSet.add(fontDef); + } + + return newFontDesc; } /** 1.4 +14 -2 xml-batik/sources/org/apache/batik/svggen/SVGFontDescriptor.java Index: SVGFontDescriptor.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/SVGFontDescriptor.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- SVGFontDescriptor.java 2001/04/26 14:17:19 1.3 +++ SVGFontDescriptor.java 2001/08/03 04:21:53 1.4 @@ -16,10 +16,13 @@ * Describes an SVG font * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: SVGFontDescriptor.java,v 1.3 2001/04/26 14:17:19 cjolif Exp $ + * @version $Id: SVGFontDescriptor.java,v 1.4 2001/08/03 04:21:53 bella Exp $ * @see org.apache.batik.svggen.SVGFont */ public class SVGFontDescriptor implements SVGDescriptor, SVGSyntax { + + private Element def; + private String fontSize; private String fontWeight; private String fontStyle; @@ -31,7 +34,8 @@ public SVGFontDescriptor(String fontSize, String fontWeight, String fontStyle, - String fontFamily){ + String fontFamily, + Element def){ if (fontSize == null || fontWeight == null || fontStyle == null || @@ -42,6 +46,7 @@ this.fontWeight = fontWeight; this.fontStyle = fontStyle; this.fontFamily = fontFamily; + this.def = def; } public Map getAttributeMap(Map attrMap){ @@ -56,9 +61,16 @@ return attrMap; } + public Element getDef(){ + return def; + } + public List getDefinitionSet(List defSet){ if (defSet == null) defSet = new LinkedList(); + + if(def != null && !defSet.contains(def)) + defSet.add(def); return defSet; } 1.10 +2 -2 xml-batik/sources/org/apache/batik/svggen/SVGGraphicContextConverter.java Index: SVGGraphicContextConverter.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/SVGGraphicContextConverter.java,v retrieving revision 1.9 retrieving revision 1.10 diff -u -r1.9 -r1.10 --- SVGGraphicContextConverter.java 2001/04/26 14:17:21 1.9 +++ SVGGraphicContextConverter.java 2001/08/03 04:21:53 1.10 @@ -22,7 +22,7 @@ * SVG attributes. * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: SVGGraphicContextConverter.java,v 1.9 2001/04/26 14:17:21 cjolif Exp $ + * @version $Id: SVGGraphicContextConverter.java,v 1.10 2001/08/03 04:21:53 bella Exp $ */ public class SVGGraphicContextConverter { private static final int GRAPHIC_CONTEXT_CONVERTER_COUNT = 6; @@ -59,7 +59,7 @@ compositeConverter = new SVGComposite(generatorContext); clipConverter = new SVGClip(generatorContext); hintsConverter = new SVGRenderingHints(); - fontConverter = new SVGFont(); + fontConverter = new SVGFont(generatorContext); int i=0; converters[i++] = paintConverter; 1.20 +7 -1 xml-batik/sources/org/apache/batik/svggen/SVGGraphics2D.java Index: SVGGraphics2D.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/SVGGraphics2D.java,v retrieving revision 1.19 retrieving revision 1.20 diff -u -r1.19 -r1.20 --- SVGGraphics2D.java 2001/06/21 07:54:30 1.19 +++ SVGGraphics2D.java 2001/08/03 04:21:53 1.20 @@ -44,7 +44,7 @@ * * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: SVGGraphics2D.java,v 1.19 2001/06/21 07:54:30 cjolif Exp $ + * @version $Id: SVGGraphics2D.java,v 1.20 2001/08/03 04:21:53 bella Exp $ * @see org.apache.batik.ext.awt.g2d.GraphicContext * @see org.apache.batik.svggen.DOMTreeManager * @see org.apache.batik.svggen.DOMGroupManager @@ -968,6 +968,12 @@ */ public void drawString(String s, float x, float y) { if (!textAsShapes) { + + // record that the font is being used to draw this string, this is + // so that the SVG Font element will only create glyphs for the + // characters that are needed + SVGFont.recordFontUsage(s, getFont()); + Element text = getDOMFactory().createElementNS(SVG_NAMESPACE_URI, SVG_TEXT_TAG); text.setAttributeNS(null, SVG_X_ATTRIBUTE, 1.7 +4 -3 xml-batik/sources/org/apache/batik/svggen/SVGGraphics2DUnitTester.java Index: SVGGraphics2DUnitTester.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/SVGGraphics2DUnitTester.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- SVGGraphics2DUnitTester.java 2001/06/21 07:54:31 1.6 +++ SVGGraphics2DUnitTester.java 2001/08/03 04:21:53 1.7 @@ -31,7 +31,7 @@ * * @author <a href="mailto:[EMAIL PROTECTED]">Christophe Jolif</a> * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: SVGGraphics2DUnitTester.java,v 1.6 2001/06/21 07:54:31 cjolif Exp $ + * @version $Id: SVGGraphics2DUnitTester.java,v 1.7 2001/08/03 04:21:53 bella Exp $ */ public class SVGGraphics2DUnitTester implements SVGConstants { private String[] args; @@ -818,11 +818,12 @@ throws Exception { Document domFactory = getDocumentPrototype(); Element group = domFactory.createElementNS(SVG_NAMESPACE_URI, SVG_G_TAG); - SVGFont converter = new SVGFont(); + SVGFont converter = new SVGFont(getContext(domFactory)); + GraphicContext gc = new GraphicContext(new AffineTransform()); for(int i=0; i<fonts.length; i++){ Font font = fonts[i]; - Map attrMap = converter.toSVG(font).getAttributeMap(null); + Map attrMap = converter.toSVG(font, gc.getFontRenderContext()).getAttributeMap(null); Element textElement = domFactory.createElementNS(SVG_NAMESPACE_URI, SVG_TEXT_TAG); Iterator iter = attrMap.keySet().iterator(); while(iter.hasNext()){ 1.5 +2 -1 xml-batik/sources/org/apache/batik/svggen/SVGSyntax.java Index: SVGSyntax.java =================================================================== RCS file: /home/cvs/xml-batik/sources/org/apache/batik/svggen/SVGSyntax.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- SVGSyntax.java 2001/03/26 09:45:16 1.4 +++ SVGSyntax.java 2001/08/03 04:21:53 1.5 @@ -14,7 +14,7 @@ * Contains the definition of the SVG tags and attribute names. * * @author <a href="mailto:[EMAIL PROTECTED]">Vincent Hardy</a> - * @version $Id: SVGSyntax.java,v 1.4 2001/03/26 09:45:16 tkormann Exp $ + * @version $Id: SVGSyntax.java,v 1.5 2001/08/03 04:21:53 bella Exp $ */ public interface SVGSyntax extends SVGConstants{ /** @@ -47,6 +47,7 @@ public static final String ID_PREFIX_FE_GAUSSIAN_BLUR = "feGaussianBlur"; public static final String ID_PREFIX_FE_LIGHTING_FILTER = "feLightingFilter"; public static final String ID_PREFIX_FE_SPECULAR_LIGHTING = "feSpecularLighting"; + public static final String ID_PREFIX_FONT = "font"; public static final String ID_PREFIX_GENERIC_DEFS = "genericDefs"; public static final String ID_PREFIX_LINEAR_GRADIENT = "linearGradient"; public static final String ID_PREFIX_MASK = "mask"; --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]