Code

Add new rearranged /dom directory
[inkscape.git] / src / dom / odf / SvgOdg.cpp
diff --git a/src/dom/odf/SvgOdg.cpp b/src/dom/odf/SvgOdg.cpp
new file mode 100644 (file)
index 0000000..f551147
--- /dev/null
@@ -0,0 +1,1551 @@
+/**\r
+ *\r
+ * This is a small experimental class for converting between\r
+ * SVG and OpenDocument .odg files.   This code is not intended\r
+ * to be a permanent solution for SVG-to-ODG conversion.  Rather,\r
+ * it is a quick-and-easy test bed for ideas which will be later\r
+ * recoded into C++.\r
+ *\r
+ * ---------------------------------------------------------------------\r
+ *\r
+ * SvgOdg - A program to experiment with conversions between SVG and ODG\r
+ * Copyright (C) 2006 Bob Jamison\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 3 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software Foundation,\r
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA\r
+ *\r
+ * For more information, please write to rwjj@earthlink.net\r
+ *\r
+ */\r
+\r
+\r
+/**\r
+ *\r
+ */\r
+public class SvgOdg\r
+{\r
+\r
+\r
+\r
+/**\r
+ * Namespace declarations\r
+ */\r
+public static final String SVG_NS =\r
+    "http://www.w3.org/2000/svg";\r
+public static final String XLINK_NS =\r
+    "http://www.w3.org/1999/xlink";\r
+public static final String ODF_NS =\r
+    "urn:oasis:names:tc:opendocument:xmlns:office:1.0";\r
+public static final String ODG_NS =\r
+    "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0";\r
+public static final String ODSVG_NS =\r
+    "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0";\r
+\r
+\r
+DecimalFormat nrfmt;\r
+//static final double pxToCm = 0.0339;\r
+static final double pxToCm  = 0.0275;\r
+static final double piToRad = 0.0174532925;\r
+BufferedWriter out;\r
+BufferedReader in;\r
+int imageNr;\r
+int styleNr;\r
+\r
+//########################################################################\r
+//#  M E S S A G E S\r
+//########################################################################\r
+\r
+/**\r
+ *\r
+ */\r
+void err(String msg)\r
+{\r
+    System.out.println("SvgOdg ERROR:" + msg);\r
+}\r
+\r
+/**\r
+ *\r
+ */\r
+void trace(String msg)\r
+{\r
+    System.out.println("SvgOdg:" + msg);\r
+}\r
+\r
+\r
+\r
+\r
+//########################################################################\r
+//#  I N P U T    /    O U T P U T\r
+//########################################################################\r
+\r
+boolean po(String s)\r
+{\r
+    try\r
+        {\r
+        out.write(s);\r
+        }\r
+    catch(IOException e)\r
+        {\r
+        return false;\r
+        }\r
+    return true;\r
+}\r
+\r
+//########################################################################\r
+//#  U T I L I T Y\r
+//########################################################################\r
+\r
+public void dumpDocument(Document doc)\r
+{\r
+    String s = "";\r
+    try\r
+        {\r
+        TransformerFactory factory = TransformerFactory.newInstance();\r
+        Transformer trans = factory.newTransformer();\r
+        DOMSource source = new DOMSource(doc);\r
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();\r
+        StreamResult result = new StreamResult(bos);\r
+        trans.transform(source, result);\r
+        byte buf[] = bos.toByteArray();\r
+        s = new String(buf);\r
+        }\r
+    catch (javax.xml.transform.TransformerException e)\r
+        {\r
+        }\r
+    trace("doc:" + s);\r
+}\r
+\r
+\r
+//########################################################################\r
+//#  I N N E R    C L A S S    ImageInfo\r
+//########################################################################\r
+public class ImageInfo\r
+{\r
+String name;\r
+String newName;\r
+byte   buf[];\r
+\r
+public String getName()\r
+{\r
+    return name;\r
+}\r
+\r
+public String getNewName()\r
+{\r
+    return newName;\r
+}\r
+\r
+\r
+public byte[] getBuf()\r
+{\r
+    return buf;\r
+}\r
+\r
+public ImageInfo(String name, String newName, byte buf[])\r
+{\r
+    this.name = name;\r
+    this.name = newName;\r
+    this.buf  = buf;\r
+}\r
+\r
+}\r
+//########################################################################\r
+//#  I N N E R    C L A S S    StyleInfo\r
+//########################################################################\r
+public class StyleInfo\r
+{\r
+\r
+String name;\r
+public String getName()\r
+{\r
+    return name;\r
+}\r
+\r
+String cssStyle;\r
+public String getCssStyle()\r
+{\r
+    return cssStyle;\r
+}\r
+\r
+String stroke;\r
+public String getStroke()\r
+{\r
+    return stroke;\r
+}\r
+\r
+String strokeColor;\r
+public String getStrokeColor()\r
+{\r
+    return strokeColor;\r
+}\r
+\r
+String strokeWidth;\r
+public String getStrokeWidth()\r
+{\r
+    return strokeWidth;\r
+}\r
+\r
+String fill;\r
+public String getFill()\r
+{\r
+    return fill;\r
+}\r
+\r
+String fillColor;\r
+public String getFillColor()\r
+{\r
+    return fillColor;\r
+}\r
+\r
+public StyleInfo(String name, String cssStyle)\r
+{\r
+    this.name     = name;\r
+    this.cssStyle = cssStyle;\r
+    fill  = "none";\r
+    stroke = "none";\r
+}\r
+\r
+}\r
+//########################################################################\r
+//#  E N D    I N N E R    C L A S S E S\r
+//########################################################################\r
+\r
+\r
+\r
+\r
+//########################################################################\r
+//#  V A R I A B L E S\r
+//########################################################################\r
+\r
+/**\r
+ * ODF content.xml file\r
+ */\r
+Document content;\r
+public Document getContent()\r
+{\r
+    return content;\r
+}\r
+\r
+/**\r
+ * ODF meta.xml file\r
+ */\r
+Document meta;\r
+public Document getMeta()\r
+{\r
+    return meta;\r
+}\r
+\r
+/**\r
+ * SVG file\r
+ */\r
+Document       svg;\r
+public Document getSvg()\r
+{\r
+    return svg;\r
+}\r
+\r
+/**\r
+ * Loaded ODF or SVG images\r
+ */\r
+ArrayList<ImageInfo> images;\r
+public ArrayList<ImageInfo> getImages()\r
+{\r
+    return images;\r
+}\r
+\r
+/**\r
+ * CSS styles\r
+ */\r
+HashMap<String, StyleInfo> styles;\r
+public HashMap<String, StyleInfo> getStyles()\r
+{\r
+    return styles;\r
+}\r
+\r
+\r
+\r
+\r
+\r
+//########################################################################\r
+//#  S V G    T O    O D F\r
+//########################################################################\r
+\r
+class PathData\r
+{\r
+String cmd;\r
+double nr[];\r
+PathData(String s, double buf[])\r
+{\r
+    cmd=s; nr = buf;\r
+}\r
+}\r
+\r
+double getPathNum(StringTokenizer st)\r
+{\r
+    if (!st.hasMoreTokens())\r
+        return 0.0;\r
+    String s = st.nextToken();\r
+    double nr = Double.parseDouble(s);\r
+    return nr;\r
+}\r
+\r
+String parsePathData(String pathData, double bounds[])\r
+{\r
+    double minx = Double.MAX_VALUE;\r
+    double maxx = Double.MIN_VALUE;\r
+    double miny = Double.MAX_VALUE;\r
+    double maxy = Double.MIN_VALUE;\r
+    //trace("#### pathData:" + pathData);\r
+    ArrayList<PathData> data = new ArrayList<PathData>();\r
+    StringTokenizer st = new StringTokenizer(pathData, " ,");\r
+    while (true)\r
+        {\r
+        String s = st.nextToken();\r
+        if ( s.equals("z") || s.equals("Z") )\r
+            {\r
+            PathData pd = new PathData(s, new double[0]);\r
+            data.add(pd);\r
+            break;\r
+            }\r
+        else if ( s.equals("h") || s.equals("H") )\r
+            {\r
+            double d[] = new double[1];\r
+            d[0] = getPathNum(st) * pxToCm;\r
+            if (d[0] < minx)  minx = d[0];\r
+            else if (d[0] > maxx) maxx = d[0];\r
+            PathData pd = new PathData(s, d);\r
+            data.add(pd);\r
+            }\r
+        else if ( s.equals("v") || s.equals("V") )\r
+            {\r
+            double d[] = new double[1];\r
+            d[0] = getPathNum(st) * pxToCm;\r
+            if (d[0] < miny) miny = d[0];\r
+            else if (d[0] > maxy) maxy = d[0];\r
+            PathData pd = new PathData(s, d);\r
+            data.add(pd);\r
+            }\r
+        else if ( s.equals("m") || s.equals("M") ||\r
+             s.equals("l") || s.equals("L") ||\r
+             s.equals("t") || s.equals("T")  )\r
+            {\r
+            double d[] = new double[2];\r
+            d[0] = getPathNum(st) * pxToCm;\r
+            d[1] = getPathNum(st) * pxToCm;\r
+            if (d[0] < minx)  minx = d[0];\r
+            else if (d[0] > maxx) maxx = d[0];\r
+            if (d[1] < miny) miny = d[1];\r
+            else if (d[1] > maxy) maxy = d[1];\r
+            PathData pd = new PathData(s, d);\r
+            data.add(pd);\r
+            }\r
+        else if ( s.equals("q") || s.equals("Q") ||\r
+             s.equals("s") || s.equals("S")  )\r
+            {\r
+            double d[] = new double[4];\r
+            d[0] = getPathNum(st) * pxToCm;\r
+            d[1] = getPathNum(st) * pxToCm;\r
+            if (d[0] < minx)  minx = d[0];\r
+            else if (d[0] > maxx) maxx = d[0];\r
+            if (d[1] < miny) miny = d[1];\r
+            else if (d[1] > maxy) maxy = d[1];\r
+            d[2] = getPathNum(st) * pxToCm;\r
+            d[3] = getPathNum(st) * pxToCm;\r
+            if (d[2] < minx)  minx = d[2];\r
+            else if (d[2] > maxx) maxx = d[2];\r
+            if (d[3] < miny) miny = d[3];\r
+            else if (d[3] > maxy) maxy = d[3];\r
+            PathData pd = new PathData(s, d);\r
+            data.add(pd);\r
+            }\r
+        else if ( s.equals("c") || s.equals("C") )\r
+            {\r
+            double d[] = new double[6];\r
+            d[0] = getPathNum(st) * pxToCm;\r
+            d[1] = getPathNum(st) * pxToCm;\r
+            if (d[0] < minx)  minx = d[0];\r
+            else if (d[0] > maxx) maxx = d[0];\r
+            if (d[1] < miny) miny = d[1];\r
+            else if (d[1] > maxy) maxy = d[1];\r
+            d[2] = getPathNum(st) * pxToCm;\r
+            d[3] = getPathNum(st) * pxToCm;\r
+            if (d[2] < minx)  minx = d[2];\r
+            else if (d[2] > maxx) maxx = d[2];\r
+            if (d[3] < miny) miny = d[3];\r
+            else if (d[3] > maxy) maxy = d[3];\r
+            d[4] = getPathNum(st) * pxToCm;\r
+            d[5] = getPathNum(st) * pxToCm;\r
+            if (d[4] < minx)  minx = d[4];\r
+            else if (d[4] > maxx) maxx = d[4];\r
+            if (d[5] < miny) miny = d[5];\r
+            else if (d[5] > maxy) maxy = d[5];\r
+            PathData pd = new PathData(s, d);\r
+            data.add(pd);\r
+            }\r
+        else if ( s.equals("a") || s.equals("A") )\r
+            {\r
+            double d[] = new double[6];\r
+            d[0] = getPathNum(st) * pxToCm;\r
+            d[1] = getPathNum(st) * pxToCm;\r
+            if (d[0] < minx)  minx = d[0];\r
+            else if (d[0] > maxx) maxx = d[0];\r
+            if (d[1] < miny) miny = d[1];\r
+            else if (d[1] > maxy) maxy = d[1];\r
+            d[2] = getPathNum(st) * piToRad;//angle\r
+            d[3] = getPathNum(st) * piToRad;//angle\r
+            d[4] = getPathNum(st) * pxToCm;\r
+            d[5] = getPathNum(st) * pxToCm;\r
+            if (d[4] < minx)  minx = d[4];\r
+            else if (d[4] > maxx) maxx = d[4];\r
+            if (d[5] < miny) miny = d[5];\r
+            else if (d[5] > maxy) maxy = d[5];\r
+            PathData pd = new PathData(s, d);\r
+            data.add(pd);\r
+            }\r
+        //trace("x:" + x + " y:" + y);\r
+        }\r
+\r
+    trace("minx:"  + minx + " maxx:" + maxx +\r
+          " miny:" + miny + " maxy:" + maxy);\r
+\r
+    StringBuffer buf = new StringBuffer();\r
+    for (PathData pd : data)\r
+        {\r
+        buf.append(pd.cmd);\r
+        buf.append(" ");\r
+        for (double d:pd.nr)\r
+            {\r
+            buf.append(nrfmt.format(d * 1000.0));\r
+            buf.append(" ");\r
+            }\r
+        }\r
+\r
+    bounds[0] = minx;\r
+    bounds[1] = miny;\r
+    bounds[2] = maxx;\r
+    bounds[3] = maxy;\r
+\r
+    return buf.toString();\r
+}\r
+\r
+\r
+\r
+boolean parseTransform(String transStr, AffineTransform trans)\r
+{\r
+    trace("== transform:"+ transStr);\r
+    StringTokenizer st = new StringTokenizer(transStr, ")");\r
+    while (st.hasMoreTokens())\r
+        {\r
+        String chunk = st.nextToken();\r
+        StringTokenizer st2 = new StringTokenizer(chunk, " ,(");\r
+        if (!st2.hasMoreTokens())\r
+            continue;\r
+        String name = st2.nextToken();\r
+        trace("   ++name:"+ name);\r
+        if (name.equals("matrix"))\r
+            {\r
+            double v[] = new double[6];\r
+            for (int i=0 ; i<6 ; i++)\r
+                {\r
+                if (!st2.hasMoreTokens())\r
+                    break;\r
+                v[i] = Double.parseDouble(st2.nextToken()) * pxToCm;\r
+                }\r
+            AffineTransform mat = new AffineTransform(v);\r
+            trans.concatenate(mat);\r
+            }\r
+        else if (name.equals("translate"))\r
+            {\r
+            double dx = 0.0;\r
+            double dy = 0.0;\r
+            if (!st2.hasMoreTokens())\r
+                continue;\r
+            dx = Double.parseDouble(st2.nextToken()) * pxToCm;\r
+            if (st2.hasMoreTokens())\r
+                dy = Double.parseDouble(st2.nextToken()) * pxToCm;\r
+            trans.translate(dx, dy);\r
+            }\r
+        else if (name.equals("scale"))\r
+            {\r
+            double sx = 1.0;\r
+            double sy = 1.0;\r
+            if (!st2.hasMoreTokens())\r
+                continue;\r
+            sx = sy = Double.parseDouble(st2.nextToken());\r
+            if (st2.hasMoreTokens())\r
+                sy = Double.parseDouble(st2.nextToken());\r
+            trans.scale(sx, sy);\r
+            }\r
+        else if (name.equals("rotate"))\r
+            {\r
+            double r  = 0.0;\r
+            double cx = 0.0;\r
+            double cy = 0.0;\r
+            if (!st2.hasMoreTokens())\r
+                continue;\r
+            r = Double.parseDouble(st2.nextToken()) * piToRad;\r
+            if (st2.hasMoreTokens())\r
+                {\r
+                cx = Double.parseDouble(st2.nextToken()) * pxToCm;\r
+                if (!st2.hasMoreTokens())\r
+                    continue;\r
+                cy = Double.parseDouble(st2.nextToken()) * pxToCm;\r
+                trans.rotate(r, cx, cy);\r
+                }\r
+            else\r
+                {\r
+                trans.rotate(r);\r
+                }\r
+            }\r
+        else if (name.equals("skewX"))\r
+            {\r
+            double angle  = 0.0;\r
+            if (!st2.hasMoreTokens())\r
+                continue;\r
+            angle = Double.parseDouble(st2.nextToken());\r
+            trans.shear(angle, 0.0);\r
+            }\r
+        else if (name.equals("skewY"))\r
+            {\r
+            double angle  = 0.0;\r
+            if (!st2.hasMoreTokens())\r
+                continue;\r
+            angle = Double.parseDouble(st2.nextToken());\r
+            trans.shear(0.0, angle);\r
+            }\r
+        }\r
+    return true;\r
+}\r
+\r
+\r
+\r
+String coordToOdg(String sval)\r
+{\r
+    double nr = Double.parseDouble(sval);\r
+    nr = nr * pxToCm;\r
+    String s = nrfmt.format(nr) + "cm";\r
+    return s;\r
+}\r
+\r
+\r
+boolean writeSvgAttributes(Element elem, AffineTransform trans)\r
+{\r
+    NamedNodeMap attrs = elem.getAttributes();\r
+    String ename = elem.getLocalName();\r
+    for (int i=0 ; i<attrs.getLength() ; i++)\r
+        {\r
+        Attr attr = (Attr)attrs.item(i);\r
+        String aname = attr.getName();\r
+        String aval  = attr.getValue();\r
+        if (aname.startsWith("xmlns"))\r
+            continue;\r
+        else if (aname.equals("d"))//already handled\r
+            continue;\r
+        else if (aname.startsWith("transform"))\r
+            {\r
+            parseTransform(aval, trans);\r
+            continue;\r
+            }\r
+        else if (aname.equals("style"))\r
+            {\r
+            StyleInfo style = styles.get(aval);\r
+            if (style != null)\r
+                {\r
+                po(" draw:style-name=\"");\r
+                po(style.getName());\r
+                po("\"");\r
+                }\r
+            continue;\r
+            }\r
+        if (aname.equals("x") || aname.equals("y") ||\r
+            aname.equals("width") || aname.equals("height"))\r
+            {\r
+            aval = coordToOdg(aval);\r
+            }\r
+        if ("id".equals(aname))\r
+            po(" ");\r
+        else if ("transform".equals(aname))\r
+            po(" draw:");\r
+        else\r
+            po(" svg:");\r
+        po(aname);\r
+        po("=\"");\r
+        po(aval);\r
+        po("\"");\r
+        }\r
+\r
+    //Output the current transform\r
+    if (!trans.isIdentity() &&\r
+        !(\r
+          ename.equals("g")     ||\r
+          ename.equals("defs")  ||\r
+          ename.equals("metadata")\r
+         )\r
+        )\r
+        {\r
+        double v[] = new double[6];\r
+        trans.getMatrix(v);\r
+        po(" draw:transform=\"matrix(" +\r
+            nrfmt.format(v[0]) + "," +\r
+            nrfmt.format(v[1]) + "," +\r
+            nrfmt.format(v[2]) + "," +\r
+            nrfmt.format(v[3]) + "," +\r
+            nrfmt.format(v[4]) + "," +\r
+            nrfmt.format(v[5]) + ")\"");\r
+        }\r
+    return true;\r
+}\r
+\r
+public boolean writeOdfContent(Element elem, AffineTransform trans)\r
+{\r
+    String ns = elem.getNamespaceURI();\r
+    String tagName = elem.getLocalName();\r
+    //trace("ns:" + ns + " tagName:" + tagName);\r
+    if (!ns.equals(SVG_NS))\r
+        return true;\r
+    if (tagName.equals("svg"))\r
+        {\r
+        NodeList children = elem.getChildNodes();\r
+        for (int i=0 ; i<children.getLength() ; i++)\r
+            {\r
+            Node n = children.item(i);\r
+            if (n.getNodeType() == Node.ELEMENT_NODE)\r
+                if (!writeOdfContent((Element)n,\r
+                     (AffineTransform)trans.clone()))\r
+                    return false;\r
+            }\r
+        }\r
+    else if (tagName.equals("g"))\r
+        {\r
+        //String transform = elem.getAttribute("transform");\r
+        po("<draw:g");\r
+        writeSvgAttributes(elem, trans); po(">\n");\r
+        NodeList children = elem.getChildNodes();\r
+        for (int i=0 ; i<children.getLength() ; i++)\r
+            {\r
+            Node n = children.item(i);\r
+            if (n.getNodeType() == Node.ELEMENT_NODE)\r
+                if (!writeOdfContent((Element)n,\r
+                     (AffineTransform)trans.clone()))\r
+                    return false;\r
+            }\r
+        po("</draw:g>\n");\r
+        }\r
+    else if (tagName.equals("text"))\r
+        {\r
+        String x         = coordToOdg(elem.getAttribute("x"));\r
+        String y         = coordToOdg(elem.getAttribute("y"));\r
+        String width     = "5cm";\r
+        String height    = "2cm";\r
+        String txt       = elem.getTextContent();\r
+        po("<draw:frame draw:style-name=\"grx1\" draw:layer=\"layout\" ");\r
+        po("svg:x=\"" + x + "\" svg:y=\"" + y + "\" ");\r
+        po("svg:width=\""  + width + "\" svg:height=\"" + height + "\"");\r
+        po(">\n");\r
+        po("    <draw:text-box draw:auto-grow-height=\"true\" draw:auto-grow-width=\"true\">\n");\r
+        po("    <text:p text:style-name=\"P1\"> " + txt + "</text:p>\n");\r
+        po("    </draw:text-box>\n");\r
+        po("</draw:frame>\n");\r
+        return true;\r
+        }\r
+    else if (tagName.equals("image"))\r
+        {\r
+        String x         = coordToOdg(elem.getAttribute("x"));\r
+        String y         = coordToOdg(elem.getAttribute("y"));\r
+        String width     = coordToOdg(elem.getAttribute("width"));\r
+        String height    = coordToOdg(elem.getAttribute("height"));\r
+        String imageName = elem.getAttributeNS(XLINK_NS, "href");\r
+        po("<draw:frame draw:style-name=\"grx1\" draw:layer=\"layout\" ");\r
+        po("svg:x=\"" + x + "\" svg:y=\"" + y + "\" ");\r
+        po("svg:width=\""  + width + "\" svg:height=\"" + height + "\">\n");\r
+        po("    <draw:image xlink:href=\"Pictures/" + imageName + "\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"><text:p/></draw:image>\n");\r
+        po("</draw:frame>\n");\r
+        return true;\r
+        }\r
+    else if (tagName.equals("path"))\r
+        {\r
+        double bounds[] = new double[4];\r
+        String d      = elem.getAttribute("d");\r
+        String newd   = parsePathData(d, bounds);\r
+        double x      = bounds[0];\r
+        double y      = bounds[1];\r
+        double width  = (bounds[2]-bounds[0]);\r
+        double height = (bounds[3]-bounds[1]);\r
+        po("<draw:path draw:layer=\"layout\" \n");\r
+        po("    svg:x=\"" + nrfmt.format(x) + "cm\" ");\r
+        po("svg:y=\"" + nrfmt.format(y) + "cm\" ");\r
+        po("svg:width=\""  + nrfmt.format(width) + "cm\" ");\r
+        po("svg:height=\"" + nrfmt.format(height) + "cm\" ");\r
+        po("svg:viewBox=\"0.0 0.0 " +\r
+             nrfmt.format(bounds[2] * 1000.0) + " " +\r
+             nrfmt.format(bounds[3] * 1000.0) + "\"\n");\r
+        po("    svg:d=\"" + newd + "\"\n   ");\r
+\r
+        writeSvgAttributes(elem, trans); po("/>\n");\r
+        //po("    svg:d=\"" + d + "\"/>\n");\r
+        return true;\r
+        }\r
+\r
+    else\r
+        {\r
+        //Verbatim tab mapping\r
+        po("<draw:"); po(tagName);\r
+        writeSvgAttributes(elem, trans); po(">\n");\r
+        po("</draw:"); po(tagName); po(">\n");\r
+        }\r
+\r
+    return true;\r
+}\r
+\r
+\r
+boolean writeOdfContent(ZipOutputStream outs)\r
+{\r
+    try\r
+        {\r
+        ZipEntry ze = new ZipEntry("content.xml");\r
+        outs.putNextEntry(ze);\r
+        out = new BufferedWriter(new OutputStreamWriter(outs));\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        return false;\r
+        }\r
+\r
+    NodeList res = svg.getElementsByTagNameNS(SVG_NS, "svg");\r
+    if (res.getLength() < 1)\r
+        {\r
+        err("saveOdf: no <svg> root in .svg file");\r
+        return false;\r
+        }\r
+    Element root = (Element)res.item(0);\r
+\r
+\r
+    po("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");\r
+    po("<office:document-content\n");\r
+    po("    xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");\r
+    po("    xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");\r
+    po("    xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");\r
+    po("    xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");\r
+    po("    xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");\r
+    po("    xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");\r
+    po("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");\r
+    po("    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");\r
+    po("    xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");\r
+    po("    xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");\r
+    po("    xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");\r
+    po("    xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");\r
+    po("    xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");\r
+    po("    xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");\r
+    po("    xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");\r
+    po("    xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");\r
+    po("    xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");\r
+    po("    xmlns:ooo=\"http://openoffice.org/2004/office\"\n");\r
+    po("    xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");\r
+    po("    xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");\r
+    po("    xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");\r
+    po("    xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");\r
+    po("    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");\r
+    po("    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");\r
+    po("    xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");\r
+    po("    xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");\r
+    po("    office:version=\"1.0\">\n");\r
+    po("\n");\r
+    po("\n");\r
+    po("<office:scripts/>\n");\r
+    po("<office:automatic-styles>\n");\r
+    po("<style:style style:name=\"dp1\" style:family=\"drawing-page\"/>\n");\r
+    po("<style:style style:name=\"grx1\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");\r
+    po("  <style:graphic-properties draw:stroke=\"none\" draw:fill=\"solid\" draw:textarea-horizontal-align=\"center\" draw:textarea-vertical-align=\"middle\" draw:color-mode=\"standard\" draw:luminance=\"0%\" draw:contrast=\"0%\" draw:gamma=\"100%\" draw:red=\"0%\" draw:green=\"0%\" draw:blue=\"0%\" fo:clip=\"rect(0cm 0cm 0cm 0cm)\" draw:image-opacity=\"100%\" style:mirror=\"none\"/>\n");\r
+    po("</style:style>\n");\r
+    po("<style:style style:name=\"P1\" style:family=\"paragraph\">\n");\r
+    po("  <style:paragraph-properties fo:text-align=\"center\"/>\n");\r
+    po("</style:style>\n");\r
+\r
+    //##  Dump our style table\r
+    for (StyleInfo s : styles.values())\r
+        {\r
+        po("<style:style style:name=\"" + s.getName() + "\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");\r
+        po("  <style:graphic-properties");\r
+        po(" draw:fill=\"" + s.getFill() + "\"");\r
+        if (!s.getFill().equals("none"))\r
+            po(" draw:fill-color=\"" + s.getFillColor() + "\"");\r
+        po(" draw:stroke=\"" + s.getStroke() + "\"");\r
+        if (!s.getStroke().equals("none"))\r
+            {\r
+            po(" svg:stroke-width=\"" + s.getStrokeWidth() + "\"");\r
+            po(" svg:stroke-color=\"" + s.getStrokeColor() + "\"");\r
+            }\r
+        po("/>\n");\r
+        po("</style:style>\n");\r
+        }\r
+    po("</office:automatic-styles>\n");\r
+    po("\n");\r
+    po("\n");\r
+    po("<office:body>\n");\r
+    po("<office:drawing>\n");\r
+    po("<draw:page draw:name=\"page1\" draw:style-name=\"dp1\" draw:master-page-name=\"Default\">\n");\r
+    po("\n\n\n");\r
+    AffineTransform trans = new AffineTransform();\r
+    //trans.scale(12.0, 12.0);\r
+    po("<!-- ######### CONVERSION FROM SVG STARTS ######## -->\n");\r
+    writeOdfContent(root, trans);\r
+    po("<!-- ######### CONVERSION FROM SVG ENDS ######## -->\n");\r
+    po("\n\n\n");\r
+\r
+    po("</draw:page>\n");\r
+    po("</office:drawing>\n");\r
+    po("</office:body>\n");\r
+    po("</office:document-content>\n");\r
+\r
+\r
+    try\r
+        {\r
+        out.flush();\r
+        outs.closeEntry();\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        err("writeOdfContent:" + e);\r
+        return false;\r
+        }\r
+    return true;\r
+}\r
+\r
+boolean writeOdfMeta(ZipOutputStream outs)\r
+{\r
+    try\r
+        {\r
+        ZipEntry ze = new ZipEntry("meta.xml");\r
+        outs.putNextEntry(ze);\r
+        out = new BufferedWriter(new OutputStreamWriter(outs));\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        return false;\r
+        }\r
+\r
+    po("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");\r
+    po("<office:document-meta\n");\r
+    po("    xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");\r
+    po("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");\r
+    po("    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");\r
+    po("    xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");\r
+    po("    xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");\r
+    po("    xmlns:ooo=\"http://openoffice.org/2004/office\"\n");\r
+    po("    xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");\r
+    po("    xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");\r
+    po("    office:version=\"1.0\">\n");\r
+    po("<office:meta>\n");\r
+    po("    <meta:generator>Inkscape-0.43</meta:generator>\n");\r
+    po("    <meta:initial-creator>clark kent</meta:initial-creator>\n");\r
+    po("    <meta:creation-date>2005-12-10T10:55:13</meta:creation-date>\n");\r
+    po("    <dc:creator>clark kent</dc:creator>\n");\r
+    po("    <dc:date>2005-12-10T10:56:20</dc:date>\n");\r
+    po("    <dc:language>en-US</dc:language>\n");\r
+    po("    <meta:editing-cycles>2</meta:editing-cycles>\n");\r
+    po("    <meta:editing-duration>PT1M13S</meta:editing-duration>\n");\r
+    po("    <meta:user-defined meta:name=\"Info 1\"/>\n");\r
+    po("    <meta:user-defined meta:name=\"Info 2\"/>\n");\r
+    po("    <meta:user-defined meta:name=\"Info 3\"/>\n");\r
+    po("    <meta:user-defined meta:name=\"Info 4\"/>\n");\r
+    po("    <meta:document-statistic meta:object-count=\"2\"/>\n");\r
+    po("</office:meta>\n");\r
+    po("</office:document-meta>\n");\r
+\r
+\r
+    try\r
+        {\r
+        out.flush();\r
+        outs.closeEntry();\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        err("writeOdfContent:" + e);\r
+        return false;\r
+        }\r
+    return true;\r
+}\r
+\r
+\r
+boolean writeOdfManifest(ZipOutputStream outs)\r
+{\r
+    try\r
+        {\r
+        ZipEntry ze = new ZipEntry("META-INF/manifest.xml");\r
+        outs.putNextEntry(ze);\r
+        out = new BufferedWriter(new OutputStreamWriter(outs));\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        return false;\r
+        }\r
+\r
+    po("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");\r
+    po("<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">\n");\r
+    po("<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n");\r
+    po("    <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.graphics\" manifest:full-path=\"/\"/>\n");\r
+    po("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n");\r
+    po("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>\n");\r
+    po("    <!--List our images here-->\n");\r
+    for (int i=0 ; i<images.size() ; i++)\r
+        {\r
+        ImageInfo ie = images.get(i);\r
+        String fname = ie.getName();\r
+        if (fname.length() < 5)\r
+            {\r
+            err("image file name too short:" + fname);\r
+            return false;\r
+            }\r
+        String ext = fname.substring(fname.length() - 4);\r
+        po("    <manifest:file-entry manifest:media-type=\"");\r
+        if (ext.equals(".gif"))\r
+            po("image/gif");\r
+        else if (ext.equals(".png"))\r
+            po("image/png");\r
+        else if (ext.equals(".jpg") || ext.equals(".jpeg"))\r
+            po("image/jpeg");\r
+        po("\" manifest:full-path=\"");\r
+        po(fname);\r
+        po("\"/>\n");\r
+        }\r
+    po("</manifest:manifest>\n");\r
+\r
+    try\r
+        {\r
+        out.flush();\r
+        outs.closeEntry();\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        err("writeOdfContent:" + e);\r
+        return false;\r
+        }\r
+    return true;\r
+}\r
+\r
+boolean writeOdfImages(ZipOutputStream outs)\r
+{\r
+    for (int i=0 ; i<images.size() ; i++)\r
+        {\r
+        ImageInfo ie = images.get(i);\r
+        try\r
+            {\r
+            String iname = "Pictures/" + ie.getName();\r
+            ZipEntry ze = new ZipEntry(iname);\r
+            outs.putNextEntry(ze);\r
+            outs.write(ie.getBuf());\r
+            outs.closeEntry();\r
+            }\r
+        catch (IOException e)\r
+            {\r
+            err("writing images:" + e);\r
+            return false;\r
+            }\r
+        }\r
+    return true;\r
+}\r
+\r
+/**\r
+ *\r
+ */\r
+public boolean writeOdf(OutputStream outs)\r
+{\r
+    try\r
+        {\r
+        ZipOutputStream zos = new ZipOutputStream(outs);\r
+        if (!writeOdfContent(zos))\r
+            return false;\r
+        if (!writeOdfManifest(zos))\r
+            return false;\r
+        if (!writeOdfMeta(zos))\r
+            return false;\r
+        if (!writeOdfImages(zos))\r
+            return false;\r
+        //if (!writeOdfStyles(zos))\r
+        //    return false;\r
+        zos.close();\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        err("closing ODF zip output stream:" + e);\r
+        return false;\r
+        }\r
+    return true;\r
+}\r
+\r
+\r
+\r
+/**\r
+ *\r
+ */\r
+public boolean saveOdf(String fileName)\r
+{\r
+    boolean ret = true;\r
+    try\r
+        {\r
+        FileOutputStream fos = new FileOutputStream(fileName);\r
+        ret = writeOdf(fos);\r
+        fos.close();\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        err("writing odf " + fileName + " : " + e);\r
+        return false;\r
+        }\r
+    return ret;\r
+}\r
+\r
+\r
+\r
+boolean parseCss(String css)\r
+{\r
+    trace("##### STYLE ### :" + css);\r
+    String name = "gr" + styleNr;\r
+    styleNr++;\r
+    StyleInfo si = new StyleInfo(name, css);\r
+    StringTokenizer st = new StringTokenizer(css, ";");\r
+    while (st.hasMoreTokens())\r
+        {\r
+        String style = st.nextToken();\r
+        //trace("    " + style);\r
+        int pos = style.indexOf(':');\r
+        if (pos < 1 || pos > style.length()-2)\r
+            continue;\r
+        String attrName = style.substring(0, pos);\r
+        String attrVal  = style.substring(pos+1);\r
+        trace("    =" + attrName + ':' + attrVal);\r
+        if ("stroke".equals(attrName))\r
+            {\r
+            si.stroke = "solid";\r
+            si.strokeColor = attrVal;\r
+            }\r
+        else if ("stroke-width".equals(attrName))\r
+            {\r
+            si.strokeWidth = attrVal;\r
+            }\r
+        else if ("fill".equals(attrName))\r
+            {\r
+            si.fill      = "solid";\r
+            si.fillColor = attrVal;\r
+            }\r
+        }\r
+    styles.put(css, si);\r
+    return true;\r
+}\r
+\r
+boolean readSvg(InputStream ins)\r
+{\r
+    //### LOAD XML\r
+    try\r
+        {\r
+        DocumentBuilderFactory factory =\r
+            DocumentBuilderFactory.newInstance();\r
+        factory.setNamespaceAware(true);\r
+        DocumentBuilder builder =\r
+            factory.newDocumentBuilder();\r
+        builder.setEntityResolver(new EntityResolver()\r
+               {\r
+               public InputSource resolveEntity(String publicId, String systemId)\r
+                   {\r
+                   return new InputSource(new ByteArrayInputStream(new byte[0]));\r
+                   }\r
+               });\r
+        Document doc = builder.parse(ins);\r
+        svg = doc;\r
+        }\r
+    catch (javax.xml.parsers.ParserConfigurationException e)\r
+        {\r
+        err("making DOM parser:"+e);\r
+        return false;\r
+        }\r
+    catch (org.xml.sax.SAXException e)\r
+        {\r
+        err("parsing svg document:"+e);\r
+        return false;\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        err("parsing svg document:"+e);\r
+        return false;\r
+        }\r
+    //dumpDocument(svg);\r
+\r
+    //### LOAD IMAGES\r
+    imageNr = 0;\r
+    images = new ArrayList<ImageInfo>();\r
+    NodeList res = svg.getElementsByTagNameNS(SVG_NS, "image");\r
+    for (int i=0 ; i<res.getLength() ; i++)\r
+        {\r
+        Element elem = (Element) res.item(i);\r
+        String fileName = elem.getAttributeNS(XLINK_NS, "href");\r
+        if (fileName == null)\r
+            {\r
+            err("No xlink:href pointer to image data for image");\r
+            return false;\r
+            }\r
+        File f = new File(fileName);\r
+        if (!f.exists())\r
+            {\r
+            err("image '" + fileName + "' does not exist");\r
+            return false;\r
+            }\r
+        int bufSize = (int)f.length();\r
+        byte buf[] = new byte[bufSize];\r
+        int pos = 0;\r
+        try\r
+            {\r
+            FileInputStream fis = new FileInputStream(fileName);\r
+            while (pos < bufSize)\r
+                {\r
+                int len = fis.read(buf, pos, bufSize - pos);\r
+                if (len < 0)\r
+                    break;\r
+                pos += len;\r
+                }\r
+            fis.close();\r
+            }\r
+        catch (IOException e)\r
+            {\r
+            err("reading image '" + fileName + "' :" + e);\r
+            return false;\r
+            }\r
+        if (pos != bufSize)\r
+            {\r
+            err("reading image entry.  expected " +\r
+                bufSize + ", found " + pos + " bytes.");\r
+            return false;\r
+            }\r
+        ImageInfo ie = new ImageInfo(fileName, fileName, buf);\r
+        images.add(ie);\r
+        }\r
+\r
+    //### Parse styles\r
+    styleNr = 0;\r
+    styles = new HashMap<String, StyleInfo>();\r
+    res = svg.getElementsByTagName("*");\r
+    for (int i=0 ; i<res.getLength() ; i++)\r
+        {\r
+        Element elem = (Element) res.item(i);\r
+        trace("elem:"+ elem.getNodeName());\r
+        String style = elem.getAttribute("style");\r
+        if (style != null && style.length() > 0)\r
+            parseCss(style);\r
+        }\r
+\r
+    return true;\r
+}\r
+\r
+boolean readSvg(String fileName)\r
+{\r
+    try\r
+        {\r
+        FileInputStream fis = new FileInputStream(fileName);\r
+        if (!readSvg(fis))\r
+            return false;\r
+        fis.close();\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        err("opening svg file:"+e);\r
+        return false;\r
+        }\r
+    return true;\r
+}\r
+\r
+\r
+//########################################################################\r
+//#  O D F    T O    S V G\r
+//########################################################################\r
+\r
+/**\r
+ *\r
+ */\r
+public boolean readOdfEntry(ZipInputStream zis, ZipEntry ent)\r
+{\r
+    String fileName = ent.getName();\r
+    trace("fileName:" + fileName);\r
+    if (fileName.length() < 4)\r
+        return true;\r
+    String ext = fileName.substring(fileName.length() - 4);\r
+    trace("ext:" + ext);\r
+    ArrayList<Byte> arr = new ArrayList<Byte>();\r
+    try\r
+        {\r
+        while (true)\r
+            {\r
+            int inb = zis.read();\r
+            if (inb < 0)\r
+                break;\r
+            arr.add((byte)inb);\r
+            }\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        return false;\r
+        }\r
+    byte buf[] = new byte[arr.size()];\r
+    for (int i=0 ; i<buf.length ; i++)\r
+         buf[i] = arr.get(i);\r
+    trace("bufsize:" + buf.length);\r
+\r
+    if (ext.equals(".xml"))\r
+        {\r
+        try\r
+            {\r
+            DocumentBuilderFactory factory =\r
+                DocumentBuilderFactory.newInstance();\r
+            factory.setNamespaceAware(true);\r
+            DocumentBuilder builder =\r
+                factory.newDocumentBuilder();\r
+            builder.setEntityResolver(new EntityResolver()\r
+                   {\r
+                   public InputSource resolveEntity(String publicId, String systemId)\r
+                       {\r
+                       return new InputSource(new ByteArrayInputStream(new byte[0]));\r
+                       }\r
+                   });\r
+            //trace("doc:"+new String(buf));\r
+            Document doc = builder.parse(new ByteArrayInputStream(buf));\r
+            if (fileName.equals("content.xml"))\r
+                content = doc;\r
+            else if (fileName.equals("meta.xml"))\r
+                meta = doc;\r
+            else if (fileName.equals("styles.xml"))\r
+                {\r
+                //styles = doc;\r
+                }\r
+            }\r
+        catch (javax.xml.parsers.ParserConfigurationException e)\r
+            {\r
+            err("making DOM parser:"+e);\r
+            return false;\r
+            }\r
+        catch (org.xml.sax.SAXException e)\r
+            {\r
+            err("parsing document:"+e);\r
+            return false;\r
+            }\r
+        catch (IOException e)\r
+            {\r
+            err("parsing document:"+e);\r
+            return false;\r
+            }\r
+        }\r
+    else if (ext.equals(".png")  ||\r
+             ext.equals(".gif")  ||\r
+             ext.equals(".jpg")  ||\r
+             ext.equals(".jpeg")   )\r
+        {\r
+        String imageName = "image" + imageNr + ext;\r
+        imageNr++;\r
+        ImageInfo ie = new ImageInfo(fileName, imageName, buf);\r
+        trace("added image '" + imageName + "'.  " + buf.length +" bytes.");\r
+        images.add(ie);\r
+\r
+        }\r
+    dumpDocument(content);\r
+    return true;\r
+}\r
+\r
+\r
+/**\r
+ *\r
+ */\r
+public boolean readOdf(InputStream ins)\r
+{\r
+    imageNr = 0;\r
+    images = new ArrayList<ImageInfo>();\r
+    styleNr = 0;\r
+    styles = new HashMap<String, StyleInfo>();\r
+    ZipInputStream zis = new ZipInputStream(ins);\r
+    while (true)\r
+        {\r
+        try\r
+            {\r
+            ZipEntry ent = zis.getNextEntry();\r
+            if (ent == null)\r
+                break;\r
+            if (!readOdfEntry(zis, ent))\r
+                return false;\r
+            zis.closeEntry();\r
+            }\r
+        catch (IOException e)\r
+            {\r
+            err("reading zip entry");\r
+            return false;\r
+            }\r
+        }\r
+\r
+    return true;\r
+}\r
+\r
+\r
+/**\r
+ *\r
+ */\r
+public boolean readOdf(String fileName)\r
+{\r
+    boolean ret = true;\r
+    try\r
+        {\r
+        FileInputStream fis = new FileInputStream(fileName);\r
+        ret = readOdf(fis);\r
+        fis.close();\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        err("reading " + fileName + " : " + e);\r
+        ret = false;\r
+        }\r
+    return true;\r
+}\r
+\r
+\r
+\r
+\r
+public boolean writeSvgElement(Element elem)\r
+{\r
+    String ns = elem.getNamespaceURI();\r
+    String tagName = elem.getLocalName();\r
+    trace("tag:" + tagName + " : " + ns);\r
+    if (ns.equals(ODSVG_NS))\r
+        {\r
+        po("<"); po(tagName);\r
+        NamedNodeMap attrs = elem.getAttributes();\r
+        for (int i=0 ; i<attrs.getLength() ; i++)\r
+            {\r
+            Attr attr = (Attr)attrs.item(i);\r
+            String aname = attr.getName();\r
+            String aval  = attr.getValue();\r
+            //Replace image name\r
+            if ("xlink:href".equals(aname) && "image".equals(tagName))\r
+                {\r
+                for (int j=0 ; j<images.size() ; j++)\r
+                    {\r
+                    ImageInfo ie = images.get(i);\r
+                    if (aval.equals(ie.getName()))\r
+                        aval = ie.getNewName();\r
+                    }\r
+                }\r
+            po(" ");\r
+            po(aname);\r
+            po("=\"");\r
+            po(aval);\r
+            po("\"");\r
+            }\r
+        po(">\n");\r
+        }\r
+    NodeList children = elem.getChildNodes();\r
+    for (int i=0 ; i<children.getLength() ; i++)\r
+        {\r
+        Node n = children.item(i);\r
+        if (n.getNodeType() == Node.ELEMENT_NODE)\r
+            if (!writeSvgElement((Element)n))\r
+                return false;\r
+        }\r
+    if (ns.equals(ODSVG_NS))\r
+        {\r
+        po("</"); po(tagName); po(">\n");\r
+        }\r
+    return true;\r
+}\r
+\r
+\r
+public boolean saveSvg(String svgFileName)\r
+{\r
+    trace("====== Saving images ===========");\r
+    try\r
+        {\r
+        for (int i=0 ; i<images.size() ; i++)\r
+            {\r
+            ImageInfo entry = images.get(i);\r
+            trace("saving:" + entry.name);\r
+            FileOutputStream fos = new FileOutputStream(entry.name);\r
+            fos.write(entry.buf);\r
+            fos.close();\r
+            }\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        err("saveAsSVG:" + e);\r
+        return false;\r
+        }\r
+\r
+    try\r
+        {\r
+        out = new BufferedWriter(new FileWriter(svgFileName));\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        err("save:" + e);\r
+        return false;\r
+        }\r
+\r
+    if (content == null)\r
+        {\r
+        err("no content in odf");\r
+        return false;\r
+        }\r
+\r
+    NodeList res = content.getElementsByTagNameNS(ODF_NS, "drawing");\r
+    if (res.getLength() < 1)\r
+        {\r
+        err("save: no drawing in document");\r
+        return false;\r
+        }\r
+    Element root = (Element)res.item(0);\r
+    trace("NS:"+root.getNamespaceURI());\r
+\r
+    res = root.getElementsByTagNameNS(ODG_NS, "page");\r
+    if (res.getLength() < 1)\r
+        {\r
+        err("save: no page in drawing");\r
+        return false;\r
+        }\r
+    Element page = (Element)res.item(0);\r
+\r
+    po("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");\r
+    po("<svg\n");\r
+    po("    xmlns:svg=\"http://www.w3.org/2000/svg\"\n");\r
+    po("    xmlns=\"http://www.w3.org/2000/svg\"\n");\r
+    po("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");\r
+    po("    version=\"1.0\"\n");\r
+    po("    >\n");\r
+\r
+    writeSvgElement(page);\r
+\r
+    po("</svg>\n");\r
+\r
+    try\r
+        {\r
+        out.close();\r
+        }\r
+    catch (IOException e)\r
+        {\r
+        err("save:close:" + e);\r
+        return false;\r
+        }\r
+    return true;\r
+}\r
+\r
+\r
+\r
+//########################################################################\r
+//#  C O N S T R U C T O R\r
+//########################################################################\r
+\r
+SvgOdg()\r
+{\r
+    //init, if necessary\r
+    nrfmt = new DecimalFormat("#.####");\r
+}\r
+\r
+\r
+//########################################################################\r
+//#  C O M M A N D    L I N E\r
+//########################################################################\r
+\r
+public boolean odfToSvg(String odfName, String svgName)\r
+{\r
+    if (!readOdf(odfName))\r
+        return false;\r
+    if (!saveSvg(svgName))\r
+        return false;\r
+    return true;\r
+}\r
+\r
+public boolean svgToOdf(String svgName, String odfName)\r
+{\r
+    if (!readSvg(svgName))\r
+        return false;\r
+    System.out.println("ok");\r
+    if (!saveOdf(odfName))\r
+        return false;\r
+    return true;\r
+}\r
+\r
+void usage()\r
+{\r
+    System.out.println("usage: SvgOdf input_file.odg output_file.svg");\r
+    System.out.println("       SvgOdf input_file.svg output_file.odg");\r
+}\r
+\r
+\r
+boolean parseArguments(String argv[])\r
+{\r
+    if (argv.length != 2)\r
+        {\r
+        usage();\r
+        return false;\r
+        }\r
+\r
+    String fileName1 = argv[0];\r
+    String fileName2 = argv[1];\r
+\r
+    if (fileName1.length()<5 || fileName2.length()<5)\r
+        {\r
+        System.out.println("one or more file names is too short");\r
+        usage();\r
+        return false;\r
+        }\r
+\r
+    String ext1 = fileName1.substring(fileName1.length()-4);\r
+    String ext2 = fileName2.substring(fileName2.length()-4);\r
+    //System.out.println("ext1:"+ext1+" ext2:"+ext2);\r
+\r
+    //##### ODG -> SVG #####\r
+    if ((ext1.equals(".odg") || ext1.equals(".odf") || ext1.equals(".zip") ) &&\r
+        ext2.equals(".svg"))\r
+        {\r
+        if (!odfToSvg(argv[0], argv[1]))\r
+            {\r
+            System.out.println("Conversion from ODG to SVG failed");\r
+            return false;\r
+            }\r
+        }\r
+\r
+    //##### SVG -> ODG #####\r
+    else if (ext1.equals(".svg") &&\r
+             ( ext2.equals(".odg") || ext2.equals(".odf") || ext2.equals(".zip") ) )\r
+        {\r
+        if (!svgToOdf(fileName1, fileName2))\r
+            {\r
+            System.out.println("Conversion from SVG to ODG failed");\r
+            return false;\r
+            }\r
+        }\r
+\r
+    //##### none of the above #####\r
+    else\r
+        {\r
+        usage();\r
+        return false;\r
+        }\r
+    return true;\r
+}\r
+\r
+\r
+public static void main(String argv[])\r
+{\r
+    SvgOdg svgodg = new SvgOdg();\r
+    svgodg.parseArguments(argv);\r
+}\r
+\r
+\r
+}\r
+\r
+//########################################################################\r
+//#  E N D    O F    F I L E\r
+//########################################################################\r
+\r