Code

Remove ca.new form ALL_LINGUAS.
[inkscape.git] / src / dom / odf / SvgOdg.cpp
1 /**
2  *
3  * This is a small experimental class for converting between
4  * SVG and OpenDocument .odg files.   This code is not intended
5  * to be a permanent solution for SVG-to-ODG conversion.  Rather,
6  * it is a quick-and-easy test bed for ideas which will be later
7  * recoded into C++.
8  *
9  * ---------------------------------------------------------------------
10  *
11  * SvgOdg - A program to experiment with conversions between SVG and ODG
12  * Copyright (C) 2006 Bob Jamison
13  *
14  * This program is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 3 of the License, or
17  * (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software Foundation,
26  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
27  *
28  * For more information, please write to rwjj@earthlink.net
29  *
30  */
33 /**
34  *
35  */
36 public class SvgOdg
37 {
41 /**
42  * Namespace declarations
43  */
44 public static final String SVG_NS =
45     "http://www.w3.org/2000/svg";
46 public static final String XLINK_NS =
47     "http://www.w3.org/1999/xlink";
48 public static final String ODF_NS =
49     "urn:oasis:names:tc:opendocument:xmlns:office:1.0";
50 public static final String ODG_NS =
51     "urn:oasis:names:tc:opendocument:xmlns:drawing:1.0";
52 public static final String ODSVG_NS =
53     "urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0";
56 DecimalFormat nrfmt;
57 //static final double pxToCm = 0.0339;
58 static final double pxToCm  = 0.0275;
59 static final double piToRad = 0.0174532925;
60 BufferedWriter out;
61 BufferedReader in;
62 int imageNr;
63 int styleNr;
65 //########################################################################
66 //#  M E S S A G E S
67 //########################################################################
69 /**
70  *
71  */
72 void err(String msg)
73 {
74     System.out.println("SvgOdg ERROR:" + msg);
75 }
77 /**
78  *
79  */
80 void trace(String msg)
81 {
82     System.out.println("SvgOdg:" + msg);
83 }
88 //########################################################################
89 //#  I N P U T    /    O U T P U T
90 //########################################################################
92 boolean po(String s)
93 {
94     try
95         {
96         out.write(s);
97         }
98     catch(IOException e)
99         {
100         return false;
101         }
102     return true;
105 //########################################################################
106 //#  U T I L I T Y
107 //########################################################################
109 public void dumpDocument(Document doc)
111     String s = "";
112     try
113         {
114         TransformerFactory factory = TransformerFactory.newInstance();
115         Transformer trans = factory.newTransformer();
116         DOMSource source = new DOMSource(doc);
117         ByteArrayOutputStream bos = new ByteArrayOutputStream();
118         StreamResult result = new StreamResult(bos);
119         trans.transform(source, result);
120         byte buf[] = bos.toByteArray();
121         s = new String(buf);
122         }
123     catch (javax.xml.transform.TransformerException e)
124         {
125         }
126     trace("doc:" + s);
130 //########################################################################
131 //#  I N N E R    C L A S S    ImageInfo
132 //########################################################################
133 public class ImageInfo
135 String name;
136 String newName;
137 byte   buf[];
139 public String getName()
141     return name;
144 public String getNewName()
146     return newName;
150 public byte[] getBuf()
152     return buf;
155 public ImageInfo(String name, String newName, byte buf[])
157     this.name = name;
158     this.name = newName;
159     this.buf  = buf;
163 //########################################################################
164 //#  I N N E R    C L A S S    StyleInfo
165 //########################################################################
166 public class StyleInfo
169 String name;
170 public String getName()
172     return name;
175 String cssStyle;
176 public String getCssStyle()
178     return cssStyle;
181 String stroke;
182 public String getStroke()
184     return stroke;
187 String strokeColor;
188 public String getStrokeColor()
190     return strokeColor;
193 String strokeWidth;
194 public String getStrokeWidth()
196     return strokeWidth;
199 String fill;
200 public String getFill()
202     return fill;
205 String fillColor;
206 public String getFillColor()
208     return fillColor;
211 public StyleInfo(String name, String cssStyle)
213     this.name     = name;
214     this.cssStyle = cssStyle;
215     fill  = "none";
216     stroke = "none";
220 //########################################################################
221 //#  E N D    I N N E R    C L A S S E S
222 //########################################################################
227 //########################################################################
228 //#  V A R I A B L E S
229 //########################################################################
231 /**
232  * ODF content.xml file
233  */
234 Document content;
235 public Document getContent()
237     return content;
240 /**
241  * ODF meta.xml file
242  */
243 Document meta;
244 public Document getMeta()
246     return meta;
249 /**
250  * SVG file
251  */
252 Document       svg;
253 public Document getSvg()
255     return svg;
258 /**
259  * Loaded ODF or SVG images
260  */
261 ArrayList<ImageInfo> images;
262 public ArrayList<ImageInfo> getImages()
264     return images;
267 /**
268  * CSS styles
269  */
270 HashMap<String, StyleInfo> styles;
271 public HashMap<String, StyleInfo> getStyles()
273     return styles;
280 //########################################################################
281 //#  S V G    T O    O D F
282 //########################################################################
284 class PathData
286 String cmd;
287 double nr[];
288 PathData(String s, double buf[])
290     cmd=s; nr = buf;
294 double getPathNum(StringTokenizer st)
296     if (!st.hasMoreTokens())
297         return 0.0;
298     String s = st.nextToken();
299     double nr = Double.parseDouble(s);
300     return nr;
303 String parsePathData(String pathData, double bounds[])
305     double minx = Double.MAX_VALUE;
306     double maxx = Double.MIN_VALUE;
307     double miny = Double.MAX_VALUE;
308     double maxy = Double.MIN_VALUE;
309     //trace("#### pathData:" + pathData);
310     ArrayList<PathData> data = new ArrayList<PathData>();
311     StringTokenizer st = new StringTokenizer(pathData, " ,");
312     while (true)
313         {
314         String s = st.nextToken();
315         if ( s.equals("z") || s.equals("Z") )
316             {
317             PathData pd = new PathData(s, new double[0]);
318             data.add(pd);
319             break;
320             }
321         else if ( s.equals("h") || s.equals("H") )
322             {
323             double d[] = new double[1];
324             d[0] = getPathNum(st) * pxToCm;
325             if (d[0] < minx)  minx = d[0];
326             else if (d[0] > maxx) maxx = d[0];
327             PathData pd = new PathData(s, d);
328             data.add(pd);
329             }
330         else if ( s.equals("v") || s.equals("V") )
331             {
332             double d[] = new double[1];
333             d[0] = getPathNum(st) * pxToCm;
334             if (d[0] < miny) miny = d[0];
335             else if (d[0] > maxy) maxy = d[0];
336             PathData pd = new PathData(s, d);
337             data.add(pd);
338             }
339         else if ( s.equals("m") || s.equals("M") ||
340              s.equals("l") || s.equals("L") ||
341              s.equals("t") || s.equals("T")  )
342             {
343             double d[] = new double[2];
344             d[0] = getPathNum(st) * pxToCm;
345             d[1] = getPathNum(st) * pxToCm;
346             if (d[0] < minx)  minx = d[0];
347             else if (d[0] > maxx) maxx = d[0];
348             if (d[1] < miny) miny = d[1];
349             else if (d[1] > maxy) maxy = d[1];
350             PathData pd = new PathData(s, d);
351             data.add(pd);
352             }
353         else if ( s.equals("q") || s.equals("Q") ||
354              s.equals("s") || s.equals("S")  )
355             {
356             double d[] = new double[4];
357             d[0] = getPathNum(st) * pxToCm;
358             d[1] = getPathNum(st) * pxToCm;
359             if (d[0] < minx)  minx = d[0];
360             else if (d[0] > maxx) maxx = d[0];
361             if (d[1] < miny) miny = d[1];
362             else if (d[1] > maxy) maxy = d[1];
363             d[2] = getPathNum(st) * pxToCm;
364             d[3] = getPathNum(st) * pxToCm;
365             if (d[2] < minx)  minx = d[2];
366             else if (d[2] > maxx) maxx = d[2];
367             if (d[3] < miny) miny = d[3];
368             else if (d[3] > maxy) maxy = d[3];
369             PathData pd = new PathData(s, d);
370             data.add(pd);
371             }
372         else if ( s.equals("c") || s.equals("C") )
373             {
374             double d[] = new double[6];
375             d[0] = getPathNum(st) * pxToCm;
376             d[1] = getPathNum(st) * pxToCm;
377             if (d[0] < minx)  minx = d[0];
378             else if (d[0] > maxx) maxx = d[0];
379             if (d[1] < miny) miny = d[1];
380             else if (d[1] > maxy) maxy = d[1];
381             d[2] = getPathNum(st) * pxToCm;
382             d[3] = getPathNum(st) * pxToCm;
383             if (d[2] < minx)  minx = d[2];
384             else if (d[2] > maxx) maxx = d[2];
385             if (d[3] < miny) miny = d[3];
386             else if (d[3] > maxy) maxy = d[3];
387             d[4] = getPathNum(st) * pxToCm;
388             d[5] = getPathNum(st) * pxToCm;
389             if (d[4] < minx)  minx = d[4];
390             else if (d[4] > maxx) maxx = d[4];
391             if (d[5] < miny) miny = d[5];
392             else if (d[5] > maxy) maxy = d[5];
393             PathData pd = new PathData(s, d);
394             data.add(pd);
395             }
396         else if ( s.equals("a") || s.equals("A") )
397             {
398             double d[] = new double[6];
399             d[0] = getPathNum(st) * pxToCm;
400             d[1] = getPathNum(st) * pxToCm;
401             if (d[0] < minx)  minx = d[0];
402             else if (d[0] > maxx) maxx = d[0];
403             if (d[1] < miny) miny = d[1];
404             else if (d[1] > maxy) maxy = d[1];
405             d[2] = getPathNum(st) * piToRad;//angle
406             d[3] = getPathNum(st) * piToRad;//angle
407             d[4] = getPathNum(st) * pxToCm;
408             d[5] = getPathNum(st) * pxToCm;
409             if (d[4] < minx)  minx = d[4];
410             else if (d[4] > maxx) maxx = d[4];
411             if (d[5] < miny) miny = d[5];
412             else if (d[5] > maxy) maxy = d[5];
413             PathData pd = new PathData(s, d);
414             data.add(pd);
415             }
416         //trace("x:" + x + " y:" + y);
417         }
419     trace("minx:"  + minx + " maxx:" + maxx +
420           " miny:" + miny + " maxy:" + maxy);
422     StringBuffer buf = new StringBuffer();
423     for (PathData pd : data)
424         {
425         buf.append(pd.cmd);
426         buf.append(" ");
427         for (double d:pd.nr)
428             {
429             buf.append(nrfmt.format(d * 1000.0));
430             buf.append(" ");
431             }
432         }
434     bounds[0] = minx;
435     bounds[1] = miny;
436     bounds[2] = maxx;
437     bounds[3] = maxy;
439     return buf.toString();
444 boolean parseTransform(String transStr, AffineTransform trans)
446     trace("== transform:"+ transStr);
447     StringTokenizer st = new StringTokenizer(transStr, ")");
448     while (st.hasMoreTokens())
449         {
450         String chunk = st.nextToken();
451         StringTokenizer st2 = new StringTokenizer(chunk, " ,(");
452         if (!st2.hasMoreTokens())
453             continue;
454         String name = st2.nextToken();
455         trace("   ++name:"+ name);
456         if (name.equals("matrix"))
457             {
458             double v[] = new double[6];
459             for (int i=0 ; i<6 ; i++)
460                 {
461                 if (!st2.hasMoreTokens())
462                     break;
463                 v[i] = Double.parseDouble(st2.nextToken()) * pxToCm;
464                 }
465             AffineTransform mat = new AffineTransform(v);
466             trans.concatenate(mat);
467             }
468         else if (name.equals("translate"))
469             {
470             double dx = 0.0;
471             double dy = 0.0;
472             if (!st2.hasMoreTokens())
473                 continue;
474             dx = Double.parseDouble(st2.nextToken()) * pxToCm;
475             if (st2.hasMoreTokens())
476                 dy = Double.parseDouble(st2.nextToken()) * pxToCm;
477             trans.translate(dx, dy);
478             }
479         else if (name.equals("scale"))
480             {
481             double sx = 1.0;
482             double sy = 1.0;
483             if (!st2.hasMoreTokens())
484                 continue;
485             sx = sy = Double.parseDouble(st2.nextToken());
486             if (st2.hasMoreTokens())
487                 sy = Double.parseDouble(st2.nextToken());
488             trans.scale(sx, sy);
489             }
490         else if (name.equals("rotate"))
491             {
492             double r  = 0.0;
493             double cx = 0.0;
494             double cy = 0.0;
495             if (!st2.hasMoreTokens())
496                 continue;
497             r = Double.parseDouble(st2.nextToken()) * piToRad;
498             if (st2.hasMoreTokens())
499                 {
500                 cx = Double.parseDouble(st2.nextToken()) * pxToCm;
501                 if (!st2.hasMoreTokens())
502                     continue;
503                 cy = Double.parseDouble(st2.nextToken()) * pxToCm;
504                 trans.rotate(r, cx, cy);
505                 }
506             else
507                 {
508                 trans.rotate(r);
509                 }
510             }
511         else if (name.equals("skewX"))
512             {
513             double angle  = 0.0;
514             if (!st2.hasMoreTokens())
515                 continue;
516             angle = Double.parseDouble(st2.nextToken());
517             trans.shear(angle, 0.0);
518             }
519         else if (name.equals("skewY"))
520             {
521             double angle  = 0.0;
522             if (!st2.hasMoreTokens())
523                 continue;
524             angle = Double.parseDouble(st2.nextToken());
525             trans.shear(0.0, angle);
526             }
527         }
528     return true;
533 String coordToOdg(String sval)
535     double nr = Double.parseDouble(sval);
536     nr = nr * pxToCm;
537     String s = nrfmt.format(nr) + "cm";
538     return s;
542 boolean writeSvgAttributes(Element elem, AffineTransform trans)
544     NamedNodeMap attrs = elem.getAttributes();
545     String ename = elem.getLocalName();
546     for (int i=0 ; i<attrs.getLength() ; i++)
547         {
548         Attr attr = (Attr)attrs.item(i);
549         String aname = attr.getName();
550         String aval  = attr.getValue();
551         if (aname.startsWith("xmlns"))
552             continue;
553         else if (aname.equals("d"))//already handled
554             continue;
555         else if (aname.startsWith("transform"))
556             {
557             parseTransform(aval, trans);
558             continue;
559             }
560         else if (aname.equals("style"))
561             {
562             StyleInfo style = styles.get(aval);
563             if (style != null)
564                 {
565                 po(" draw:style-name=\"");
566                 po(style.getName());
567                 po("\"");
568                 }
569             continue;
570             }
571         if (aname.equals("x") || aname.equals("y") ||
572             aname.equals("width") || aname.equals("height"))
573             {
574             aval = coordToOdg(aval);
575             }
576         if ("id".equals(aname))
577             po(" ");
578         else if ("transform".equals(aname))
579             po(" draw:");
580         else
581             po(" svg:");
582         po(aname);
583         po("=\"");
584         po(aval);
585         po("\"");
586         }
588     //Output the current transform
589     if (!trans.isIdentity() &&
590         !(
591           ename.equals("g")     ||
592           ename.equals("defs")  ||
593           ename.equals("metadata")
594          )
595         )
596         {
597         double v[] = new double[6];
598         trans.getMatrix(v);
599         po(" draw:transform=\"matrix(" +
600             nrfmt.format(v[0]) + "," +
601             nrfmt.format(v[1]) + "," +
602             nrfmt.format(v[2]) + "," +
603             nrfmt.format(v[3]) + "," +
604             nrfmt.format(v[4]) + "," +
605             nrfmt.format(v[5]) + ")\"");
606         }
607     return true;
610 public boolean writeOdfContent(Element elem, AffineTransform trans)
612     String ns = elem.getNamespaceURI();
613     String tagName = elem.getLocalName();
614     //trace("ns:" + ns + " tagName:" + tagName);
615     if (!ns.equals(SVG_NS))
616         return true;
617     if (tagName.equals("svg"))
618         {
619         NodeList children = elem.getChildNodes();
620         for (int i=0 ; i<children.getLength() ; i++)
621             {
622             Node n = children.item(i);
623             if (n.getNodeType() == Node.ELEMENT_NODE)
624                 if (!writeOdfContent((Element)n,
625                      (AffineTransform)trans.clone()))
626                     return false;
627             }
628         }
629     else if (tagName.equals("g"))
630         {
631         //String transform = elem.getAttribute("transform");
632         po("<draw:g");
633         writeSvgAttributes(elem, trans); po(">\n");
634         NodeList children = elem.getChildNodes();
635         for (int i=0 ; i<children.getLength() ; i++)
636             {
637             Node n = children.item(i);
638             if (n.getNodeType() == Node.ELEMENT_NODE)
639                 if (!writeOdfContent((Element)n,
640                      (AffineTransform)trans.clone()))
641                     return false;
642             }
643         po("</draw:g>\n");
644         }
645     else if (tagName.equals("text"))
646         {
647         String x         = coordToOdg(elem.getAttribute("x"));
648         String y         = coordToOdg(elem.getAttribute("y"));
649         String width     = "5cm";
650         String height    = "2cm";
651         String txt       = elem.getTextContent();
652         po("<draw:frame draw:style-name=\"grx1\" draw:layer=\"layout\" ");
653         po("svg:x=\"" + x + "\" svg:y=\"" + y + "\" ");
654         po("svg:width=\""  + width + "\" svg:height=\"" + height + "\"");
655         po(">\n");
656         po("    <draw:text-box draw:auto-grow-height=\"true\" draw:auto-grow-width=\"true\">\n");
657         po("    <text:p text:style-name=\"P1\"> " + txt + "</text:p>\n");
658         po("    </draw:text-box>\n");
659         po("</draw:frame>\n");
660         return true;
661         }
662     else if (tagName.equals("image"))
663         {
664         String x         = coordToOdg(elem.getAttribute("x"));
665         String y         = coordToOdg(elem.getAttribute("y"));
666         String width     = coordToOdg(elem.getAttribute("width"));
667         String height    = coordToOdg(elem.getAttribute("height"));
668         String imageName = elem.getAttributeNS(XLINK_NS, "href");
669         po("<draw:frame draw:style-name=\"grx1\" draw:layer=\"layout\" ");
670         po("svg:x=\"" + x + "\" svg:y=\"" + y + "\" ");
671         po("svg:width=\""  + width + "\" svg:height=\"" + height + "\">\n");
672         po("    <draw:image xlink:href=\"Pictures/" + imageName + "\" xlink:type=\"simple\" xlink:show=\"embed\" xlink:actuate=\"onLoad\"><text:p/></draw:image>\n");
673         po("</draw:frame>\n");
674         return true;
675         }
676     else if (tagName.equals("path"))
677         {
678         double bounds[] = new double[4];
679         String d      = elem.getAttribute("d");
680         String newd   = parsePathData(d, bounds);
681         double x      = bounds[0];
682         double y      = bounds[1];
683         double width  = (bounds[2]-bounds[0]);
684         double height = (bounds[3]-bounds[1]);
685         po("<draw:path draw:layer=\"layout\" \n");
686         po("    svg:x=\"" + nrfmt.format(x) + "cm\" ");
687         po("svg:y=\"" + nrfmt.format(y) + "cm\" ");
688         po("svg:width=\""  + nrfmt.format(width) + "cm\" ");
689         po("svg:height=\"" + nrfmt.format(height) + "cm\" ");
690         po("svg:viewBox=\"0.0 0.0 " +
691              nrfmt.format(bounds[2] * 1000.0) + " " +
692              nrfmt.format(bounds[3] * 1000.0) + "\"\n");
693         po("    svg:d=\"" + newd + "\"\n   ");
695         writeSvgAttributes(elem, trans); po("/>\n");
696         //po("    svg:d=\"" + d + "\"/>\n");
697         return true;
698         }
700     else
701         {
702         //Verbatim tab mapping
703         po("<draw:"); po(tagName);
704         writeSvgAttributes(elem, trans); po(">\n");
705         po("</draw:"); po(tagName); po(">\n");
706         }
708     return true;
712 boolean writeOdfContent(ZipOutputStream outs)
714     try
715         {
716         ZipEntry ze = new ZipEntry("content.xml");
717         outs.putNextEntry(ze);
718         out = new BufferedWriter(new OutputStreamWriter(outs));
719         }
720     catch (IOException e)
721         {
722         return false;
723         }
725     NodeList res = svg.getElementsByTagNameNS(SVG_NS, "svg");
726     if (res.getLength() < 1)
727         {
728         err("saveOdf: no <svg> root in .svg file");
729         return false;
730         }
731     Element root = (Element)res.item(0);
734     po("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
735     po("<office:document-content\n");
736     po("    xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
737     po("    xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");
738     po("    xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");
739     po("    xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");
740     po("    xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");
741     po("    xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");
742     po("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
743     po("    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
744     po("    xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
745     po("    xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");
746     po("    xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
747     po("    xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");
748     po("    xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");
749     po("    xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");
750     po("    xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");
751     po("    xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");
752     po("    xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");
753     po("    xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
754     po("    xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");
755     po("    xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");
756     po("    xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");
757     po("    xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");
758     po("    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");
759     po("    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
760     po("    xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
761     po("    xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
762     po("    office:version=\"1.0\">\n");
763     po("\n");
764     po("\n");
765     po("<office:scripts/>\n");
766     po("<office:automatic-styles>\n");
767     po("<style:style style:name=\"dp1\" style:family=\"drawing-page\"/>\n");
768     po("<style:style style:name=\"grx1\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
769     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");
770     po("</style:style>\n");
771     po("<style:style style:name=\"P1\" style:family=\"paragraph\">\n");
772     po("  <style:paragraph-properties fo:text-align=\"center\"/>\n");
773     po("</style:style>\n");
775     //##  Dump our style table
776     for (StyleInfo s : styles.values())
777         {
778         po("<style:style style:name=\"" + s.getName() + "\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
779         po("  <style:graphic-properties");
780         po(" draw:fill=\"" + s.getFill() + "\"");
781         if (!s.getFill().equals("none"))
782             po(" draw:fill-color=\"" + s.getFillColor() + "\"");
783         po(" draw:stroke=\"" + s.getStroke() + "\"");
784         if (!s.getStroke().equals("none"))
785             {
786             po(" svg:stroke-width=\"" + s.getStrokeWidth() + "\"");
787             po(" svg:stroke-color=\"" + s.getStrokeColor() + "\"");
788             }
789         po("/>\n");
790         po("</style:style>\n");
791         }
792     po("</office:automatic-styles>\n");
793     po("\n");
794     po("\n");
795     po("<office:body>\n");
796     po("<office:drawing>\n");
797     po("<draw:page draw:name=\"page1\" draw:style-name=\"dp1\" draw:master-page-name=\"Default\">\n");
798     po("\n\n\n");
799     AffineTransform trans = new AffineTransform();
800     //trans.scale(12.0, 12.0);
801     po("<!-- ######### CONVERSION FROM SVG STARTS ######## -->\n");
802     writeOdfContent(root, trans);
803     po("<!-- ######### CONVERSION FROM SVG ENDS ######## -->\n");
804     po("\n\n\n");
806     po("</draw:page>\n");
807     po("</office:drawing>\n");
808     po("</office:body>\n");
809     po("</office:document-content>\n");
812     try
813         {
814         out.flush();
815         outs.closeEntry();
816         }
817     catch (IOException e)
818         {
819         err("writeOdfContent:" + e);
820         return false;
821         }
822     return true;
825 boolean writeOdfMeta(ZipOutputStream outs)
827     try
828         {
829         ZipEntry ze = new ZipEntry("meta.xml");
830         outs.putNextEntry(ze);
831         out = new BufferedWriter(new OutputStreamWriter(outs));
832         }
833     catch (IOException e)
834         {
835         return false;
836         }
838     po("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
839     po("<office:document-meta\n");
840     po("    xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
841     po("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
842     po("    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
843     po("    xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
844     po("    xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
845     po("    xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
846     po("    xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
847     po("    xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
848     po("    office:version=\"1.0\">\n");
849     po("<office:meta>\n");
850     po("    <meta:generator>Inkscape-0.43</meta:generator>\n");
851     po("    <meta:initial-creator>clark kent</meta:initial-creator>\n");
852     po("    <meta:creation-date>2005-12-10T10:55:13</meta:creation-date>\n");
853     po("    <dc:creator>clark kent</dc:creator>\n");
854     po("    <dc:date>2005-12-10T10:56:20</dc:date>\n");
855     po("    <dc:language>en-US</dc:language>\n");
856     po("    <meta:editing-cycles>2</meta:editing-cycles>\n");
857     po("    <meta:editing-duration>PT1M13S</meta:editing-duration>\n");
858     po("    <meta:user-defined meta:name=\"Info 1\"/>\n");
859     po("    <meta:user-defined meta:name=\"Info 2\"/>\n");
860     po("    <meta:user-defined meta:name=\"Info 3\"/>\n");
861     po("    <meta:user-defined meta:name=\"Info 4\"/>\n");
862     po("    <meta:document-statistic meta:object-count=\"2\"/>\n");
863     po("</office:meta>\n");
864     po("</office:document-meta>\n");
867     try
868         {
869         out.flush();
870         outs.closeEntry();
871         }
872     catch (IOException e)
873         {
874         err("writeOdfContent:" + e);
875         return false;
876         }
877     return true;
881 boolean writeOdfManifest(ZipOutputStream outs)
883     try
884         {
885         ZipEntry ze = new ZipEntry("META-INF/manifest.xml");
886         outs.putNextEntry(ze);
887         out = new BufferedWriter(new OutputStreamWriter(outs));
888         }
889     catch (IOException e)
890         {
891         return false;
892         }
894     po("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
895     po("<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">\n");
896     po("<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n");
897     po("    <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.graphics\" manifest:full-path=\"/\"/>\n");
898     po("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n");
899     po("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>\n");
900     po("    <!--List our images here-->\n");
901     for (int i=0 ; i<images.size() ; i++)
902         {
903         ImageInfo ie = images.get(i);
904         String fname = ie.getName();
905         if (fname.length() < 5)
906             {
907             err("image file name too short:" + fname);
908             return false;
909             }
910         String ext = fname.substring(fname.length() - 4);
911         po("    <manifest:file-entry manifest:media-type=\"");
912         if (ext.equals(".gif"))
913             po("image/gif");
914         else if (ext.equals(".png"))
915             po("image/png");
916         else if (ext.equals(".jpg") || ext.equals(".jpeg"))
917             po("image/jpeg");
918         po("\" manifest:full-path=\"");
919         po(fname);
920         po("\"/>\n");
921         }
922     po("</manifest:manifest>\n");
924     try
925         {
926         out.flush();
927         outs.closeEntry();
928         }
929     catch (IOException e)
930         {
931         err("writeOdfContent:" + e);
932         return false;
933         }
934     return true;
937 boolean writeOdfImages(ZipOutputStream outs)
939     for (int i=0 ; i<images.size() ; i++)
940         {
941         ImageInfo ie = images.get(i);
942         try
943             {
944             String iname = "Pictures/" + ie.getName();
945             ZipEntry ze = new ZipEntry(iname);
946             outs.putNextEntry(ze);
947             outs.write(ie.getBuf());
948             outs.closeEntry();
949             }
950         catch (IOException e)
951             {
952             err("writing images:" + e);
953             return false;
954             }
955         }
956     return true;
959 /**
960  *
961  */
962 public boolean writeOdf(OutputStream outs)
964     try
965         {
966         ZipOutputStream zos = new ZipOutputStream(outs);
967         if (!writeOdfContent(zos))
968             return false;
969         if (!writeOdfManifest(zos))
970             return false;
971         if (!writeOdfMeta(zos))
972             return false;
973         if (!writeOdfImages(zos))
974             return false;
975         //if (!writeOdfStyles(zos))
976         //    return false;
977         zos.close();
978         }
979     catch (IOException e)
980         {
981         err("closing ODF zip output stream:" + e);
982         return false;
983         }
984     return true;
989 /**
990  *
991  */
992 public boolean saveOdf(String fileName)
994     boolean ret = true;
995     try
996         {
997         FileOutputStream fos = new FileOutputStream(fileName);
998         ret = writeOdf(fos);
999         fos.close();
1000         }
1001     catch (IOException e)
1002         {
1003         err("writing odf " + fileName + " : " + e);
1004         return false;
1005         }
1006     return ret;
1011 boolean parseCss(String css)
1013     trace("##### STYLE ### :" + css);
1014     String name = "gr" + styleNr;
1015     styleNr++;
1016     StyleInfo si = new StyleInfo(name, css);
1017     StringTokenizer st = new StringTokenizer(css, ";");
1018     while (st.hasMoreTokens())
1019         {
1020         String style = st.nextToken();
1021         //trace("    " + style);
1022         int pos = style.indexOf(':');
1023         if (pos < 1 || pos > style.length()-2)
1024             continue;
1025         String attrName = style.substring(0, pos);
1026         String attrVal  = style.substring(pos+1);
1027         trace("    =" + attrName + ':' + attrVal);
1028         if ("stroke".equals(attrName))
1029             {
1030             si.stroke = "solid";
1031             si.strokeColor = attrVal;
1032             }
1033         else if ("stroke-width".equals(attrName))
1034             {
1035             si.strokeWidth = attrVal;
1036             }
1037         else if ("fill".equals(attrName))
1038             {
1039             si.fill      = "solid";
1040             si.fillColor = attrVal;
1041             }
1042         }
1043     styles.put(css, si);
1044     return true;
1047 boolean readSvg(InputStream ins)
1049     //### LOAD XML
1050     try
1051         {
1052         DocumentBuilderFactory factory =
1053             DocumentBuilderFactory.newInstance();
1054         factory.setNamespaceAware(true);
1055         DocumentBuilder builder =
1056             factory.newDocumentBuilder();
1057         builder.setEntityResolver(new EntityResolver()
1058                {
1059                public InputSource resolveEntity(String publicId, String systemId)
1060                    {
1061                    return new InputSource(new ByteArrayInputStream(new byte[0]));
1062                    }
1063                });
1064         Document doc = builder.parse(ins);
1065         svg = doc;
1066         }
1067     catch (javax.xml.parsers.ParserConfigurationException e)
1068         {
1069         err("making DOM parser:"+e);
1070         return false;
1071         }
1072     catch (org.xml.sax.SAXException e)
1073         {
1074         err("parsing svg document:"+e);
1075         return false;
1076         }
1077     catch (IOException e)
1078         {
1079         err("parsing svg document:"+e);
1080         return false;
1081         }
1082     //dumpDocument(svg);
1084     //### LOAD IMAGES
1085     imageNr = 0;
1086     images = new ArrayList<ImageInfo>();
1087     NodeList res = svg.getElementsByTagNameNS(SVG_NS, "image");
1088     for (int i=0 ; i<res.getLength() ; i++)
1089         {
1090         Element elem = (Element) res.item(i);
1091         String fileName = elem.getAttributeNS(XLINK_NS, "href");
1092         if (fileName == null)
1093             {
1094             err("No xlink:href pointer to image data for image");
1095             return false;
1096             }
1097         File f = new File(fileName);
1098         if (!f.exists())
1099             {
1100             err("image '" + fileName + "' does not exist");
1101             return false;
1102             }
1103         int bufSize = (int)f.length();
1104         byte buf[] = new byte[bufSize];
1105         int pos = 0;
1106         try
1107             {
1108             FileInputStream fis = new FileInputStream(fileName);
1109             while (pos < bufSize)
1110                 {
1111                 int len = fis.read(buf, pos, bufSize - pos);
1112                 if (len < 0)
1113                     break;
1114                 pos += len;
1115                 }
1116             fis.close();
1117             }
1118         catch (IOException e)
1119             {
1120             err("reading image '" + fileName + "' :" + e);
1121             return false;
1122             }
1123         if (pos != bufSize)
1124             {
1125             err("reading image entry.  expected " +
1126                 bufSize + ", found " + pos + " bytes.");
1127             return false;
1128             }
1129         ImageInfo ie = new ImageInfo(fileName, fileName, buf);
1130         images.add(ie);
1131         }
1133     //### Parse styles
1134     styleNr = 0;
1135     styles = new HashMap<String, StyleInfo>();
1136     res = svg.getElementsByTagName("*");
1137     for (int i=0 ; i<res.getLength() ; i++)
1138         {
1139         Element elem = (Element) res.item(i);
1140         trace("elem:"+ elem.getNodeName());
1141         String style = elem.getAttribute("style");
1142         if (style != null && style.length() > 0)
1143             parseCss(style);
1144         }
1146     return true;
1149 boolean readSvg(String fileName)
1151     try
1152         {
1153         FileInputStream fis = new FileInputStream(fileName);
1154         if (!readSvg(fis))
1155             return false;
1156         fis.close();
1157         }
1158     catch (IOException e)
1159         {
1160         err("opening svg file:"+e);
1161         return false;
1162         }
1163     return true;
1167 //########################################################################
1168 //#  O D F    T O    S V G
1169 //########################################################################
1171 /**
1172  *
1173  */
1174 public boolean readOdfEntry(ZipInputStream zis, ZipEntry ent)
1176     String fileName = ent.getName();
1177     trace("fileName:" + fileName);
1178     if (fileName.length() < 4)
1179         return true;
1180     String ext = fileName.substring(fileName.length() - 4);
1181     trace("ext:" + ext);
1182     ArrayList<Byte> arr = new ArrayList<Byte>();
1183     try
1184         {
1185         while (true)
1186             {
1187             int inb = zis.read();
1188             if (inb < 0)
1189                 break;
1190             arr.add((byte)inb);
1191             }
1192         }
1193     catch (IOException e)
1194         {
1195         return false;
1196         }
1197     byte buf[] = new byte[arr.size()];
1198     for (int i=0 ; i<buf.length ; i++)
1199          buf[i] = arr.get(i);
1200     trace("bufsize:" + buf.length);
1202     if (ext.equals(".xml"))
1203         {
1204         try
1205             {
1206             DocumentBuilderFactory factory =
1207                 DocumentBuilderFactory.newInstance();
1208             factory.setNamespaceAware(true);
1209             DocumentBuilder builder =
1210                 factory.newDocumentBuilder();
1211             builder.setEntityResolver(new EntityResolver()
1212                    {
1213                    public InputSource resolveEntity(String publicId, String systemId)
1214                        {
1215                        return new InputSource(new ByteArrayInputStream(new byte[0]));
1216                        }
1217                    });
1218             //trace("doc:"+new String(buf));
1219             Document doc = builder.parse(new ByteArrayInputStream(buf));
1220             if (fileName.equals("content.xml"))
1221                 content = doc;
1222             else if (fileName.equals("meta.xml"))
1223                 meta = doc;
1224             else if (fileName.equals("styles.xml"))
1225                 {
1226                 //styles = doc;
1227                 }
1228             }
1229         catch (javax.xml.parsers.ParserConfigurationException e)
1230             {
1231             err("making DOM parser:"+e);
1232             return false;
1233             }
1234         catch (org.xml.sax.SAXException e)
1235             {
1236             err("parsing document:"+e);
1237             return false;
1238             }
1239         catch (IOException e)
1240             {
1241             err("parsing document:"+e);
1242             return false;
1243             }
1244         }
1245     else if (ext.equals(".png")  ||
1246              ext.equals(".gif")  ||
1247              ext.equals(".jpg")  ||
1248              ext.equals(".jpeg")   )
1249         {
1250         String imageName = "image" + imageNr + ext;
1251         imageNr++;
1252         ImageInfo ie = new ImageInfo(fileName, imageName, buf);
1253         trace("added image '" + imageName + "'.  " + buf.length +" bytes.");
1254         images.add(ie);
1256         }
1257     dumpDocument(content);
1258     return true;
1262 /**
1263  *
1264  */
1265 public boolean readOdf(InputStream ins)
1267     imageNr = 0;
1268     images = new ArrayList<ImageInfo>();
1269     styleNr = 0;
1270     styles = new HashMap<String, StyleInfo>();
1271     ZipInputStream zis = new ZipInputStream(ins);
1272     while (true)
1273         {
1274         try
1275             {
1276             ZipEntry ent = zis.getNextEntry();
1277             if (ent == null)
1278                 break;
1279             if (!readOdfEntry(zis, ent))
1280                 return false;
1281             zis.closeEntry();
1282             }
1283         catch (IOException e)
1284             {
1285             err("reading zip entry");
1286             return false;
1287             }
1288         }
1290     return true;
1294 /**
1295  *
1296  */
1297 public boolean readOdf(String fileName)
1299     boolean ret = true;
1300     try
1301         {
1302         FileInputStream fis = new FileInputStream(fileName);
1303         ret = readOdf(fis);
1304         fis.close();
1305         }
1306     catch (IOException e)
1307         {
1308         err("reading " + fileName + " : " + e);
1309         ret = false;
1310         }
1311     return true;
1317 public boolean writeSvgElement(Element elem)
1319     String ns = elem.getNamespaceURI();
1320     String tagName = elem.getLocalName();
1321     trace("tag:" + tagName + " : " + ns);
1322     if (ns.equals(ODSVG_NS))
1323         {
1324         po("<"); po(tagName);
1325         NamedNodeMap attrs = elem.getAttributes();
1326         for (int i=0 ; i<attrs.getLength() ; i++)
1327             {
1328             Attr attr = (Attr)attrs.item(i);
1329             String aname = attr.getName();
1330             String aval  = attr.getValue();
1331             //Replace image name
1332             if ("xlink:href".equals(aname) && "image".equals(tagName))
1333                 {
1334                 for (int j=0 ; j<images.size() ; j++)
1335                     {
1336                     ImageInfo ie = images.get(i);
1337                     if (aval.equals(ie.getName()))
1338                         aval = ie.getNewName();
1339                     }
1340                 }
1341             po(" ");
1342             po(aname);
1343             po("=\"");
1344             po(aval);
1345             po("\"");
1346             }
1347         po(">\n");
1348         }
1349     NodeList children = elem.getChildNodes();
1350     for (int i=0 ; i<children.getLength() ; i++)
1351         {
1352         Node n = children.item(i);
1353         if (n.getNodeType() == Node.ELEMENT_NODE)
1354             if (!writeSvgElement((Element)n))
1355                 return false;
1356         }
1357     if (ns.equals(ODSVG_NS))
1358         {
1359         po("</"); po(tagName); po(">\n");
1360         }
1361     return true;
1365 public boolean saveSvg(String svgFileName)
1367     trace("====== Saving images ===========");
1368     try
1369         {
1370         for (int i=0 ; i<images.size() ; i++)
1371             {
1372             ImageInfo entry = images.get(i);
1373             trace("saving:" + entry.name);
1374             FileOutputStream fos = new FileOutputStream(entry.name);
1375             fos.write(entry.buf);
1376             fos.close();
1377             }
1378         }
1379     catch (IOException e)
1380         {
1381         err("saveAsSVG:" + e);
1382         return false;
1383         }
1385     try
1386         {
1387         out = new BufferedWriter(new FileWriter(svgFileName));
1388         }
1389     catch (IOException e)
1390         {
1391         err("save:" + e);
1392         return false;
1393         }
1395     if (content == null)
1396         {
1397         err("no content in odf");
1398         return false;
1399         }
1401     NodeList res = content.getElementsByTagNameNS(ODF_NS, "drawing");
1402     if (res.getLength() < 1)
1403         {
1404         err("save: no drawing in document");
1405         return false;
1406         }
1407     Element root = (Element)res.item(0);
1408     trace("NS:"+root.getNamespaceURI());
1410     res = root.getElementsByTagNameNS(ODG_NS, "page");
1411     if (res.getLength() < 1)
1412         {
1413         err("save: no page in drawing");
1414         return false;
1415         }
1416     Element page = (Element)res.item(0);
1418     po("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
1419     po("<svg\n");
1420     po("    xmlns:svg=\"http://www.w3.org/2000/svg\"\n");
1421     po("    xmlns=\"http://www.w3.org/2000/svg\"\n");
1422     po("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
1423     po("    version=\"1.0\"\n");
1424     po("    >\n");
1426     writeSvgElement(page);
1428     po("</svg>\n");
1430     try
1431         {
1432         out.close();
1433         }
1434     catch (IOException e)
1435         {
1436         err("save:close:" + e);
1437         return false;
1438         }
1439     return true;
1444 //########################################################################
1445 //#  C O N S T R U C T O R
1446 //########################################################################
1448 SvgOdg()
1450     //init, if necessary
1451     nrfmt = new DecimalFormat("#.####");
1455 //########################################################################
1456 //#  C O M M A N D    L I N E
1457 //########################################################################
1459 public boolean odfToSvg(String odfName, String svgName)
1461     if (!readOdf(odfName))
1462         return false;
1463     if (!saveSvg(svgName))
1464         return false;
1465     return true;
1468 public boolean svgToOdf(String svgName, String odfName)
1470     if (!readSvg(svgName))
1471         return false;
1472     System.out.println("ok");
1473     if (!saveOdf(odfName))
1474         return false;
1475     return true;
1478 void usage()
1480     System.out.println("usage: SvgOdf input_file.odg output_file.svg");
1481     System.out.println("       SvgOdf input_file.svg output_file.odg");
1485 boolean parseArguments(String argv[])
1487     if (argv.length != 2)
1488         {
1489         usage();
1490         return false;
1491         }
1493     String fileName1 = argv[0];
1494     String fileName2 = argv[1];
1496     if (fileName1.length()<5 || fileName2.length()<5)
1497         {
1498         System.out.println("one or more file names is too short");
1499         usage();
1500         return false;
1501         }
1503     String ext1 = fileName1.substring(fileName1.length()-4);
1504     String ext2 = fileName2.substring(fileName2.length()-4);
1505     //System.out.println("ext1:"+ext1+" ext2:"+ext2);
1507     //##### ODG -> SVG #####
1508     if ((ext1.equals(".odg") || ext1.equals(".odf") || ext1.equals(".zip") ) &&
1509         ext2.equals(".svg"))
1510         {
1511         if (!odfToSvg(argv[0], argv[1]))
1512             {
1513             System.out.println("Conversion from ODG to SVG failed");
1514             return false;
1515             }
1516         }
1518     //##### SVG -> ODG #####
1519     else if (ext1.equals(".svg") &&
1520              ( ext2.equals(".odg") || ext2.equals(".odf") || ext2.equals(".zip") ) )
1521         {
1522         if (!svgToOdf(fileName1, fileName2))
1523             {
1524             System.out.println("Conversion from SVG to ODG failed");
1525             return false;
1526             }
1527         }
1529     //##### none of the above #####
1530     else
1531         {
1532         usage();
1533         return false;
1534         }
1535     return true;
1539 public static void main(String argv[])
1541     SvgOdg svgodg = new SvgOdg();
1542     svgodg.parseArguments(argv);
1548 //########################################################################
1549 //#  E N D    O F    F I L E
1550 //########################################################################