Code

More WIP. Got all of the files generated. Now just work on content.xml to get viabl...
[inkscape.git] / src / extension / internal / odf.cpp
1 /**
2  * OpenDocument <drawing> input and output
3  *
4  * This is an an entry in the extensions mechanism to begin to enable
5  * the inputting and outputting of OpenDocument Format (ODF) files from
6  * within Inkscape.  Although the initial implementations will be very lossy
7  * do to the differences in the models of SVG and ODF, they will hopefully
8  * improve greatly with time.
9  *
10  * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/idl-definitions.html
11  *
12  * Authors:
13  *   Bob Jamison
14  *
15  * Copyright (C) 2006 Bob Jamison
16  *
17  *  This library is free software; you can redistribute it and/or
18  *  modify it under the terms of the GNU Lesser General Public
19  *  License as published by the Free Software Foundation; either
20  *  version 2.1 of the License, or (at your option) any later version.
21  *
22  *  This library is distributed in the hope that it will be useful,
23  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
24  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  *  Lesser General Public License for more details.
26  *
27  *  You should have received a copy of the GNU Lesser General Public
28  *  License along with this library; if not, write to the Free Software
29  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
30  */
34 #ifdef HAVE_CONFIG_H
35 # include <config.h>
36 #endif
38 #include "odf.h"
40 //# System includes
41 #include <stdio.h>
42 #include <time.h>
43 #include <vector>
46 //# Inkscape includes
47 #include "clear-n_.h"
48 #include "inkscape.h"
49 #include <style.h>
50 #include "display/curve.h"
51 #include "libnr/n-art-bpath.h"
52 #include "extension/system.h"
54 #include "xml/repr.h"
55 #include "xml/attribute-record.h"
56 #include "sp-image.h"
57 #include "sp-path.h"
58 #include "sp-text.h"
59 #include "sp-flowtext.h"
60 #include "svg/svg.h"
61 #include "text-editing.h"
64 //# DOM-specific includes
65 #include "dom/dom.h"
66 #include "dom/util/ziptool.h"
67 #include "dom/io/domstream.h"
68 #include "dom/io/bufferstream.h"
71 //# Shorthand notation
72 typedef org::w3c::dom::DOMString DOMString;
73 typedef org::w3c::dom::io::OutputStreamWriter OutputStreamWriter;
74 typedef org::w3c::dom::io::BufferOutputStream BufferOutputStream;
79 namespace Inkscape
80 {
81 namespace Extension
82 {
83 namespace Internal
84 {
90 //########################################################################
91 //# O U T P U T
92 //########################################################################
94 static std::string getAttribute( Inkscape::XML::Node *node, char *attrName)
95 {
96     std::string val;
97     char *valstr = (char *)node->attribute(attrName);
98     if (valstr)
99         val = (const char *)valstr;
100     return val;
104 static std::string getExtension(const std::string &fname)
106     std::string ext;
108     unsigned int pos = fname.rfind('.');
109     if (pos == fname.npos)
110         {
111         ext = "";
112         }
113     else
114         {
115         ext = fname.substr(pos);
116         }
117     return ext;
120 /**
121  * Method descends into the repr tree, converting image and style info
122  * into forms compatible in ODF.
123  */
124 void
125 OdfOutput::preprocess(ZipFile &zf, Inkscape::XML::Node *node)
128     std::string nodeName = node->name();
130     if (nodeName == "image" || nodeName == "svg:image")
131         {
132         //g_message("image");
133         std::string href = getAttribute(node, "xlink:href");
134         if (href.size() > 0)
135             {
136             std::string oldName = href;
137             std::string ext = getExtension(oldName);
138             if (ext == ".jpeg")
139                 ext = ".jpg";
140             if (imageTable.find(oldName) == imageTable.end())
141                 {
142                 char buf[64];
143                 snprintf(buf, 63, "Pictures/image%d%s",
144                     imageTable.size(), ext.c_str());
145                 std::string newName = buf;
146                 imageTable[oldName] = newName;
147                 std::string comment = "old name was: ";
148                 comment.append(oldName);
149                 ZipEntry *ze = zf.addFile(oldName, comment);
150                 if (ze)
151                     {
152                     ze->setFileName(newName);
153                     }
154                 else
155                     {
156                     g_warning("Could not load image file '%s'", oldName.c_str());
157                     }
158                 }
159             }
160         }
164     //Look for style values in the svg element
165     Inkscape::Util::List<Inkscape::XML::AttributeRecord const> attr =
166         node->attributeList();
167     for ( ; attr ; ++attr)
168         {
169         if (!attr->key || !attr->value)
170             {
171             g_warning("null key or value in attribute");
172             continue;
173             }
174         //g_message("key:%s value:%s", g_quark_to_string(attr->key),
175         //                             g_quark_to_string(attr->value)  );
177         std::string attrName  = (const char *)g_quark_to_string(attr->key);
178         std::string attrValue = (const char *)attr->value;
179         g_message("tag:'%s'    key:'%s'    value:'%s'",
180             nodeName.c_str(), attrName.c_str(), attrValue.c_str()  );
181         if (attrName == "style")
182             {
183             StyleInfo si(attrName, attrValue);
184             if (styleTable.find(attrValue) != styleTable.end())
185                 {
186                 g_message("duplicate style");
187                 }
188             else
189                 {
190                 char buf[16];
191                 snprintf(buf, 15, "style%d", styleTable.size());
192                 std::string attrName  = buf;
193                 //Map from value-->name .   Looks backwards, i know
194                 styleTable[attrValue] = si;
195                 g_message("mapping '%s' to '%s'",
196                     attrValue.c_str(), attrName.c_str());
197                 }
198             }
199         }
203     for (Inkscape::XML::Node *child = node->firstChild() ;
204             child ; child = child->next())
205         preprocess(zf, child);
210 bool OdfOutput::writeManifest(ZipFile &zf)
212     BufferOutputStream bouts;
213     OutputStreamWriter outs(bouts);
215     time_t tim;
216     time(&tim);
218     outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
219     outs.printf("<!DOCTYPE manifest:manifest PUBLIC \"-//OpenOffice.org//DTD Manifest 1.0//EN\" \"Manifest.dtd\">\n");
220     outs.printf("\n");
221     outs.printf("\n");
222     outs.printf("<!--\n");
223     outs.printf("*************************************************************************\n");
224     outs.printf("  file:  manifest.xml\n");
225     outs.printf("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
226     outs.printf("  http://www.inkscape.org\n");
227     outs.printf("*************************************************************************\n");
228     outs.printf("-->\n");
229     outs.printf("\n");
230     outs.printf("\n");
231     outs.printf("<manifest:manifest xmlns:manifest=\"urn:oasis:names:tc:opendocument:xmlns:manifest:1.0\">\n");
232     outs.printf("    <manifest:file-entry manifest:media-type=\"application/vnd.oasis.opendocument.graphics\" manifest:full-path=\"/\"/>\n");
233     outs.printf("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"content.xml\"/>\n");
234     //outs.printf("    <manifest:file-entry manifest:media-type=\"text/xml\" manifest:full-path=\"meta.xml\"/>\n");
235     outs.printf("    <!--List our images here-->\n");
236     std::map<std::string, std::string>::iterator iter;
237     for (iter = imageTable.begin() ; iter!=imageTable.end() ; iter++)
238         {
239         std::string oldName = iter->first;
240         std::string newName = iter->second;
242         std::string ext = getExtension(oldName);
243         if (ext == ".jpeg")
244             ext = ".jpg";
245         outs.printf("    <manifest:file-entry manifest:media-type=\"");
246         if (ext == ".gif")
247             outs.printf("image/gif");
248         else if (ext == ".png")
249             outs.printf("image/png");
250         else if (ext == ".jpg")
251             outs.printf("image/jpeg");
252         outs.printf("\" manifest:full-path=\"");
253         outs.printf((char *)newName.c_str());
254         outs.printf("\"/>\n");
255         }
256     outs.printf("</manifest:manifest>\n");
258     outs.close();
260     //Make our entry
261     ZipEntry *ze = zf.newEntry("META-INF/manifest.xml", "ODF file manifest");
262     ze->setUncompressedData(bouts.getBuffer());
263     ze->finish();
265     return true;
272 bool OdfOutput::writeStyle(Writer &outs)
274     outs.printf("<office:automatic-styles>\n");
275     outs.printf("<style:style style:name=\"dp1\" style:family=\"drawing-page\"/>\n");
276     outs.printf("<style:style style:name=\"grx1\" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
277     outs.printf("  <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");
278     outs.printf("</style:style>\n");
279     outs.printf("<style:style style:name=\"P1\" style:family=\"paragraph\">\n");
280     outs.printf("  <style:paragraph-properties fo:text-align=\"center\"/>\n");
281     outs.printf("</style:style>\n");
283     //##  Dump our style table
284     /*
285     std::map<std::string, std::string>::iterator iter;
286     for (iter = styleTable.begin() ; iter != styleTable.end() ; iter++)
287         {
288         outs.printf("<style:style style:name=\"%s\"", iter->second);
289         outs.printf(" style:family=\"graphic\" style:parent-style-name=\"standard\">\n");
290         outs.printf("  <style:graphic-properties");
291         outs.printf(" draw:fill=\"" + s.getFill() + "\"");
292         if (!s.getFill().equals("none"))
293             outs.printf(" draw:fill-color=\"" + s.getFillColor() + "\"");
294         outs.printf(" draw:stroke=\"" + s.getStroke() + "\"");
295         if (!s.getStroke().equals("none"))
296             {
297             outs.printf(" svg:stroke-width=\"" + s.getStrokeWidth() + "\"");
298             outs.printf(" svg:stroke-color=\"" + s.getStrokeColor() + "\"");
299             }
300         outs.printf("/>\n");
301         outs.printf("</style:style>\n");
302         }
303     */
304     outs.printf("</office:automatic-styles>\n");
305     outs.printf("\n");
307     return true;
313 bool OdfOutput::writeTree(Writer &outs, Inkscape::XML::Node *node)
315     //# Get the SPItem, if applicable
316     SPObject *reprobj = SP_ACTIVE_DOCUMENT->getObjectByRepr(node);
317     if (!reprobj)
318         return true;
319     if (!SP_IS_ITEM(reprobj))
320         {
321         return true;
322         }
323     SPItem *item = SP_ITEM(reprobj);
325     //# Do our stuff
326     SPCurve *curve = NULL;
327     std::string tagName = "path";
328     if (SP_IS_IMAGE(item))
329         {
330         tagName = "image";
331         std::string href = getAttribute(node, "xlink:href");
332         std::map<std::string, std::string>::iterator iter = imageTable.find(href);
333         if (iter == imageTable.end())
334             {
335             g_warning("image '%s' not in table", href.c_str());
336             return false;
337             }
338         std::string newName = iter->second;
339         outs.printf("<image xlink:href=\"%s\"/>\n", newName.c_str());
340         return true;
341         }
342     else if (SP_IS_SHAPE(item))
343         {
344         curve = sp_shape_get_curve(SP_SHAPE(item));
345         }
346     else if (SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))
347         {
348         curve = te_get_layout(item)->convertToCurves();
349         }
351     if (curve)
352         {
354         //Inkscape::XML::Node *repr = sp_repr_new("svg:path");
355         /* Transformation */
356         //repr->setAttribute("transform", SP_OBJECT_REPR(item)->attribute("transform"));
358         /* Rotation center */
359         //sp_repr_set_attr(repr, "inkscape:transform-center-x", SP_OBJECT_REPR(item)->attribute("inkscape:transform-center-x"));
360         //sp_repr_set_attr(repr, "inkscape:transform-center-y", SP_OBJECT_REPR(item)->attribute("inkscape:transform-center-y"));
362         /* Definition */
363         gchar *def_str = sp_svg_write_path(curve->bpath);
365         g_free(def_str);
366         sp_curve_unref(curve);
367         }
369     //# Iterate through the children
370     for (Inkscape::XML::Node *child = node->firstChild() ; child ; child = child->next())
371         {
372         if (!writeTree(outs, child))
373             return false;
374         }
375     return true;
381 bool OdfOutput::writeContent(ZipFile &zf, Inkscape::XML::Node *node)
383     BufferOutputStream bouts;
384     OutputStreamWriter outs(bouts);
386     time_t tim;
387     time(&tim);
389     outs.printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
390     outs.printf("\n");
391     outs.printf("\n");
392     outs.printf("<!--\n");
393     outs.printf("*************************************************************************\n");
394     outs.printf("  file:  content.xml\n");
395     outs.printf("  Generated by Inkscape: %s", ctime(&tim)); //ctime has its own <cr>
396     outs.printf("  http://www.inkscape.org\n");
397     outs.printf("*************************************************************************\n");
398     outs.printf("-->\n");
399     outs.printf("\n");
400     outs.printf("\n");
401     outs.printf("<office:document-content\n");
402     outs.printf("    xmlns:office=\"urn:oasis:names:tc:opendocument:xmlns:office:1.0\"\n");
403     outs.printf("    xmlns:style=\"urn:oasis:names:tc:opendocument:xmlns:style:1.0\"\n");
404     outs.printf("    xmlns:text=\"urn:oasis:names:tc:opendocument:xmlns:text:1.0\"\n");
405     outs.printf("    xmlns:table=\"urn:oasis:names:tc:opendocument:xmlns:table:1.0\"\n");
406     outs.printf("    xmlns:draw=\"urn:oasis:names:tc:opendocument:xmlns:drawing:1.0\"\n");
407     outs.printf("    xmlns:fo=\"urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0\"\n");
408     outs.printf("    xmlns:xlink=\"http://www.w3.org/1999/xlink\"\n");
409     outs.printf("    xmlns:dc=\"http://purl.org/dc/elements/1.1/\"\n");
410     outs.printf("    xmlns:meta=\"urn:oasis:names:tc:opendocument:xmlns:meta:1.0\"\n");
411     outs.printf("    xmlns:number=\"urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0\"\n");
412     outs.printf("    xmlns:presentation=\"urn:oasis:names:tc:opendocument:xmlns:presentation:1.0\"\n");
413     outs.printf("    xmlns:svg=\"urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0\"\n");
414     outs.printf("    xmlns:chart=\"urn:oasis:names:tc:opendocument:xmlns:chart:1.0\"\n");
415     outs.printf("    xmlns:dr3d=\"urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0\"\n");
416     outs.printf("    xmlns:math=\"http://www.w3.org/1998/Math/MathML\"\n");
417     outs.printf("    xmlns:form=\"urn:oasis:names:tc:opendocument:xmlns:form:1.0\"\n");
418     outs.printf("    xmlns:script=\"urn:oasis:names:tc:opendocument:xmlns:script:1.0\"\n");
419     outs.printf("    xmlns:ooo=\"http://openoffice.org/2004/office\"\n");
420     outs.printf("    xmlns:ooow=\"http://openoffice.org/2004/writer\"\n");
421     outs.printf("    xmlns:oooc=\"http://openoffice.org/2004/calc\"\n");
422     outs.printf("    xmlns:dom=\"http://www.w3.org/2001/xml-events\"\n");
423     outs.printf("    xmlns:xforms=\"http://www.w3.org/2002/xforms\"\n");
424     outs.printf("    xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\"\n");
425     outs.printf("    xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n");
426     outs.printf("    xmlns:smil=\"urn:oasis:names:tc:opendocument:xmlns:smil-compatible:1.0\"\n");
427     outs.printf("    xmlns:anim=\"urn:oasis:names:tc:opendocument:xmlns:animation:1.0\"\n");
428     outs.printf("    office:version=\"1.0\">\n");
429     outs.printf("\n");
430     outs.printf("\n");
431     outs.printf("<office:scripts/>\n");
432     outs.printf("\n");
433     outs.printf("\n");
434     //AffineTransform trans = new AffineTransform();
435     //trans.scale(12.0, 12.0);
436     outs.printf("<!-- ######### CONVERSION FROM SVG STARTS ######## -->\n");
437     outs.printf("<!--\n");
438     outs.printf("*************************************************************************\n");
439     outs.printf("  S T Y L E S\n");
440     outs.printf("  Style entries have been pulled from the svg style and\n");
441     outs.printf("  representation attributes in the SVG tree.  The tree elements\n");
442     outs.printf("  then refer to them by name, in the ODF manner\n");
443     outs.printf("*************************************************************************\n");
444     outs.printf("-->\n");
445     outs.printf("\n");
446     outs.printf("\n");
448     if (!writeStyle(outs))
449         {
450         g_warning("Failed to write styles");
451         return false;
452         }
454     outs.printf("\n");
455     outs.printf("\n");
456     outs.printf("\n");
457     outs.printf("\n");
458     outs.printf("<!--\n");
459     outs.printf("*************************************************************************\n");
460     outs.printf("  D R A W I N G\n");
461     outs.printf("  This section is the heart of SVG-ODF conversion.  We are\n");
462     outs.printf("  starting with simple conversions, and will slowly evolve\n");
463     outs.printf("  into a 'smarter' translation as time progresses.  Any help\n");
464     outs.printf("  in improving .odg export is welcome.\n");
465     outs.printf("*************************************************************************\n");
466     outs.printf("-->\n");
467     outs.printf("\n");
468     outs.printf("\n");
469     outs.printf("<office:body>\n");
470     outs.printf("<office:drawing>\n");
471     outs.printf("<draw:page draw:name=\"page1\" draw:style-name=\"dp1\" draw:master-page-name=\"Default\">\n");
472     outs.printf("\n");
473     outs.printf("\n");
475     if (!writeTree(outs, node))
476         {
477         g_warning("Failed to convert SVG tree");
478         return false;
479         }
481     outs.printf("\n");
482     outs.printf("\n");
484     outs.printf("</draw:page>\n");
485     outs.printf("</office:drawing>\n");
487     outs.printf("\n");
488     outs.printf("\n");
489     outs.printf("<!-- ######### CONVERSION FROM SVG ENDS ######## -->\n");
490     outs.printf("\n");
491     outs.printf("\n");
493     outs.printf("</office:body>\n");
494     outs.printf("</office:document-content>\n");
495     outs.printf("\n");
496     outs.printf("\n");
497     outs.printf("\n");
498     outs.printf("<!--\n");
499     outs.printf("*************************************************************************\n");
500     outs.printf("  E N D    O F    F I L E\n");
501     outs.printf("  Have a nice day -- ishmal\n");
502     outs.printf("*************************************************************************\n");
503     outs.printf("-->\n");
504     outs.printf("\n");
505     outs.printf("\n");
509     //Make our entry
510     ZipEntry *ze = zf.newEntry("content.xml", "ODF master content file");
511     ze->setUncompressedData(bouts.getBuffer());
512     ze->finish();
514     return true;
520 /**
521  * Descends into the SVG tree, mapping things to ODF when appropriate
522  */
523 void
524 OdfOutput::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *uri)
526     ZipFile zf;
527     styleTable.clear();
528     imageTable.clear();
529     preprocess(zf, doc->rroot);
530     if (!writeManifest(zf))
531         {
532         g_warning("Failed to write manifest");
533         return;
534         }
535     if (!writeContent(zf, doc->rroot))
536         {
537         g_warning("Failed to write content");
538         return;
539         }
540     if (!zf.writeFile(uri))
541         {
542         return;
543         }
547 /**
548  * This is the definition of PovRay output.  This function just
549  * calls the extension system with the memory allocated XML that
550  * describes the data.
551 */
552 void
553 OdfOutput::init()
555     Inkscape::Extension::build_from_mem(
556         "<inkscape-extension>\n"
557             "<name>" N_("OpenDocument Drawing Output") "</name>\n"
558             "<id>org.inkscape.output.odf</id>\n"
559             "<output>\n"
560                 "<extension>.odg</extension>\n"
561                 "<mimetype>text/x-povray-script</mimetype>\n"
562                 "<filetypename>" N_("OpenDocument drawing (*.odg)") "</filetypename>\n"
563                 "<filetypetooltip>" N_("OpenDocument drawing file") "</filetypetooltip>\n"
564             "</output>\n"
565         "</inkscape-extension>",
566         new OdfOutput());
569 /**
570  * Make sure that we are in the database
571  */
572 bool
573 OdfOutput::check (Inkscape::Extension::Extension *module)
575     /* We don't need a Key
576     if (NULL == Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_POV))
577         return FALSE;
578     */
580     return TRUE;
585 //########################################################################
586 //# I N P U T
587 //########################################################################
591 //#######################
592 //# L A T E R  !!!  :-)
593 //#######################
607 }  //namespace Internal
608 }  //namespace Extension
609 }  //namespace Inkscape
612 //########################################################################
613 //# E N D    O F    F I L E
614 //########################################################################
616 /*
617   Local Variables:
618   mode:c++
619   c-file-style:"stroustrup"
620   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
621   indent-tabs-mode:nil
622   fill-column:99
623   End:
624 */
625 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :