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