hi, i think you are right - i think the confusion was coming from the fact that inkscape can't do SVG fonts, so if i open it there, everything goes bonkers. below the full code, for reference of others, although i think something is wrong with the rectangles in draw - although the context is dynamic, they're not drawn until i write the svg out, then transcode the output.
however, for the current task, one upstream component cannot handle kerning (yet) and we have to be able to see it consistenly in different editors, so the path i chose now is to convert the text to paths immediately, basically doing this: Node n = SvgUtil.createSvg(); CharacterIterator ci = new StringCharacterIterator(text); Character c = null; double currX = 0; Element gMaster = (Element)SvgUtil.createSvgNode(n, SVG12Constants.SVG_G_TAG); FontGlyph lastGlyph = null; FontGlyph fg = getGlyph(ci.current()); while((c = ci.current())!=CharacterIterator.DONE) { Element g = (Element)SvgUtil.createSvgNode(gMaster, SVG12Constants.SVG_G_TAG); g.setAttributeNS(null,SVG12Constants.SVG_TRANSFORM_ATTRIBUTE, "translate("+currX+" 0) " + "scale("+getGlyphScale(point)+" "+(-1d*getGlyphScale(point))+")" + ""); Element p = (Element)SvgUtil.createSvgNode(g, SVG12Constants.SVG_PATH_TAG); p.setAttributeNS(null, "d", fg.getNodePath()); // standard h-advance double advance = fg.gethAdvanceX(point); currX+=advance; ci.next(); lastGlyph = fg; if(ci.current()==CharacterIterator.DONE) { break; } fg = getGlyph(ci.current()); if(kern) { double k = getKerning(lastGlyph.getGlyphName(), fg.getGlyphName()); log.info("== KERNING "+lastGlyph.getGlyphName()+" / "+fg.getGlyphName()+": "+k); currX = currX - (emToUserspace(k, point)); } } i probably will lose support for arabic, korean, thai and various other scripts, because this cannot properly do combined glyphs (i guess), but it works quite nice and has the benefit of looking exaxtly the same, no matter if it's batik, inkscape, illustrator or any other editor. .rm ===== font / getExtentOfChar() ===== ===== ===== ===== ===== ===== ===== ===== ===== ===== import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.batik.bridge.BridgeContext; import org.apache.batik.bridge.DocumentLoader; import org.apache.batik.bridge.GVTBuilder; import org.apache.batik.bridge.UserAgent; import org.apache.batik.bridge.UserAgentAdapter; import org.apache.batik.dom.svg.SVGDOMImplementation; import org.apache.batik.dom.svg.SVGOMTextElement; import org.apache.batik.gvt.GraphicsNode; import org.apache.batik.svggen.SVGGraphics2D; import org.apache.batik.transcoder.SVGAbstractTranscoder; import org.apache.batik.transcoder.Transcoder; import org.apache.batik.transcoder.TranscoderInput; import org.apache.batik.transcoder.TranscoderOutput; import org.apache.batik.transcoder.image.PNGTranscoder; import org.apache.batik.util.SVG12Constants; import org.apache.batik.util.SVGConstants; import org.w3c.dom.DOMImplementation; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.svg.SVGDocument; import org.w3c.dom.svg.SVGRect; public class Font { public static void text(String textContent, String outfilename) { try { // boot the document DOMImplementation domImpl = new SVGDOMImplementation(); String svgNS = SVG12Constants.SVG_NAMESPACE_URI; String svgPrefix = SVG12Constants.SVG_SVG_TAG; SVGDocument svg = (SVGDocument) domImpl.createDocument(svgNS, svgPrefix, null); SVGGraphics2D graphics = new SVGGraphics2D(svg); UserAgent userAgent = new UserAgentAdapter(); DocumentLoader loader = new DocumentLoader(userAgent); BridgeContext ctx = new BridgeContext(userAgent, loader); ctx.setDynamicState(BridgeContext.DYNAMIC); GVTBuilder builder = new GVTBuilder(); GraphicsNode rootGN = builder.build(ctx, svg); svg.getDocumentElement().setAttributeNS(null, "version", "1.2"); // add the font reference in the defs Element fontface = svg.createElementNS(svgNS, SVGConstants.SVG_FONT_FACE_TAG); fontface.setAttributeNS(null, "font-family", "test_font"); Element fontfacesrc = svg.createElementNS(svgNS, SVGConstants.SVG_FONT_FACE_SRC_TAG); Element fontfaceuri = svg.createElementNS(svgNS, SVGConstants.SVG_FONT_FACE_URI_TAG); File f = new File("FONT.SVG"); fontfaceuri.setAttributeNS(svgNS, "xlink:href", f.toURI()+"#font-2c95d8b23a8e51ae013a8e75ff18006b"); Element fontfaceformat = svg.createElementNS(svgNS, SVGConstants.SVG_FONT_FACE_FORMAT_TAG); fontfaceformat.setAttributeNS(svgNS, "string", "svg"); Element defs = svg.createElementNS(svgNS, SVGConstants.SVG_DEFS_TAG); fontfaceuri.appendChild(fontfaceformat); fontfacesrc.appendChild(fontfaceuri); fontface.appendChild(fontfacesrc); defs.appendChild(fontface); // create the text element Element text = svg.createElementNS(svgNS, SVG12Constants.SVG_TEXT_TAG); svg.getDocumentElement().appendChild(text); text.setAttributeNS(null, SVGConstants.SVG_STYLE_ATTRIBUTE, "font-size:12pt;" + "font-family:test_font;" + "font-style:normal;" + "font-variant:normal;" + "font-weight:normal;" + "fill:#000000;" + "stroke:none"); text.setAttributeNS(null,"y", "200"); Node fpText = text.getOwnerDocument().createTextNode(textContent); text.appendChild(fpText); SVGOMTextElement te = (SVGOMTextElement) text; // paint a border around each character for (int i = 0; i < textContent.length(); i++) { System.err.println(" getting extent of char: "+textContent.charAt(i)); SVGRect rect = te.getExtentOfChar(i); Element e = svg.createElement("rect"); e.setAttributeNS(null, "x", rect.getX() + ""); e.setAttributeNS(null, "y", rect.getY() + ""); e.setAttributeNS(null, "width", rect.getWidth() + ""); e.setAttributeNS(null, "height", rect.getHeight() + ""); e.setAttributeNS(null, "stroke", "#000000"); e.setAttributeNS(null, "fill", "none "); e.setAttributeNS(null, "stroke-width", "1"); //e.setAttributeNS(null, "style", "fill:none;fill-opacity:1;stroke:#0000ff;stroke-width:1;stroke-opacity:1;stroke-dasharray:9,9;stroke-dashoffset:0"); svg.getDocumentElement().appendChild(e); } // output to svg { FileOutputStream fos = new FileOutputStream(outfilename+".svg"); DOMSource domSource = new DOMSource(svg); StreamResult res = new StreamResult(fos); Transformer t = TransformerFactory.newInstance().newTransformer(); t.transform(domSource, res); fos.flush(); } // transcode to png { FileInputStream fis = new FileInputStream(outfilename+".svg"); FileOutputStream fos = new FileOutputStream(outfilename+".png"); TranscoderInput tip = new TranscoderInput(fis); TranscoderOutput top = new TranscoderOutput(fos); Transcoder t = new PNGTranscoder(); t.addTranscodingHint(SVGAbstractTranscoder.KEY_HEIGHT, 1600f); t.addTranscodingHint(SVGAbstractTranscoder.KEY_WIDTH, 1600f); t.transcode(tip, top); fos.flush(); } } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { text("HELLO AT, W, T, V, or other Kerning","with_spaces"); text("HELLOAT,W,T,V,orotherKerning","without_spaces"); } } On Nov 10, 2012, at 11:21 PM, DeWeese Thomas wrote: > Hi Ruben, > > I'm fairly certain that getExtentOfChar works properly. We have a fairly > complete test of it in "samples/tests/spec/scripting/text_content.svg" I > added some hkern examples to the SVG font that test includes and > getExtentOfChar worked correctly in that context. In looking at that test > one thing that occurred to me is the possibility that the coordinate system > you are adding the rectangles is slightly different from the texts coordinate > system (note that getExtentOfChar returns box in the text element not in the > coordinate system of the parent of the text which is likely where you are > appending the rect). You might want to adopt the code that test uses to > display it's rectangles for your rectangle display. > > BTW it's expected that the boxes don't overlap that doesn't indicate that > the kerning is being ignored. > The extent boxes for horizontal text are adjusted to just touch each other so > they can be used for hit detection. If text selection works correctly for > that text then getExtentOfChar should work. > > As for the issue with getting the extent of a single space char I suspect > the issue is that we are required to remove > leading and trailing spaces from text elements in the normal case. I think > you need to set the "xml:space" attribute > to "preserve". > > I would strong recommend that you try and get the 'getExtentOfChar' > approach to work for you as that is a much better solution than dropping > support for all the nice text features in SVG. It also seems to me like if > you can't getExtentOfChar to work properly then it likely means you don't > really understand your coordinate systems which will likely lead to similar > issues down the road with your own solution eventually. > > Thomas