d7e0eebf3794515a9a07e94b8143219f8ecfb401
1 /*
2 * This is file is kind of the junk file. Basically everything that
3 * didn't fit in one of the other well defined areas, well, it's now
4 * here. Which is good in someways, but this file really needs some
5 * definition. Hopefully that will come ASAP.
6 *
7 * Authors:
8 * Ted Gould <ted@gould.cx>
9 * Johan Engelen <johan@shouraizou.nl>
10 *
11 * Copyright (C) 2006-2007 Johan Engelen
12 * Copyright (C) 2002-2004 Ted Gould
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
21 #include <interface.h>
23 #include "extension.h"
24 #include "db.h"
25 #include "input.h"
26 #include "output.h"
27 #include "effect.h"
28 #include "patheffect.h"
29 #include "print.h"
30 #include "implementation/script.h"
31 #include "implementation/xslt.h"
32 #include "xml/rebase-hrefs.h"
33 /* #include "implementation/plugin.h" */
35 namespace Inkscape {
36 namespace Extension {
38 static void open_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
39 static void save_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
40 static Extension *build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp);
42 /**
43 * \return A new document created from the filename passed in
44 * \brief This is a generic function to use the open function of
45 * a module (including Autodetect)
46 * \param key Identifier of which module to use
47 * \param filename The file that should be opened
48 *
49 * First things first, are we looking at an autodetection? Well if that's the case then the module
50 * needs to be found, and that is done with a database lookup through the module DB. The foreach
51 * function is called, with the parameter being a gpointer array. It contains both the filename
52 * (to find its extension) and where to write the module when it is found.
53 *
54 * If there is no autodetection, then the module database is queried with the key given.
55 *
56 * If everything is cool at this point, the module is loaded, and there is possibility for
57 * preferences. If there is a function, then it is executed to get the dialog to be displayed.
58 * After it is finished the function continues.
59 *
60 * Lastly, the open function is called in the module itself.
61 */
62 SPDocument *
63 open(Extension *key, gchar const *filename)
64 {
65 Input *imod = NULL;
66 if (key == NULL) {
67 gpointer parray[2];
68 parray[0] = (gpointer)filename;
69 parray[1] = (gpointer)&imod;
70 db.foreach(open_internal, (gpointer)&parray);
71 } else {
72 imod = dynamic_cast<Input *>(key);
73 }
75 bool last_chance_svg = false;
76 if (key == NULL && imod == NULL) {
77 last_chance_svg = true;
78 imod = dynamic_cast<Input *>(db.get(SP_MODULE_KEY_INPUT_SVG));
79 }
81 if (imod == NULL) {
82 throw Input::no_extension_found();
83 }
85 imod->set_state(Extension::STATE_LOADED);
87 if (!imod->loaded()) {
88 throw Input::open_failed();
89 }
91 if (!imod->prefs(filename))
92 return NULL;
94 SPDocument *doc = imod->open(filename);
95 if (!doc) {
96 throw Input::open_failed();
97 }
99 if (last_chance_svg) {
100 /* We can't call sp_ui_error_dialog because we may be
101 running from the console, in which case calling sp_ui
102 routines will cause a segfault. See bug 1000350 - bryce */
103 // sp_ui_error_dialog(_("Format autodetect failed. The file is being opened as SVG."));
104 g_warning(_("Format autodetect failed. The file is being opened as SVG."));
105 }
107 /* This kinda overkill as most of these are already set, but I want
108 to make sure for this release -- TJG */
109 doc->setModifiedSinceSave(false);
111 sp_document_set_uri(doc, filename);
113 return doc;
114 }
116 /**
117 * \return none
118 * \brief This is the function that searches each module to see
119 * if it matches the filename for autodetection.
120 * \param in_plug The module to be tested
121 * \param in_data An array of pointers containing the filename, and
122 * the place to put a successfully found module.
123 *
124 * Basically this function only looks at input modules as it is part of the open function. If the
125 * module is an input module, it then starts to take it apart, and the data that is passed in.
126 * Because the data being passed in is in such a weird format, there are a few casts to make it
127 * easier to use. While it looks like a lot of local variables, they'll all get removed by the
128 * compiler.
129 *
130 * First thing that is checked is if the filename is shorter than the extension itself. There is
131 * no way for a match in that case. If it's long enough then there is a string compare of the end
132 * of the filename (for the length of the extension), and the extension itself. If this passes
133 * then the pointer passed in is set to the current module.
134 */
135 static void
136 open_internal(Extension *in_plug, gpointer in_data)
137 {
138 if (!in_plug->deactivated() && dynamic_cast<Input *>(in_plug)) {
139 gpointer *parray = (gpointer *)in_data;
140 gchar const *filename = (gchar const *)parray[0];
141 Input **pimod = (Input **)parray[1];
143 // skip all the rest if we already found a function to open it
144 // since they're ordered by preference now.
145 if (!*pimod) {
146 gchar const *ext = dynamic_cast<Input *>(in_plug)->get_extension();
148 gchar *filenamelower = g_utf8_strdown(filename, -1);
149 gchar *extensionlower = g_utf8_strdown(ext, -1);
151 if (g_str_has_suffix(filenamelower, extensionlower)) {
152 *pimod = dynamic_cast<Input *>(in_plug);
153 }
155 g_free(filenamelower);
156 g_free(extensionlower);
157 }
158 }
160 return;
161 }
163 /**
164 * \return None
165 * \brief This is a generic function to use the save function of
166 * a module (including Autodetect)
167 * \param key Identifier of which module to use
168 * \param doc The document to be saved
169 * \param filename The file that the document should be saved to
170 * \param official (optional) whether to set :output_module and :modified in the
171 * document; is true for normal save, false for temporary saves
172 *
173 * First things first, are we looking at an autodetection? Well if that's the case then the module
174 * needs to be found, and that is done with a database lookup through the module DB. The foreach
175 * function is called, with the parameter being a gpointer array. It contains both the filename
176 * (to find its extension) and where to write the module when it is found.
177 *
178 * If there is no autodetection the module database is queried with the key given.
179 *
180 * If everything is cool at this point, the module is loaded, and there is possibility for
181 * preferences. If there is a function, then it is executed to get the dialog to be displayed.
182 * After it is finished the function continues.
183 *
184 * Lastly, the save function is called in the module itself.
185 */
186 void
187 save(Extension *key, SPDocument *doc, gchar const *filename, bool setextension, bool check_overwrite, bool official)
188 {
189 Output *omod;
190 if (key == NULL) {
191 gpointer parray[2];
192 parray[0] = (gpointer)filename;
193 parray[1] = (gpointer)&omod;
194 omod = NULL;
195 db.foreach(save_internal, (gpointer)&parray);
197 /* This is a nasty hack, but it is required to ensure that
198 autodetect will always save with the Inkscape extensions
199 if they are available. */
200 if (omod != NULL && !strcmp(omod->get_id(), SP_MODULE_KEY_OUTPUT_SVG)) {
201 omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
202 }
203 /* If autodetect fails, save as Inkscape SVG */
204 if (omod == NULL) {
205 omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
206 }
207 } else {
208 omod = dynamic_cast<Output *>(key);
209 }
211 if (!dynamic_cast<Output *>(omod)) {
212 g_warning("Unable to find output module to handle file: %s\n", filename);
213 throw Output::no_extension_found();
214 return;
215 }
217 omod->set_state(Extension::STATE_LOADED);
218 if (!omod->loaded()) {
219 throw Output::save_failed();
220 }
222 if (!omod->prefs()) {
223 throw Output::save_cancelled();
224 }
226 gchar *fileName = NULL;
227 if (setextension) {
228 gchar *lowerfile = g_utf8_strdown(filename, -1);
229 gchar *lowerext = g_utf8_strdown(omod->get_extension(), -1);
231 if (!g_str_has_suffix(lowerfile, lowerext)) {
232 fileName = g_strdup_printf("%s%s", filename, omod->get_extension());
233 }
235 g_free(lowerfile);
236 g_free(lowerext);
237 }
239 if (fileName == NULL) {
240 fileName = g_strdup(filename);
241 }
243 if (check_overwrite && !sp_ui_overwrite_file(fileName)) {
244 g_free(fileName);
245 throw Output::no_overwrite();
246 }
248 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
251 // remember attributes in case this is an unofficial save and/or overwrite fails
252 gchar *saved_uri = g_strdup(doc->uri);
253 bool saved_modified = false;
254 gchar *saved_output_extension = NULL;
255 gchar *saved_dataloss = NULL;
256 saved_modified = doc->isModifiedSinceSave();
257 saved_output_extension = g_strdup(repr->attribute("inkscape:output_extension"));
258 saved_dataloss = g_strdup(repr->attribute("inkscape:dataloss"));
259 if (official) {
260 /* The document is changing name/uri. */
261 sp_document_change_uri_and_hrefs(doc, fileName);
262 }
264 // Update attributes:
265 {
266 bool const saved = sp_document_get_undo_sensitive(doc);
267 sp_document_set_undo_sensitive(doc, false);
268 {
269 // also save the extension for next use
270 repr->setAttribute("inkscape:output_extension", omod->get_id());
271 // set the "dataloss" attribute if the chosen extension is lossy
272 repr->setAttribute("inkscape:dataloss", NULL);
273 if (omod->causes_dataloss()) {
274 repr->setAttribute("inkscape:dataloss", "true");
275 }
276 }
277 sp_document_set_undo_sensitive(doc, saved);
278 doc->setModifiedSinceSave(false);
279 }
281 try {
282 omod->save(doc, fileName);
283 }
284 catch(...) {
285 // revert attributes in case of official and overwrite
286 if(check_overwrite && official) {
287 bool const saved = sp_document_get_undo_sensitive(doc);
288 sp_document_set_undo_sensitive(doc, false);
289 {
290 repr->setAttribute("inkscape:output_extension", saved_output_extension);
291 repr->setAttribute("inkscape:dataloss", saved_dataloss);
292 }
293 sp_document_set_undo_sensitive(doc, saved);
294 sp_document_change_uri_and_hrefs(doc, saved_uri);
295 }
296 doc->setModifiedSinceSave(saved_modified);
297 // free used ressources
298 g_free(saved_output_extension);
299 g_free(saved_dataloss);
300 g_free(saved_uri);
302 g_free(fileName);
304 throw Inkscape::Extension::Output::save_failed();
305 }
307 // If it is an unofficial save, set the modified attributes back to what they were.
308 if ( !official) {
309 bool const saved = sp_document_get_undo_sensitive(doc);
310 sp_document_set_undo_sensitive(doc, false);
311 {
312 repr->setAttribute("inkscape:output_extension", saved_output_extension);
313 repr->setAttribute("inkscape:dataloss", saved_dataloss);
314 }
315 sp_document_set_undo_sensitive(doc, saved);
316 doc->setModifiedSinceSave(saved_modified);
318 g_free(saved_output_extension);
319 g_free(saved_dataloss);
320 }
322 g_free(fileName);
323 return;
324 }
326 /**
327 * \return none
328 * \brief This is the function that searches each module to see
329 * if it matches the filename for autodetection.
330 * \param in_plug The module to be tested
331 * \param in_data An array of pointers containing the filename, and
332 * the place to put a successfully found module.
333 *
334 * Basically this function only looks at output modules as it is part of the open function. If the
335 * module is an output module, it then starts to take it apart, and the data that is passed in.
336 * Because the data being passed in is in such a weird format, there are a few casts to make it
337 * easier to use. While it looks like a lot of local variables, they'll all get removed by the
338 * compiler.
339 *
340 * First thing that is checked is if the filename is shorter than the extension itself. There is
341 * no way for a match in that case. If it's long enough then there is a string compare of the end
342 * of the filename (for the length of the extension), and the extension itself. If this passes
343 * then the pointer passed in is set to the current module.
344 */
345 static void
346 save_internal(Extension *in_plug, gpointer in_data)
347 {
348 if (!in_plug->deactivated() && dynamic_cast<Output *>(in_plug)) {
349 gpointer *parray = (gpointer *)in_data;
350 gchar const *filename = (gchar const *)parray[0];
351 Output **pomod = (Output **)parray[1];
353 // skip all the rest if we already found someone to save it
354 // since they're ordered by preference now.
355 if (!*pomod) {
356 gchar const *ext = dynamic_cast<Output *>(in_plug)->get_extension();
358 gchar *filenamelower = g_utf8_strdown(filename, -1);
359 gchar *extensionlower = g_utf8_strdown(ext, -1);
361 if (g_str_has_suffix(filenamelower, extensionlower)) {
362 *pomod = dynamic_cast<Output *>(in_plug);
363 }
365 g_free(filenamelower);
366 g_free(extensionlower);
367 }
368 }
370 return;
371 }
373 Print *
374 get_print(gchar const *key)
375 {
376 return dynamic_cast<Print *>(db.get(key));
377 }
379 /**
380 * \return The built module
381 * \brief Creates a module from a Inkscape::XML::Document describing the module
382 * \param doc The XML description of the module
383 *
384 * This function basically has two segments. The first is that it goes through the Repr tree
385 * provided, and determines what kind of of module this is, and what kind of implementation to use.
386 * All of these are then stored in two enums that are defined in this function. This makes it
387 * easier to add additional types (which will happen in the future, I'm sure).
388 *
389 * Second, there is case statements for these enums. The first one is the type of module. This is
390 * the one where the module is actually created. After that, then the implementation is applied to
391 * get the load and unload functions. If there is no implementation then these are not set. This
392 * case could apply to modules that are built in (like the SVG load/save functions).
393 */
394 static Extension *
395 build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp)
396 {
397 enum {
398 MODULE_EXTENSION,
399 MODULE_XSLT,
400 /* MODULE_PLUGIN, */
401 MODULE_UNKNOWN_IMP
402 } module_implementation_type = MODULE_UNKNOWN_IMP;
403 enum {
404 MODULE_INPUT,
405 MODULE_OUTPUT,
406 MODULE_FILTER,
407 MODULE_PRINT,
408 MODULE_PATH_EFFECT,
409 MODULE_UNKNOWN_FUNC
410 } module_functional_type = MODULE_UNKNOWN_FUNC;
412 g_return_val_if_fail(doc != NULL, NULL);
414 Inkscape::XML::Node *repr = doc->root();
416 if (strcmp(repr->name(), INKSCAPE_EXTENSION_NS "inkscape-extension")) {
417 g_warning("Extension definition started with <%s> instead of <" INKSCAPE_EXTENSION_NS "inkscape-extension>. Extension will not be created. See http://wiki.inkscape.org/wiki/index.php/Extensions for reference.\n", repr->name());
418 return NULL;
419 }
421 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
422 while (child_repr != NULL) {
423 char const *element_name = child_repr->name();
424 /* printf("Child: %s\n", child_repr->name()); */
425 if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "input")) {
426 module_functional_type = MODULE_INPUT;
427 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "output")) {
428 module_functional_type = MODULE_OUTPUT;
429 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "effect")) {
430 module_functional_type = MODULE_FILTER;
431 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "print")) {
432 module_functional_type = MODULE_PRINT;
433 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "path-effect")) {
434 module_functional_type = MODULE_PATH_EFFECT;
435 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "script")) {
436 module_implementation_type = MODULE_EXTENSION;
437 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "xslt")) {
438 module_implementation_type = MODULE_XSLT;
439 #if 0
440 } else if (!strcmp(element_name, "plugin")) {
441 module_implementation_type = MODULE_PLUGIN;
442 #endif
443 }
445 //Inkscape::XML::Node *old_repr = child_repr;
446 child_repr = sp_repr_next(child_repr);
447 //Inkscape::GC::release(old_repr);
448 }
450 Implementation::Implementation *imp;
451 if (in_imp == NULL) {
452 switch (module_implementation_type) {
453 case MODULE_EXTENSION: {
454 Implementation::Script *script = new Implementation::Script();
455 imp = static_cast<Implementation::Implementation *>(script);
456 break;
457 }
458 case MODULE_XSLT: {
459 Implementation::XSLT *xslt = new Implementation::XSLT();
460 imp = static_cast<Implementation::Implementation *>(xslt);
461 break;
462 }
463 #if 0
464 case MODULE_PLUGIN: {
465 Implementation::Plugin *plugin = new Implementation::Plugin();
466 imp = static_cast<Implementation::Implementation *>(plugin);
467 break;
468 }
469 #endif
470 default: {
471 imp = NULL;
472 break;
473 }
474 }
475 } else {
476 imp = in_imp;
477 }
479 Extension *module = NULL;
480 switch (module_functional_type) {
481 case MODULE_INPUT: {
482 module = new Input(repr, imp);
483 break;
484 }
485 case MODULE_OUTPUT: {
486 module = new Output(repr, imp);
487 break;
488 }
489 case MODULE_FILTER: {
490 module = new Effect(repr, imp);
491 break;
492 }
493 case MODULE_PRINT: {
494 module = new Print(repr, imp);
495 break;
496 }
497 case MODULE_PATH_EFFECT: {
498 module = new PathEffect(repr, imp);
499 break;
500 }
501 default: {
502 break;
503 }
504 }
506 return module;
507 }
509 /**
510 * \return The module created
511 * \brief This function creates a module from a filename of an
512 * XML description.
513 * \param filename The file holding the XML description of the module.
514 *
515 * This function calls build_from_reprdoc with using sp_repr_read_file to create the reprdoc.
516 */
517 Extension *
518 build_from_file(gchar const *filename)
519 {
520 Inkscape::XML::Document *doc = sp_repr_read_file(filename, INKSCAPE_EXTENSION_URI);
521 Extension *ext = build_from_reprdoc(doc, NULL);
522 if (ext != NULL)
523 Inkscape::GC::release(doc);
524 else
525 g_warning("Unable to create extension from definition file %s.\n", filename);
526 return ext;
527 }
529 /**
530 * \return The module created
531 * \brief This function creates a module from a buffer holding an
532 * XML description.
533 * \param buffer The buffer holding the XML description of the module.
534 *
535 * This function calls build_from_reprdoc with using sp_repr_read_mem to create the reprdoc. It
536 * finds the length of the buffer using strlen.
537 */
538 Extension *
539 build_from_mem(gchar const *buffer, Implementation::Implementation *in_imp)
540 {
541 Inkscape::XML::Document *doc = sp_repr_read_mem(buffer, strlen(buffer), INKSCAPE_EXTENSION_URI);
542 Extension *ext = build_from_reprdoc(doc, in_imp);
543 Inkscape::GC::release(doc);
544 return ext;
545 }
548 } } /* namespace Inkscape::Extension */
550 /*
551 Local Variables:
552 mode:c++
553 c-file-style:"stroustrup"
554 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
555 indent-tabs-mode:nil
556 fill-column:99
557 End:
558 */
559 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :