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