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 "db.h"
24 #include "input.h"
25 #include "output.h"
26 #include "effect.h"
27 #include "patheffect.h"
28 #include "print.h"
29 #include "implementation/script.h"
30 #include "implementation/xslt.h"
31 /* #include "implementation/plugin.h" */
33 namespace Inkscape {
34 namespace Extension {
36 static void open_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
37 static void save_internal(Inkscape::Extension::Extension *in_plug, gpointer in_data);
38 static Extension *build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp);
40 /**
41 * \return A new document created from the filename passed in
42 * \brief This is a generic function to use the open function of
43 * a module (including Autodetect)
44 * \param key Identifier of which module to use
45 * \param filename The file that should be opened
46 *
47 * First things first, are we looking at an autodetection? Well if that's the case then the module
48 * needs to be found, and that is done with a database lookup through the module DB. The foreach
49 * function is called, with the parameter being a gpointer array. It contains both the filename
50 * (to find its extension) and where to write the module when it is found.
51 *
52 * If there is no autodetection, then the module database is queried with the key given.
53 *
54 * If everything is cool at this point, the module is loaded, and there is possibility for
55 * preferences. If there is a function, then it is executed to get the dialog to be displayed.
56 * After it is finished the function continues.
57 *
58 * Lastly, the open function is called in the module itself.
59 */
60 SPDocument *
61 open(Extension *key, gchar const *filename)
62 {
63 Input *imod = NULL;
64 if (key == NULL) {
65 gpointer parray[2];
66 parray[0] = (gpointer)filename;
67 parray[1] = (gpointer)&imod;
68 db.foreach(open_internal, (gpointer)&parray);
69 } else {
70 imod = dynamic_cast<Input *>(key);
71 }
73 bool last_chance_svg = false;
74 if (key == NULL && imod == NULL) {
75 last_chance_svg = true;
76 imod = dynamic_cast<Input *>(db.get(SP_MODULE_KEY_INPUT_SVG));
77 }
79 if (imod == NULL) {
80 throw Input::no_extension_found();
81 }
83 imod->set_state(Extension::STATE_LOADED);
85 if (!imod->loaded()) {
86 throw Input::open_failed();
87 }
89 if (!imod->prefs(filename))
90 return NULL;
92 SPDocument *doc = imod->open(filename);
93 if (!doc) {
94 return NULL;
95 }
97 if (last_chance_svg) {
98 /* We can't call sp_ui_error_dialog because we may be
99 running from the console, in which case calling sp_ui
100 routines will cause a segfault. See bug 1000350 - bryce */
101 // sp_ui_error_dialog(_("Format autodetect failed. The file is being opened as SVG."));
102 g_warning(_("Format autodetect failed. The file is being opened as SVG."));
103 }
105 /* This kinda overkill as most of these are already set, but I want
106 to make sure for this release -- TJG */
107 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
108 bool saved = sp_document_get_undo_sensitive(doc);
109 sp_document_set_undo_sensitive(doc, false);
110 repr->setAttribute("sodipodi:modified", NULL);
111 sp_document_set_undo_sensitive(doc, saved);
113 sp_document_set_uri(doc, filename);
115 return doc;
116 }
118 /**
119 * \return none
120 * \brief This is the function that searches each module to see
121 * if it matches the filename for autodetection.
122 * \param in_plug The module to be tested
123 * \param in_data An array of pointers containing the filename, and
124 * the place to put a successfully found module.
125 *
126 * Basically this function only looks at input modules as it is part of the open function. If the
127 * module is an input module, it then starts to take it apart, and the data that is passed in.
128 * Because the data being passed in is in such a weird format, there are a few casts to make it
129 * easier to use. While it looks like a lot of local variables, they'll all get removed by the
130 * compiler.
131 *
132 * First thing that is checked is if the filename is shorter than the extension itself. There is
133 * no way for a match in that case. If it's long enough then there is a string compare of the end
134 * of the filename (for the length of the extension), and the extension itself. If this passes
135 * then the pointer passed in is set to the current module.
136 */
137 static void
138 open_internal(Extension *in_plug, gpointer in_data)
139 {
140 if (!in_plug->deactivated() && dynamic_cast<Input *>(in_plug)) {
141 gpointer *parray = (gpointer *)in_data;
142 gchar const *filename = (gchar const *)parray[0];
143 Input **pimod = (Input **)parray[1];
145 // skip all the rest if we already found a function to open it
146 // since they're ordered by preference now.
147 if (!*pimod) {
148 gchar const *ext = dynamic_cast<Input *>(in_plug)->get_extension();
150 gchar *filenamelower = g_utf8_strdown(filename, -1);
151 gchar *extensionlower = g_utf8_strdown(ext, -1);
153 if (g_str_has_suffix(filenamelower, extensionlower)) {
154 *pimod = dynamic_cast<Input *>(in_plug);
155 }
157 g_free(filenamelower);
158 g_free(extensionlower);
159 }
160 }
162 return;
163 }
165 /**
166 * \return None
167 * \brief This is a generic function to use the save function of
168 * a module (including Autodetect)
169 * \param key Identifier of which module to use
170 * \param doc The document to be saved
171 * \param filename The file that the document should be saved to
172 * \param official (optional) whether to set :output_module and :modified in the
173 * document; is true for normal save, false for temporary saves
174 *
175 * First things first, are we looking at an autodetection? Well if that's the case then the module
176 * needs to be found, and that is done with a database lookup through the module DB. The foreach
177 * function is called, with the parameter being a gpointer array. It contains both the filename
178 * (to find its extension) and where to write the module when it is found.
179 *
180 * If there is no autodetection the module database is queried with the key given.
181 *
182 * If everything is cool at this point, the module is loaded, and there is possibility for
183 * preferences. If there is a function, then it is executed to get the dialog to be displayed.
184 * After it is finished the function continues.
185 *
186 * Lastly, the save function is called in the module itself.
187 */
188 void
189 save(Extension *key, SPDocument *doc, gchar const *filename, bool setextension, bool check_overwrite, bool official)
190 {
191 Output *omod;
192 if (key == NULL) {
193 gpointer parray[2];
194 parray[0] = (gpointer)filename;
195 parray[1] = (gpointer)&omod;
196 omod = NULL;
197 db.foreach(save_internal, (gpointer)&parray);
199 /* This is a nasty hack, but it is required to ensure that
200 autodetect will always save with the Inkscape extensions
201 if they are available. */
202 if (omod != NULL && !strcmp(omod->get_id(), SP_MODULE_KEY_OUTPUT_SVG)) {
203 omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
204 }
205 /* If autodetect fails, save as Inkscape SVG */
206 if (omod == NULL) {
207 omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
208 }
209 } else {
210 omod = dynamic_cast<Output *>(key);
211 }
213 if (!dynamic_cast<Output *>(omod)) {
214 g_warning("Unable to find output module to handle file: %s\n", filename);
215 throw Output::no_extension_found();
216 return;
217 }
219 omod->set_state(Extension::STATE_LOADED);
220 if (!omod->loaded()) {
221 throw Output::save_failed();
222 }
224 if (!omod->prefs())
225 return;
227 gchar *fileName = NULL;
228 if (setextension) {
229 gchar *lowerfile = g_utf8_strdown(filename, -1);
230 gchar *lowerext = g_utf8_strdown(omod->get_extension(), -1);
232 if (!g_str_has_suffix(lowerfile, lowerext)) {
233 fileName = g_strdup_printf("%s%s", filename, omod->get_extension());
234 }
236 g_free(lowerfile);
237 g_free(lowerext);
238 }
240 if (fileName == NULL) {
241 fileName = g_strdup(filename);
242 }
244 if (check_overwrite && !sp_ui_overwrite_file(fileName)) {
245 g_free(fileName);
246 throw Output::no_overwrite();
247 }
249 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
251 // remember attributes in case this is an unofficial save
252 gchar *saved_sodipodi_modified = NULL;
253 gchar *saved_output_extension = NULL;
254 gchar *saved_dataloss = NULL;
255 gchar *saved_uri = NULL;
256 if (!official) {
257 if (repr->attribute("sodipodi:modified")) {
258 saved_sodipodi_modified = g_strdup(repr->attribute("sodipodi:modified"));
259 }
260 if (repr->attribute("inkscape:output_extension")) {
261 saved_output_extension = g_strdup(repr->attribute("inkscape:output_extension"));
262 }
263 if (repr->attribute("inkscape:dataloss")) {
264 saved_dataloss = g_strdup(repr->attribute("inkscape:dataloss"));
265 }
266 if (doc->uri) {
267 saved_uri = g_strdup(doc->uri);
268 }
269 }
271 // update attributes:
272 bool saved = sp_document_get_undo_sensitive(doc);
273 sp_document_set_undo_sensitive (doc, false);
274 repr->setAttribute("sodipodi:modified", NULL);
275 // save the filename for next use
276 sp_document_set_uri(doc, fileName);
277 // also save the extension for next use
278 repr->setAttribute("inkscape:output_extension", omod->get_id());
279 // set the "dataloss" attribute if the chosen extension is lossy
280 repr->setAttribute("inkscape:dataloss", NULL);
281 if ( omod->causes_dataloss() ) {
282 repr->setAttribute("inkscape:dataloss", "true");
283 }
284 sp_document_set_undo_sensitive (doc, saved);
286 omod->save(doc, fileName);
288 // if it is an unofficial save, set the modified attributes back to what they were
289 if ( !official) {
290 saved = sp_document_get_undo_sensitive(doc);
291 sp_document_set_undo_sensitive (doc, false);
292 repr->setAttribute("sodipodi:modified", saved_sodipodi_modified);
293 repr->setAttribute("inkscape:output_extension", saved_output_extension);
294 repr->setAttribute("inkscape:dataloss", saved_dataloss);
295 sp_document_set_uri(doc, saved_uri);
296 sp_document_set_undo_sensitive (doc, saved);
297 }
299 if (saved_sodipodi_modified) g_free(saved_sodipodi_modified);
300 if (saved_output_extension) g_free(saved_output_extension);
301 if (saved_dataloss) g_free(saved_dataloss);
302 if (saved_uri) g_free(saved_uri);
304 g_free(fileName);
305 return;
306 }
308 /**
309 * \return none
310 * \brief This is the function that searches each module to see
311 * if it matches the filename for autodetection.
312 * \param in_plug The module to be tested
313 * \param in_data An array of pointers containing the filename, and
314 * the place to put a successfully found module.
315 *
316 * Basically this function only looks at output modules as it is part of the open function. If the
317 * module is an output module, it then starts to take it apart, and the data that is passed in.
318 * Because the data being passed in is in such a weird format, there are a few casts to make it
319 * easier to use. While it looks like a lot of local variables, they'll all get removed by the
320 * compiler.
321 *
322 * First thing that is checked is if the filename is shorter than the extension itself. There is
323 * no way for a match in that case. If it's long enough then there is a string compare of the end
324 * of the filename (for the length of the extension), and the extension itself. If this passes
325 * then the pointer passed in is set to the current module.
326 */
327 static void
328 save_internal(Extension *in_plug, gpointer in_data)
329 {
330 if (!in_plug->deactivated() && dynamic_cast<Output *>(in_plug)) {
331 gpointer *parray = (gpointer *)in_data;
332 gchar const *filename = (gchar const *)parray[0];
333 Output **pomod = (Output **)parray[1];
335 // skip all the rest if we already found someone to save it
336 // since they're ordered by preference now.
337 if (!*pomod) {
338 gchar const *ext = dynamic_cast<Output *>(in_plug)->get_extension();
340 gchar *filenamelower = g_utf8_strdown(filename, -1);
341 gchar *extensionlower = g_utf8_strdown(ext, -1);
343 if (g_str_has_suffix(filenamelower, extensionlower)) {
344 *pomod = dynamic_cast<Output *>(in_plug);
345 }
347 g_free(filenamelower);
348 g_free(extensionlower);
349 }
350 }
352 return;
353 }
355 Print *
356 get_print(gchar const *key)
357 {
358 return dynamic_cast<Print *>(db.get(key));
359 }
361 /**
362 * \return The built module
363 * \brief Creates a module from a Inkscape::XML::Document describing the module
364 * \param doc The XML description of the module
365 *
366 * This function basically has two segments. The first is that it goes through the Repr tree
367 * provided, and determines what kind of of module this is, and what kind of implementation to use.
368 * All of these are then stored in two enums that are defined in this function. This makes it
369 * easier to add additional types (which will happen in the future, I'm sure).
370 *
371 * Second, there is case statements for these enums. The first one is the type of module. This is
372 * the one where the module is actually created. After that, then the implementation is applied to
373 * get the load and unload functions. If there is no implementation then these are not set. This
374 * case could apply to modules that are built in (like the SVG load/save functions).
375 */
376 static Extension *
377 build_from_reprdoc(Inkscape::XML::Document *doc, Implementation::Implementation *in_imp)
378 {
379 enum {
380 MODULE_EXTENSION,
381 MODULE_XSLT,
382 /* MODULE_PLUGIN, */
383 MODULE_UNKNOWN_IMP
384 } module_implementation_type = MODULE_UNKNOWN_IMP;
385 enum {
386 MODULE_INPUT,
387 MODULE_OUTPUT,
388 MODULE_FILTER,
389 MODULE_PRINT,
390 MODULE_PATH_EFFECT,
391 MODULE_UNKNOWN_FUNC
392 } module_functional_type = MODULE_UNKNOWN_FUNC;
394 g_return_val_if_fail(doc != NULL, NULL);
396 Inkscape::XML::Node *repr = sp_repr_document_root(doc);
398 /* sp_repr_print(repr); */
400 if (strcmp(repr->name(), "inkscape-extension")) {
401 g_warning("Extension definition started with <%s> instead of <inkscape-extension>. Extension will not be created.\n", repr->name());
402 return NULL;
403 }
405 Inkscape::XML::Node *child_repr = sp_repr_children(repr);
406 while (child_repr != NULL) {
407 char const *element_name = child_repr->name();
408 /* printf("Child: %s\n", child_repr->name()); */
409 if (!strcmp(element_name, "input")) {
410 module_functional_type = MODULE_INPUT;
411 } else if (!strcmp(element_name, "output")) {
412 module_functional_type = MODULE_OUTPUT;
413 } else if (!strcmp(element_name, "effect")) {
414 module_functional_type = MODULE_FILTER;
415 } else if (!strcmp(element_name, "print")) {
416 module_functional_type = MODULE_PRINT;
417 } else if (!strcmp(element_name, "path-effect")) {
418 module_functional_type = MODULE_PATH_EFFECT;
419 } else if (!strcmp(element_name, "script")) {
420 module_implementation_type = MODULE_EXTENSION;
421 } else if (!strcmp(element_name, "xslt")) {
422 module_implementation_type = MODULE_XSLT;
423 #if 0
424 } else if (!strcmp(element_name, "plugin")) {
425 module_implementation_type = MODULE_PLUGIN;
426 #endif
427 }
429 //Inkscape::XML::Node *old_repr = child_repr;
430 child_repr = sp_repr_next(child_repr);
431 //Inkscape::GC::release(old_repr);
432 }
434 Implementation::Implementation *imp;
435 if (in_imp == NULL) {
436 switch (module_implementation_type) {
437 case MODULE_EXTENSION: {
438 Implementation::Script *script = new Implementation::Script();
439 imp = static_cast<Implementation::Implementation *>(script);
440 break;
441 }
442 case MODULE_XSLT: {
443 Implementation::XSLT *xslt = new Implementation::XSLT();
444 imp = static_cast<Implementation::Implementation *>(xslt);
445 break;
446 }
447 #if 0
448 case MODULE_PLUGIN: {
449 Implementation::Plugin *plugin = new Implementation::Plugin();
450 imp = static_cast<Implementation::Implementation *>(plugin);
451 break;
452 }
453 #endif
454 default: {
455 imp = NULL;
456 break;
457 }
458 }
459 } else {
460 imp = in_imp;
461 }
463 Extension *module = NULL;
464 switch (module_functional_type) {
465 case MODULE_INPUT: {
466 module = new Input(repr, imp);
467 break;
468 }
469 case MODULE_OUTPUT: {
470 module = new Output(repr, imp);
471 break;
472 }
473 case MODULE_FILTER: {
474 module = new Effect(repr, imp);
475 break;
476 }
477 case MODULE_PRINT: {
478 module = new Print(repr, imp);
479 break;
480 }
481 case MODULE_PATH_EFFECT: {
482 module = new PathEffect(repr, imp);
483 break;
484 }
485 default: {
486 break;
487 }
488 }
490 return module;
491 }
493 /**
494 * \return The module created
495 * \brief This function creates a module from a filename of an
496 * XML description.
497 * \param filename The file holding the XML description of the module.
498 *
499 * This function calls build_from_reprdoc with using sp_repr_read_file to create the reprdoc.
500 */
501 Extension *
502 build_from_file(gchar const *filename)
503 {
504 /* TODO: Need to define namespace here, need to write the
505 DTD in general for this stuff */
506 Inkscape::XML::Document *doc = sp_repr_read_file(filename, NULL);
507 Extension *ext = build_from_reprdoc(doc, NULL);
508 if (ext != NULL)
509 Inkscape::GC::release(doc);
510 else
511 g_warning("Unable to create extension from definition file %s.\n", filename);
512 return ext;
513 }
515 /**
516 * \return The module created
517 * \brief This function creates a module from a buffer holding an
518 * XML description.
519 * \param buffer The buffer holding the XML description of the module.
520 *
521 * This function calls build_from_reprdoc with using sp_repr_read_mem to create the reprdoc. It
522 * finds the length of the buffer using strlen.
523 */
524 Extension *
525 build_from_mem(gchar const *buffer, Implementation::Implementation *in_imp)
526 {
527 Inkscape::XML::Document *doc = sp_repr_read_mem(buffer, strlen(buffer), NULL);
528 Extension *ext = build_from_reprdoc(doc, in_imp);
529 Inkscape::GC::release(doc);
530 return ext;
531 }
534 } } /* namespace Inkscape::Extension */
536 /*
537 Local Variables:
538 mode:c++
539 c-file-style:"stroustrup"
540 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
541 indent-tabs-mode:nil
542 fill-column:99
543 End:
544 */
545 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :