Please find below some code I've written to modify the internal CSS within an SVG document.
What I need: I need help with telling batik to update/redraw the drawing following the modification. Detail: I am not using Swing or any associated batik libraries and do not wish to. What I want is for the GVTBuilder/GraphicsNode/Bridge or whatever to be updated to take into account my CSS modification so that I can then do my processing of the drawing. The resultant text SVG XML that I output should also reflect all the changes. I've searched this forum and found a few posts that appeared to already do this but when I tried them, it didn't work. Plus they appeared to process the text of the CSS using generic text processing tools, rather than manipulate the batik objects that are associated with the CSS. For example: http://www.nabble.com/modifying-css-in-live-svg-document-to3826681.html#a3826681 My solution manipulates the batik CSS objects. I have to say, the documentation is very minimal in this area! But with the help of print outs, debugging and Eclipse I achieved my solution. I wrote this code because I am processing a SVG document from a commercial drawing application, Corel version 12 which contains an internal CSS definition, which has several styles defining the font size as negative. (I don't want to enter in a debate about the merits of various drawing tools. Let us just assume that I have to work with the output of this tool. ) The CSS negative font size attribute causes an exception when I attempt to process the SVG - i.e. booting the CSS and DOM is OK but when I then try to get attributes from the graphics e.g. getting bounding boxes, I get an exception. GlyphLayout.java:1024>> getUnderlineShape() GlyphLayout.java:436>> getDecorationOutline() StrokingTextPainter.java:1191>> getDecorationOutline() StrokingTextPainter.java:1036>> getOutline() TextNode.java:281>> getOutline() AbstractGraphicsNodeBridge.java:492>> getBBox() SVGLocatableSupport.java:77>> getBBox() SVGOMTextElement.java:135>> getBBox() XyOutsideLabelGraphicsFilter.java:113>> acceptNode() The cause of this exception is apparently because negative font sizes are illegal - as explaned in the response to my post of the original problem: http://www.nabble.com/Need-to-process-negative-width-elements-without-causing-Exception-to16036194.html Well of course, size of something in the real world can never be negative. However I did think that it might not be, that perhaps it is a relative figure, relative to the absolute value defined for something else - much like size attribute was used with the font tag in HTML. But it is illegal, because the CSS style is not relative to/or inheriting from any other style, so it can't be legal. And the CSS 2 specification says it is illegal, according to the respondent to my post above. Inkscape is more tolerant than batik; the text for the font is reflected - i.e. backwards. Not sure whether this is as intended by the original program. I'm just using Inkscape to view the output for reference purposes. I don't wish to use it in any other way and need to use batik for the work I am doing. Here is an excerpt of the SVG file with the internal CSS with the offending negative font size value (then read on beyond it for more information and the code I've written): <?xml version="1.0" encoding="UTF-8" standalone="no"?> <!-- Creator: CorelDRAW --> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://web.resource.org/cc/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xml:space="preserve" width="297mm" height="210mm" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd" viewBox="0 0 297 210" id="svg13071" sodipodi:version="0.32" inkscape:version="0.45.1" inkscape:output_extension="org.inkscape.output.svg.inkscape" sodipodi:docbase="C:\Documents and Settings\rdavis\Desktop"><metadata id="metadata13993"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><sodipodi:namedview inkscape:window-height="975" inkscape:window-width="1280" inkscape:pageshadow="2" inkscape:pageopacity="0.0" guidetolerance="10.0" gridtolerance="10.0" objecttolerance="10.0" borderopacity="1.0" bordercolor="#666666" pagecolor="#ffffff" id="base" inkscape:zoom="1.1670775" inkscape:cx="526.18109" inkscape:cy="372.04724" inkscape:window-x="-4" inkscape:window-y="-4" inkscape:current-layer="Artwork" /> <defs id="defs13073"> <style type="text/css" id="style13484"> @font-face { font-family:"Arial";src:url("#FontID2") format(svg)} @font-face { font-family:"Humnst777 Blk BT";src:url("#FontID1") format(svg)} @font-face { font-family:"Humnst777 BT";src:url("#FontID0") format(svg)} .fil3 {fill:#1F1A17} .fnt10 {font-weight:normal;font-size:-6.9144;font-family:'Humnst777 BT'} </style> </defs> <g id="EC_x0020_Markup"> <metadata id="CorelCorpID_5Corel-Layer" /> <text x="73.0257" y="11.3166" class="fil3 fnt10" id="text13892">91098-01</text> </g> </svg> Batik is not very defensive at all with this. When I process it, the exception I get originates from non-batik code in the awt, when it tries to render a negative stroke width of a font - here's that excerpt from the exception call stack again GlyphLayout.java:1024>> getUnderlineShape() GlyphLayout.java:436>> getDecorationOutline() StrokingTextPainter.java:1191>> getDecorationOutline() StrokingTextPainter.java:1036>> getOutline() TextNode.java:281>> getOutline() AbstractGraphicsNodeBridge.java:492>> getBBox() SVGLocatableSupport.java:77>> getBBox() SVGOMTextElement.java:135>> getBBox() XyOutsideLabelGraphicsFilter.java:113>> acceptNode() In batik's GlyphLayout a call is made to awt. So being pragmatic I have decided to write my own code to intercept the non-compliant styles. It works, almost - the batikl object associated with the CSS style is updated successfully when I change the value from a negative to a positive value - I can see this from the inspect feature in the Eclipse IDE debugger. Here is the code below. What is does is seek out the offending negative font size CSS style, make it positive. Then it finds the SVG elements that use the style. At this point I want to tell batik to update them but don't know how. See also my comments after the code. import java.awt.Shape; import java.awt.geom.AffineTransform; import java.awt.geom.PathIterator; import java.io.File; import java.io.IOException; import java.io.StringReader; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Set; import java.util.Vector; import java.util.regex.Pattern; 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.css.dom.CSSOMStyleDeclaration; import org.apache.batik.css.engine.CSSEngine; import org.apache.batik.css.engine.CSSStyleSheetNode; import org.apache.batik.css.engine.FontFaceRule; import org.apache.batik.css.engine.Rule; import org.apache.batik.css.engine.SVGCSSEngine; import org.apache.batik.css.engine.StyleDeclaration; import org.apache.batik.css.engine.StyleRule; import org.apache.batik.css.engine.StyleSheet; import org.apache.batik.css.engine.sac.CSSConditionalSelector; import org.apache.batik.css.engine.value.FloatValue; import org.apache.batik.css.engine.value.Value; import org.apache.batik.css.engine.value.css2.FontSizeManager; import org.apache.batik.dom.svg.SAXSVGDocumentFactory; import org.apache.batik.ext.awt.geom.Polygon2D; import org.apache.batik.gvt.AbstractGraphicsNode; import org.apache.batik.gvt.CompositeGraphicsNode; import org.apache.batik.gvt.GraphicsNode; import org.apache.batik.gvt.RootGraphicsNode; import org.apache.batik.parser.AWTPathProducer; import org.apache.batik.parser.AWTPolygonProducer; import org.apache.batik.util.XMLResourceDescriptor; import org.jaxen.JaxenException; import org.w3c.css.sac.Selector; import org.w3c.css.sac.SelectorList; import org.w3c.dom.css.CSSFontFaceRule; import org.w3c.dom.css.CSSPrimitiveValue; import org.w3c.dom.css.CSSStyleDeclaration; import org.w3c.dom.svg.SVGDocument; import org.w3c.dom.svg.SVGElement; import org.w3c.dom.svg.SVGLocatable; import org.w3c.dom.svg.SVGRect; import org.w3c.dom.traversal.NodeFilter; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.Element; import org.w3c.dom.NodeList; public class Filter implements NodeFilter { private Shape labelOutlineShape = null; private Document doc; private UserAgent userAgent; private DocumentLoader loader; private BridgeContext ctx; private GVTBuilder builder; private RootGraphicsNode rootGN; Filter(Document entireLabelSVGw3cDoc, String labelOutlineId) throws Exception { doc = entireLabelSVGw3cDoc; Element labelOutlineElement = entireLabelSVGw3cDoc .getElementById(labelOutlineId); String shapeType = labelOutlineElement.getTagName(); String points = ""; BootSvgAndCssDom(); } public void updateElementsUsingCSSStyle( CSSConditionalSelector cssConditionalSelector, BridgeContext bridgeContext, CompositeGraphicsNode compositeGraphicsNode) { List rootGNchildren = compositeGraphicsNode.getChildren(); int gnsize = compositeGraphicsNode.size(); for (ListIterator gniterator = rootGNchildren.listIterator(); gniterator .hasNext();) { Object object = gniterator.next(); if (object instanceof CompositeGraphicsNode) { updateElementsUsingCSSStyle(cssConditionalSelector, bridgeContext, (CompositeGraphicsNode) object); } else { if (object instanceof GraphicsNode) { GraphicsNode graphicsNode = (GraphicsNode) object; Element element = ctx.getElement(graphicsNode); if (element != null) { String elementName = element.getTagName(); System.out.println("element.getTagName() " + elementName); String elementStyleClass = element.getAttribute("class"); boolean b = cssConditionalSelector.match(element, elementName); if (b) { int idx = compositeGraphicsNode.indexOf(graphicsNode); System.out.println("match"); compositeGraphicsNode.remove(idx); } } return; } } System.out.println("object.toString() " + object.toString()); } } private void BootSvgAndCssDom() throws Exception { userAgent = new UserAgentAdapter(); loader = new DocumentLoader(userAgent); ctx = new BridgeContext(userAgent, loader); ctx.setDynamicState(BridgeContext.DYNAMIC); builder = new GVTBuilder(); rootGN = (RootGraphicsNode) builder.build(ctx, doc); SVGCSSEngine cssEngine = (SVGCSSEngine) ctx.getCSSEngineForElement(doc .getDocumentElement()); int fontSizePropertyIndex = 0; for (int svgValueManagersIndex = 0; svgValueManagersIndex < (cssEngine.SVG_VALUE_MANAGERS).length; svgValueManagersIndex++) { if (cssEngine.SVG_VALUE_MANAGERS[svgValueManagersIndex] instanceof FontSizeManager) { System.out.println("found font size manager " + svgValueManagersIndex); fontSizePropertyIndex = svgValueManagersIndex; } } NodeList listOfTextElements = doc.getElementsByTagName("text"); int listOfTextElementsLength = listOfTextElements.getLength(); // assuming one style sheet? List styleSheetsList = cssEngine.getStyleSheetNodes(); CSSStyleSheetNode cssNode = (CSSStyleSheetNode) styleSheetsList.get(0); StyleSheet styleSheet = cssNode.getCSSStyleSheet(); int numRules = styleSheet.getSize(); for (int ruleIndex = 0; ruleIndex < numRules; ruleIndex++) { System.out.println("styleSheet.getRule(" + ruleIndex + ") " + styleSheet.getRule(ruleIndex)); Rule rule = styleSheet.getRule(ruleIndex); if (rule instanceof StyleRule) { StyleRule sr = ((StyleRule) rule); System.out.println("rule.getType() " + rule.getType()); StyleDeclaration sd = ((StyleRule) rule).getStyleDeclaration(); System.out.println("sr.toString() " + sr.toString()); System.out.println("sd.toString() " + sd.toString()); int sdlen = sd.size(); Vector listOfIndexToNegativeFontSizes; Vector listOfReplacementPostiveFontSizes; for (int sdindex = 0; sdindex < sdlen; sdindex++) { Value val = sd.getValue(sdindex); System.out.println("sd.getIndex(sdindex) " + sd.getIndex(sdindex)); if (sd.getIndex(sdindex) == fontSizePropertyIndex) { System.out.println("found font size"); float aFloatValue = Float.valueOf(val.getCssText()); if (aFloatValue < 0.0F) { System.out.println("is negative:" + aFloatValue); FloatValue positiveFloatValueObj = new FloatValue( CSSPrimitiveValue.CSS_NUMBER, Math.abs(aFloatValue)); sd.put(sdindex, positiveFloatValueObj, CSSPrimitiveValue.CSS_NUMBER, false); System.out.println("sd.toString() " + sd.toString()); SelectorList sl = sr.getSelectorList(); int sllen = sl.getLength(); for (int slindex = 0; slindex < sllen; slindex++) { // seek out effected elements Selector selector = sl.item(slindex); if (selector instanceof CSSConditionalSelector) { CSSConditionalSelector cssConditionalSelector = (CSSConditionalSelector) selector; String styleClass = cssConditionalSelector.toString(); System.out.println("cssConditionalSelector.toString() " + styleClass); updateElementsUsingCSSStyle(cssConditionalSelector, ctx, rootGN); for (int indexOfTextElements = 0; indexOfTextElements < listOfTextElementsLength; indexOfTextElements++) { Element aTextElement = (Element) listOfTextElements .item(indexOfTextElements); String elementStyleClass = aTextElement .getAttribute("class"); boolean b = cssConditionalSelector.match(aTextElement, aTextElement.getTagName()); if (b) { GraphicsNode elementGN = ctx .getGraphicsNode(aTextElement); int gni = rootGN.indexOf(elementGN); rootGN.remove(elementGN); System.out.println(elementGN.toString()); } } } } } } } // end style declaration iteration System.out.println("StyleRule"); } // end instanceof style rule System.out.println("rule.toString()" + rule.toString()); } System.out.println("styleSheet.getSize() " + styleSheet.getSize()); System.out.println("styleSheet.toString() " + styleSheet.toString()); int numCssNodes = styleSheetsList.size(); System.out.println("got here"); } } However, the batik system is not completely updated - because when I then go on to process the drawing - I still get the same exception. So my question is - how to update the entire batik system? I've already started to look into this - there is this thing called RunnableQueue. But again, the documentation is almost non-existent - i.e. here and also: http://xmlgraphics.apache.org/batik/ I think this code would be useful to anyone who wants to update the CSS whether or not it is compliant. Please help. -- View this message in context: http://www.nabble.com/Modifying-internal-CSS-in-SVG-document---partial-solution---need-help-with-batik-re-draw-update-tp16275087p16275087.html Sent from the Batik - Users mailing list archive at Nabble.com. --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
