Hi, all,
Last night, user <giordano> came onto the channel and showed an SVG file exported from Visio, which did not render properly on Inkscape. Turns out that the file has a separation of style specification into <style> and class="" references like this:
<style type="text/css"> <![CDATA[ .st1 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72} .st2 {fill:#ffffff;stroke:#000000;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72} ...etc....
and later referenced like:
<rect v:rectContext="foreign" x="0" y="892.83" width="2327.33" height="807.957" class="st1"/>
I know that this is exactly the missing CSS functionality that we have on the roadmap, but is it really that hard to implement? I made a quickie Java class that loads up the css definitions into a HashMap, and later when the class="" tags use the refs, replaces the class="" with style="". So that line would look like this now:
<rect v:rectContext="foreign" x="0" y="892.83" width="2327.33" height="807.957" style="fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.72"/>
Could we not, in the interim before we integrate full libcroco functionality, parse the <style type="text/css"> element into a std::map<std::string, std::string> and do the same substitution at load-time?
I attached the Java source to show how simple this method would be.
Bob
import java.io.File; import java.util.StringTokenizer; import java.util.HashMap; import java.util.Iterator;
import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerConfigurationException;
import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.SAXException;
class VisioFilter {
HashMap<String, String> styles;
void error(String msg) { System.out.println("VisioFilter:" + msg); }
String getElementText(Element elem) { StringBuffer buf = new StringBuffer(); NodeList nl = elem.getChildNodes(); for (int i=0 ; i<nl.getLength() ; i++) { Node n = nl.item(i); int type = n.getNodeType(); if (type == Node.TEXT_NODE || type == Node.CDATA_SECTION_NODE) { buf.append(n.getNodeValue()); } } return buf.toString(); }
boolean processStyleElement(String str) { StringTokenizer bigSt = new StringTokenizer(str, "\n\r"); while (bigSt.hasMoreTokens()) { String line = bigSt.nextToken(); //System.out.println("line:"+line); StringTokenizer st = new StringTokenizer(line, "{}"); if (!st.hasMoreTokens()) continue; String name = st.nextToken(); name = name.trim(); if (!name.startsWith(".")) continue; name=name.substring(1); if (!st.hasMoreTokens()) continue; String style = st.nextToken(); styles.put(name, style); }
/* for (Iterator<String> iter = styles.keySet().iterator() ; iter.hasNext() ; ) { String key = iter.next(); System.out.println("name:'"+key + "' style:'"+styles.get(key)+"'"); } */
return true; }
void substituteClassAttribute(Element elem) { String className = elem.getAttribute("class"); if (className!=null && className.length()>0) { //System.out.println("found class:"+className); String style = styles.get(className); if (style!=null) { elem.removeAttribute("class"); elem.setAttribute("style", style); //System.out.println("replaced with:"+style); } }
NodeList nl = elem.getChildNodes(); for (int i=0 ; i< nl.getLength() ; i++) { Node n = nl.item(i); if (n.getNodeType() != Node.ELEMENT_NODE) continue; Element childElem = (Element)n; substituteClassAttribute(childElem); } }
public boolean processFile(String inFile, String outFile) { System.out.println("############### PARSING"); DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); DocumentBuilder db = null; try { db = dbf.newDocumentBuilder(); } catch (ParserConfigurationException pce) { error("processFile:" + pce); return false; }
File inf = new File(inFile); Document doc = null; try { doc = db.parse(inf); } catch (Exception se)//saxexception + ioexception { error("processFile:" + se); return false; }
NodeList nl = doc.getElementsByTagName("style"); if (nl.getLength() < 1) { error("no style elements"); return false; }
for (int i=0 ; i<nl.getLength() ; i++) { Node n = nl.item(i); if (n.getNodeType() != Node.ELEMENT_NODE) continue; Element elem = (Element)n; String type = elem.getAttribute("type"); if (type == null || !"text/css".equals(type)) continue; String val = getElementText(elem); processStyleElement(val); }
System.out.println("############### PROCESSING"); substituteClassAttribute(doc.getDocumentElement());
System.out.println("############### WRITING");
File outf = new File(outFile); TransformerFactory tf = TransformerFactory.newInstance(); Source source = new DOMSource(doc); Result result = new StreamResult(outf); Transformer trans; try { trans = tf.newTransformer(); trans.transform(source, result); } catch (Exception tce) { error("processFile:" + tce); return false; }
return true; }
public VisioFilter() { styles = new HashMap<String, String>(); }
public static void main(String argv[]) {
if (argv.length!=2) { System.out.println("usage: VisioFilter infile outfile"); return; }
String inFile = argv[0]; String outFile = argv[1];
VisioFilter vf = new VisioFilter(); vf.processFile(inFile, outFile);
}
}//end class