1 /*
2 * This is the code that moves all of the SVG loading and saving into
3 * the module format. Really Inkscape is built to handle these formats
4 * internally, so this is just calling those internal functions.
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * Ted Gould <ted@gould.cx>
9 *
10 * Copyright (C) 2002-2003 Authors
11 *
12 * Released under GNU GPL, read the file 'COPYING' for more information
13 */
15 #ifdef HAVE_CONFIG_H
16 # include <config.h>
17 #endif
18 #include "sp-object.h"
19 #include "svg.h"
20 #include "file.h"
21 #include "extension/system.h"
22 #include "extension/output.h"
23 #include <vector>
24 #include "xml/attribute-record.h"
26 #ifdef WITH_GNOME_VFS
27 # include <libgnomevfs/gnome-vfs.h>
28 #endif
30 namespace Inkscape {
31 namespace Extension {
32 namespace Internal {
34 #include "clear-n_.h"
37 using Inkscape::Util::List;
38 using Inkscape::XML::AttributeRecord;
39 using Inkscape::XML::Node;
43 void pruneExtendedAttributes( Inkscape::XML::Node *repr )
44 {
45 if (repr) {
46 if ( repr->type() == Inkscape::XML::ELEMENT_NODE ) {
47 std::vector<gchar const*> toBeRemoved;
48 for ( List<AttributeRecord const> it = repr->attributeList(); it; ++it ) {
49 const gchar* attrName = g_quark_to_string(it->key);
50 if ((strncmp("inkscape:", attrName, 9) == 0) || (strncmp("sodipodi:", attrName, 9) == 0)) {
51 toBeRemoved.push_back(attrName);
52 }
53 }
54 // Can't change the set we're interating over while we are iterating.
55 for ( std::vector<gchar const*>::iterator it = toBeRemoved.begin(); it != toBeRemoved.end(); ++it ) {
56 repr->setAttribute(*it, 0);
57 }
58 }
60 for ( Node *child = repr->firstChild(); child; child = child->next() ) {
61 pruneExtendedAttributes(child);
62 }
63 }
64 }
67 /**
68 \return None
69 \brief What would an SVG editor be without loading/saving SVG
70 files. This function sets that up.
72 For each module there is a call to Inkscape::Extension::build_from_mem
73 with a rather large XML file passed in. This is a constant string
74 that describes the module. At the end of this call a module is
75 returned that is basically filled out. The one thing that it doesn't
76 have is the key function for the operation. And that is linked at
77 the end of each call.
78 */
79 void
80 Svg::init(void)
81 {
82 Inkscape::Extension::Extension * ext;
84 /* SVG in */
85 ext = Inkscape::Extension::build_from_mem(
86 "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
87 "<name>" N_("SVG Input") "</name>\n"
88 "<id>" SP_MODULE_KEY_INPUT_SVG "</id>\n"
89 "<input>\n"
90 "<extension>.svg</extension>\n"
91 "<mimetype>image/svg+xml</mimetype>\n"
92 "<filetypename>" N_("Scalable Vector Graphic (*.svg)") "</filetypename>\n"
93 "<filetypetooltip>" N_("Inkscape native file format and W3C standard") "</filetypetooltip>\n"
94 "<output_extension>" SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE "</output_extension>\n"
95 "</input>\n"
96 "</inkscape-extension>", new Svg());
98 /* SVG out Inkscape */
99 ext = Inkscape::Extension::build_from_mem(
100 "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
101 "<name>" N_("SVG Output Inkscape") "</name>\n"
102 "<id>" SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE "</id>\n"
103 "<output>\n"
104 "<extension>.svg</extension>\n"
105 "<mimetype>image/x-inkscape-svg</mimetype>\n"
106 "<filetypename>" N_("Inkscape SVG (*.svg)") "</filetypename>\n"
107 "<filetypetooltip>" N_("SVG format with Inkscape extensions") "</filetypetooltip>\n"
108 "<dataloss>false</dataloss>\n"
109 "</output>\n"
110 "</inkscape-extension>", new Svg());
112 /* SVG out */
113 ext = Inkscape::Extension::build_from_mem(
114 "<inkscape-extension xmlns=\"" INKSCAPE_EXTENSION_URI "\">\n"
115 "<name>" N_("SVG Output") "</name>\n"
116 "<id>" SP_MODULE_KEY_OUTPUT_SVG "</id>\n"
117 "<output>\n"
118 "<extension>.svg</extension>\n"
119 "<mimetype>image/svg+xml</mimetype>\n"
120 "<filetypename>" N_("Plain SVG (*.svg)") "</filetypename>\n"
121 "<filetypetooltip>" N_("Scalable Vector Graphics format as defined by the W3C") "</filetypetooltip>\n"
122 "</output>\n"
123 "</inkscape-extension>", new Svg());
125 #ifdef WITH_GNOME_VFS
126 gnome_vfs_init();
127 #endif
130 return;
131 }
134 #ifdef WITH_GNOME_VFS
135 #define BUF_SIZE 8192
137 static gchar *
138 _load_uri (const gchar *uri)
139 {
140 GnomeVFSHandle *handle = NULL;
141 GnomeVFSFileSize bytes_read;
143 gsize bytesRead = 0;
144 gsize bytesWritten = 0;
145 GError* error = NULL;
146 gchar* uri_local = g_filename_from_utf8( uri, -1, &bytesRead, &bytesWritten, &error);
148 if ( uri_local == NULL ) {
149 g_warning( "Error converting filename to locale encoding.");
150 }
152 GnomeVFSResult result = gnome_vfs_open (&handle, uri_local, GNOME_VFS_OPEN_READ);
154 if (result != GNOME_VFS_OK) {
155 g_warning("%s", gnome_vfs_result_to_string(result));
156 }
158 std::vector<gchar> doc;
159 while (result == GNOME_VFS_OK) {
160 gchar buffer[BUF_SIZE];
161 result = gnome_vfs_read (handle, buffer, BUF_SIZE, &bytes_read);
162 doc.insert(doc.end(), buffer, buffer+bytes_read);
163 }
165 return g_strndup(&doc[0], doc.size());
166 }
167 #endif
170 /**
171 \return A new document just for you!
172 \brief This function takes in a filename of a SVG document and
173 turns it into a SPDocument.
174 \param mod Module to use
175 \param uri The path to the file (UTF-8)
177 This function is really simple, it just calls sp_document_new...
178 */
179 SPDocument *
180 Svg::open (Inkscape::Extension::Input */*mod*/, const gchar *uri)
181 {
182 #ifdef WITH_GNOME_VFS
183 if (!gnome_vfs_initialized() || gnome_vfs_uri_is_local(gnome_vfs_uri_new(uri))) {
184 // Use built-in loader instead of VFS for this
185 return sp_document_new(uri, TRUE);
186 }
187 gchar * buffer = _load_uri(uri);
188 if (buffer == NULL) {
189 g_warning("Error: Could not open file '%s' with VFS\n", uri);
190 return NULL;
191 }
192 SPDocument * doc = sp_document_new_from_mem(buffer, strlen(buffer), 1);
194 g_free(buffer);
195 return doc;
196 #else
197 return sp_document_new (uri, TRUE);
198 #endif
199 }
201 /**
202 \return None
203 \brief This is the function that does all of the SVG saves in
204 Inkscape. It detects whether it should do a Inkscape
205 namespace save internally.
206 \param mod Extension to use.
207 \param doc Document to save.
208 \param uri The filename to save the file to.
210 This function first checks its parameters, and makes sure that
211 we're getting good data. It also checks the module ID of the
212 incoming module to figure out whether this save should include
213 the Inkscape namespace stuff or not. The result of that comparison
214 is stored in the exportExtensions variable.
216 If there is not to be Inkscape name spaces a new document is created
217 without. (I think, I'm not sure on this code)
219 All of the internally referenced imageins are also set to relative
220 paths in the file. And the file is saved.
222 This really needs to be fleshed out more, but I don't quite understand
223 all of this code. I just stole it.
224 */
225 void
226 Svg::save(Inkscape::Extension::Output *mod, SPDocument *doc, gchar const *filename)
227 {
228 g_return_if_fail(doc != NULL);
229 g_return_if_fail(filename != NULL);
231 gchar *save_path = g_path_get_dirname(filename);
233 bool const exportExtensions = ( !mod->get_id()
234 || !strcmp (mod->get_id(), SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE)
235 || !strcmp (mod->get_id(), SP_MODULE_KEY_OUTPUT_SVGZ_INKSCAPE));
237 Inkscape::XML::Document *rdoc = NULL;
238 Inkscape::XML::Node *repr = NULL;
239 if (exportExtensions) {
240 repr = sp_document_repr_root (doc);
241 } else {
242 rdoc = sp_repr_document_new ("svg:svg");
243 repr = rdoc->root();
244 repr = sp_document_root (doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
246 pruneExtendedAttributes(repr);
247 }
249 if (!sp_repr_save_rebased_file(repr->document(), filename, SP_SVG_NS_URI,
250 doc->base, filename)) {
251 throw Inkscape::Extension::Output::save_failed();
252 }
254 if (!exportExtensions) {
255 Inkscape::GC::release(rdoc);
256 }
258 g_free(save_path);
260 return;
261 }
263 } } } /* namespace inkscape, module, implementation */
265 /*
266 Local Variables:
267 mode:c++
268 c-file-style:"stroustrup"
269 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
270 indent-tabs-mode:nil
271 fill-column:99
272 End:
273 */
274 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :