Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
system.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * This is file is kind of the junk file. Basically everything that
4 * didn't fit in one of the other well defined areas, well, it's now
5 * here. Which is good in someways, but this file really needs some
6 * definition. Hopefully that will come ASAP.
7 *
8 * Authors:
9 * Ted Gould <ted@gould.cx>
10 * Johan Engelen <johan@shouraizou.nl>
11 * Jon A. Cruz <jon@joncruz.org>
12 * Abhishek Sharma
13 *
14 * Copyright (C) 2006-2007 Johan Engelen
15 * Copyright (C) 2002-2004 Ted Gould
16 *
17 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
18 */
19
20#include "system.h"
21
22#include <glibmm/miscutils.h>
23
24#include "db.h"
25#include "document.h"
26#include "document-undo.h"
27#include "effect.h"
28#include "extension.h"
30#include "implementation/xslt.h"
31#include "inkscape.h"
32#include "input.h"
33#include "io/sys.h"
34#include "loader.h"
35#include "output.h"
36#include "patheffect.h"
37#include "preferences.h"
38#include "print.h"
39#include "template.h"
40#include "ui/interface.h"
41#include "xml/rebase-hrefs.h"
42
43namespace Inkscape::Extension {
44
66std::unique_ptr<SPDocument> open(Extension *key, char const *filename, bool is_importing)
67{
68 Input *imod = dynamic_cast<Input *>(key ? key : Input::find_by_filename(filename));
69
70 bool last_chance_svg = false;
71 if (!key && !imod) {
72 last_chance_svg = true;
73 imod = dynamic_cast<Input *>(db.get(SP_MODULE_KEY_INPUT_SVG));
74 }
75
76 if (!imod) {
78 }
79
80 // Hide pixbuf extensions depending on user preferences.
81 //g_warning("Extension: %s", imod->get_id());
82
83 bool show = true;
84 if (std::strlen(imod->get_id()) > 21) {
86 bool ask = prefs->getBool("/dialogs/import/ask");
87 bool ask_svg = prefs->getBool("/dialogs/import/ask_svg");
88 Glib::ustring id = Glib::ustring(imod->get_id(), 22);
89 if (id.compare("org.inkscape.input.svg") == 0) {
90 if (ask_svg && is_importing) {
91 show = true;
92 imod->set_gui(true);
93 } else {
94 show = false;
95 imod->set_gui(false);
96 }
97 } else if(strlen(imod->get_id()) > 27) {
98 id = Glib::ustring(imod->get_id(), 28);
99 if (!ask && id.compare( "org.inkscape.input.gdkpixbuf") == 0) {
100 show = false;
101 imod->set_gui(false);
102 }
103 }
104 }
106
107 if (!imod->loaded()) {
108 throw Input::open_failed();
109 }
110
111 if (!imod->prefs()) {
112 throw Input::open_cancelled();
113 }
114
115 auto doc = imod->open(filename, is_importing);
116
117 if (!doc) {
118 if (last_chance_svg) {
119 if ( INKSCAPE.use_gui() ) {
120 sp_ui_error_dialog(_("Could not detect file format. Tried to open it as an SVG anyway but this also failed."));
121 } else {
122 g_warning("%s", _("Could not detect file format. Tried to open it as an SVG anyway but this also failed."));
123 }
124 }
125 throw Input::open_failed();
126 }
127 // If last_chance_svg is true here, it means we successfully opened a file as an svg
128 // and there's no need to warn the user about it, just do it.
129
130 doc->setDocumentFilename(filename);
131 if (!show) {
132 imod->set_gui(true);
133 }
134
135 return doc;
136}
137
161void
162save(Extension *key, SPDocument *doc, gchar const *filename, bool check_overwrite, bool official,
164{
165 Output *omod = nullptr;
166 if (key == nullptr) {
168 for (auto mod : db.get_output_list(o)) {
169 if (mod->can_save_filename(filename)) {
170 omod = mod;
171 break;
172 }
173 }
174
175 /* This is a nasty hack, but it is required to ensure that
176 autodetect will always save with the Inkscape extensions
177 if they are available. */
178 if (omod != nullptr && !strcmp(omod->get_id(), SP_MODULE_KEY_OUTPUT_SVG)) {
179 omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE));
180 }
181 /* If autodetect fails, save as Inkscape SVG */
182 if (omod == nullptr) {
183 // omod = dynamic_cast<Output *>(db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE)); use exception and let user choose
184 }
185 } else {
186 omod = dynamic_cast<Output *>(key);
187 }
188
189 if (!dynamic_cast<Output *>(omod)) {
190 g_warning("Unable to find output module to handle file: %s\n", filename);
192 }
193
195 if (!omod->loaded()) {
196 throw Output::save_failed();
197 }
198
199 if (!omod->prefs()) {
201 }
202
203 if (check_overwrite && !sp_ui_overwrite_file(filename)) {
204 throw Output::no_overwrite();
205 }
206
207 // test if the file exists and is writable
208 // the test only checks the file attributes and might pass where ACL does not allow writes
209 if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS) && !Inkscape::IO::file_is_writable(filename)) {
211 }
212
213 Inkscape::XML::Node *repr = doc->getReprRoot();
214
215
216 // remember attributes in case this is an unofficial save and/or overwrite fails
217 gchar *saved_filename = g_strdup(doc->getDocumentFilename());
218 gchar *saved_output_extension = nullptr;
219 gchar *saved_dataloss = nullptr;
220 bool saved_modified = doc->isModifiedSinceSave();
221 saved_output_extension = g_strdup(get_file_save_extension(save_method).c_str());
222 saved_dataloss = g_strdup(repr->attribute("inkscape:dataloss"));
223 if (official) {
224 // The document is changing name/uri.
225 doc->changeFilenameAndHrefs(filename);
226 }
227
228 // Update attributes:
229 {
230 {
232 // also save the extension for next use
233 store_file_extension_in_prefs (omod->get_id(), save_method);
234 // set the "dataloss" attribute if the chosen extension is lossy
235 repr->removeAttribute("inkscape:dataloss");
236 if (omod->causes_dataloss()) {
237 repr->setAttribute("inkscape:dataloss", "true");
238 }
239 }
240 doc->setModifiedSinceSave(false);
241 }
242
243 try {
244 omod->save(doc, filename);
245 }
246 catch(...) {
247 // revert attributes in case of official and overwrite
248 if(check_overwrite && official) {
249 {
251 store_file_extension_in_prefs (saved_output_extension, save_method);
252 repr->setAttribute("inkscape:dataloss", saved_dataloss);
253 }
254 doc->changeFilenameAndHrefs(saved_filename);
255 }
256 doc->setModifiedSinceSave(saved_modified);
257 // free used resources
258 g_free(saved_output_extension);
259 g_free(saved_dataloss);
260 g_free(saved_filename);
261
262 throw;
263 }
264
265 // If it is an unofficial save, set the modified attributes back to what they were.
266 if ( !official) {
267 {
269 store_file_extension_in_prefs (saved_output_extension, save_method);
270 repr->setAttribute("inkscape:dataloss", saved_dataloss);
271 }
272 doc->setModifiedSinceSave(saved_modified);
273
274 g_free(saved_output_extension);
275 g_free(saved_dataloss);
276 }
277
278 return;
279}
280
281Print *
282get_print(gchar const *key)
283{
284 return dynamic_cast<Print *>(db.get(key));
285}
286
304bool
305build_from_reprdoc(Inkscape::XML::Document *doc, std::unique_ptr<Implementation::Implementation> in_imp, std::string* baseDir, std::string* file_name)
306{
307 ModuleImpType module_implementation_type = MODULE_UNKNOWN_IMP;
308 ModuleFuncType module_functional_type = MODULE_UNKNOWN_FUNC;
309
310 g_return_val_if_fail(doc != nullptr, false);
311
312 Inkscape::XML::Node *repr = doc->root();
313
314 if (strcmp(repr->name(), INKSCAPE_EXTENSION_NS "inkscape-extension")) {
315 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());
316 return false;
317 }
318
319 Inkscape::XML::Node *child_repr = repr->firstChild();
320 while (child_repr != nullptr) {
321 char const *element_name = child_repr->name();
322 /* printf("Child: %s\n", child_repr->name()); */
323 if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "input")) {
324 module_functional_type = MODULE_INPUT;
325 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "template")) {
326 module_functional_type = MODULE_TEMPLATE;
327 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "output")) {
328 module_functional_type = MODULE_OUTPUT;
329 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "effect")) {
330 module_functional_type = MODULE_FILTER;
331 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "print")) {
332 module_functional_type = MODULE_PRINT;
333 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "path-effect")) {
334 module_functional_type = MODULE_PATH_EFFECT;
335 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "script")) {
336 module_implementation_type = MODULE_EXTENSION;
337 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "xslt")) {
338 module_implementation_type = MODULE_XSLT;
339 } else if (!strcmp(element_name, INKSCAPE_EXTENSION_NS "plugin")) {
340 module_implementation_type = MODULE_PLUGIN;
341 }
342
343 //Inkscape::XML::Node *old_repr = child_repr;
344 child_repr = child_repr->next();
345 //Inkscape::GC::release(old_repr);
346 }
347
348 using ImplementationHolder = Util::HybridPointer<Implementation::Implementation>;
349 ImplementationHolder imp;
350 if (in_imp) {
351 imp = std::move(in_imp);
352 } else {
353 switch (module_implementation_type) {
354 case MODULE_EXTENSION: {
355 imp = ImplementationHolder::make_owning<Implementation::Script>();
356 break;
357 }
358 case MODULE_XSLT: {
359 imp = ImplementationHolder::make_owning<Implementation::XSLT>();
360 break;
361 }
362 case MODULE_PLUGIN: {
363 auto loader = Inkscape::Extension::Loader();
364 if (baseDir) {
365 loader.set_base_directory(*baseDir);
366 }
367 imp = ImplementationHolder::make_nonowning(loader.load_implementation(doc));
368 break;
369 }
370 }
371 }
372
373 std::unique_ptr<Extension> module;
374 try {
375 switch (module_functional_type) {
376 case MODULE_INPUT: {
377 module = std::make_unique<Input>(repr, std::move(imp), baseDir);
378 break;
379 }
380 case MODULE_TEMPLATE: {
381 module = std::make_unique<Template>(repr, std::move(imp), baseDir);
382 break;
383 }
384 case MODULE_OUTPUT: {
385 module = std::make_unique<Output>(repr, std::move(imp), baseDir);
386 break;
387 }
388 case MODULE_FILTER: {
389 module = std::make_unique<Effect>(repr, std::move(imp), baseDir, file_name);
390 break;
391 }
392 case MODULE_PRINT: {
393 module = std::make_unique<Print>(repr, std::move(imp), baseDir);
394 break;
395 }
396 case MODULE_PATH_EFFECT: {
397 module = std::make_unique<PathEffect>(repr, std::move(imp), baseDir);
398 break;
399 }
400 default: {
401 g_warning("Extension of unknown type!"); // TODO: Should not happen! Is this even useful?
402 module = std::make_unique<Extension>(repr, std::move(imp), baseDir);
403 break;
404 }
405 }
406 } catch (const Extension::extension_no_id &) {
407 g_warning("Building extension failed. Extension does not have a valid ID");
408 return false;
409 } catch (const Extension::extension_no_name &) {
410 g_warning("Building extension failed. Extension does not have a valid name");
411 return false;
412 } catch (const Extension::extension_not_compatible &) {
413 return true; // This is not an actual error; just silently ignore the extension
415 g_warning("Building extension failed: no implementation was found");
416 return false;
417 }
418
419 assert(module);
420 db.take_ownership(std::move(module));
421 return true;
422}
423
431void
432build_from_file(gchar const *filename)
433{
434 std::string dir = Glib::path_get_dirname(filename);
435 auto file_name = Glib::path_get_basename(filename);
436
437 Inkscape::XML::Document *doc = sp_repr_read_file(filename, INKSCAPE_EXTENSION_URI);
438 if (!doc) {
439 g_critical("Inkscape::Extension::build_from_file() - XML description loaded from '%s' not valid.", filename);
440 return;
441 }
442
443 if (!build_from_reprdoc(doc, {}, &dir, &file_name)) {
444 g_warning("Inkscape::Extension::build_from_file() - Could not parse extension from '%s'.", filename);
445 }
446
448}
449
458void
459build_from_mem(gchar const *buffer, std::unique_ptr<Implementation::Implementation> in_imp)
460{
461 Inkscape::XML::Document *doc = sp_repr_read_mem(buffer, strlen(buffer), INKSCAPE_EXTENSION_URI);
462 if (!doc) {
463 g_critical("Inkscape::Extension::build_from_mem() - XML description loaded from memory buffer not valid.");
464 return;
465 }
466
467 if (!build_from_reprdoc(doc, std::move(in_imp), nullptr, nullptr)) {
468 g_critical("Inkscape::Extension::build_from_mem() - Could not parse extension from memory buffer.");
469 }
470
472}
473
474/*
475 * TODO: Is it guaranteed that the returned extension is valid? If so, we can remove the check for
476 * filename_extension in sp_file_save_dialog().
477 */
478Glib::ustring
481 Glib::ustring extension;
482 switch (method) {
485 extension = prefs->getString("/dialogs/save_as/default");
486 break;
488 extension = prefs->getString("/dialogs/save_copy/default");
489 break;
491 extension = SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE;
492 break;
495 break;
496 }
497
498 if(extension.empty()) {
499 extension = SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE;
500 }
501
502 return extension;
503}
504
505Glib::ustring
508 Glib::ustring path;
509 bool use_current_dir = true;
510 switch (method) {
512 {
513 use_current_dir = prefs->getBool("/dialogs/save_as/use_current_dir", true);
514 if (doc->getDocumentFilename() && use_current_dir) {
515 path = Glib::path_get_dirname(doc->getDocumentFilename());
516 } else {
517 path = prefs->getString("/dialogs/save_as/path");
518 }
519 break;
520 }
522 path = prefs->getString("/dialogs/save_as/path");
523 break;
525 use_current_dir = prefs->getBool("/dialogs/save_copy/use_current_dir", prefs->getBool("/dialogs/save_as/use_current_dir", true));
526 if (doc->getDocumentFilename() && use_current_dir) {
527 path = Glib::path_get_dirname(doc->getDocumentFilename());
528 } else {
529 path = prefs->getString("/dialogs/save_copy/path");
530 }
531 break;
533 if (doc->getDocumentFilename()) {
534 path = Glib::path_get_dirname(doc->getDocumentFilename());
535 } else {
536 // FIXME: should we use the save_as path here or something else? Maybe we should
537 // leave this as a choice to the user.
538 path = prefs->getString("/dialogs/save_as/path");
539 }
540 break;
543 // defaults to g_get_home_dir()
544 break;
545 }
546
547 if(path.empty()) {
548 path = g_get_home_dir(); // Is this the most sensible solution? Note that we should avoid
549 // g_get_current_dir because this leads to problems on OS X where
550 // Inkscape opens the dialog inside application bundle when it is
551 // invoked for the first teim.
552 }
553
554 return path;
555}
556
557void
558store_file_extension_in_prefs (Glib::ustring extension, FileSaveMethod method) {
560 switch (method) {
563 prefs->setString("/dialogs/save_as/default", extension);
564 break;
566 prefs->setString("/dialogs/save_copy/default", extension);
567 break;
570 // do nothing
571 break;
572 }
573}
574
575void
576store_save_path_in_prefs (Glib::ustring path, FileSaveMethod method) {
578 switch (method) {
581 prefs->setString("/dialogs/save_as/path", path);
582 break;
584 prefs->setString("/dialogs/save_copy/path", path);
585 break;
588 // do nothing
589 break;
590 }
591}
592
593} // namespace Inkscape::Extension
594
595/*
596 Local Variables:
597 mode:c++
598 c-file-style:"stroustrup"
599 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
600 indent-tabs-mode:nil
601 fill-column:99
602 End:
603*/
604// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
RAII-style mechanism for creating a temporary undo-insensitive context.
std::list< Output * > OutputList
Definition db.h:58
void take_ownership(std::unique_ptr< Extension > module)
Take the ownership of an extension to ensure that it is freed on program exit.
Definition db.cpp:86
OutputList & get_output_list(OutputList &ou_list)
Creates a list of all the Output extensions.
Definition db.cpp:256
Extension * get(const gchar *key) const
This function looks up a Inkscape::Extension::Extension by using its unique id. It then returns a ref...
Definition db.cpp:101
no valid ID found while parsing XML representation
Definition extension.h:218
no valid name found while parsing XML representation
Definition extension.h:221
extension is not compatible with the current system and should not be loaded
Definition extension.h:224
No implementation could be loaded for the extension.
Definition extension.h:227
An error class for when a filename already exists, but the user doesn't want to overwrite it.
Definition extension.h:231
The object that is the basis for the Extension system.
Definition extension.h:133
@ STATE_LOADED
The extension has been loaded successfully.
Definition extension.h:137
char const * get_id() const
Get the ID of this extension - not a copy don't delete!
void set_state(state_t in_state)
A function to set whether the extension should be loaded or unloaded.
bool loaded()
A quick function to test the state of the extension.
virtual bool prefs()
Create a dialog for preference for this extension.
static Extension * find_by_filename(char const *filename)
Get an input extension by filename matching.
Definition input.cpp:257
std::unique_ptr< SPDocument > open(char const *uri, bool is_importing=false)
This function creates a document from a file.
Definition input.cpp:147
This class contains the mechanism to load c++ plugins dynamically.
Definition loader.h:30
The existing file can not be opened for writing.
Definition output.h:37
Failed because we couldn't find an extension to match the filename.
Definition output.h:36
Generic failure for an undescribed reason.
Definition output.h:34
bool causes_dataloss() const
Definition output.h:61
void save(SPDocument *doc, gchar const *filename, bool detachbase=false)
Save a document as a file.
Definition output.cpp:215
Preference storage class.
Definition preferences.h:66
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
Glib::ustring getString(Glib::ustring const &pref_path, Glib::ustring const &def="")
Retrieve an UTF-8 string.
static Preferences * get()
Access the singleton Preferences object.
void setString(Glib::ustring const &pref_path, Glib::ustring const &value)
Set an UTF-8 string value.
A helper class holding an owning or non-owning pointer depending on the memory management requirement...
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * next()=0
Get the next sibling of this node.
virtual char const * name() const =0
Get the name of the element node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual Node * firstChild()=0
Get the first child of this node.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
Definition node.h:280
virtual Node * root()=0
Get the root node of this node's document.
Typed SVG document implementation.
Definition document.h:103
char const * getDocumentFilename() const
Definition document.h:231
bool isModifiedSinceSave() const
Definition document.h:140
void setModifiedSinceSave(bool const modified=true)
Indicate to the user if the document has been modified since the last save by displaying a "*" in fro...
Inkscape::XML::Node * getReprRoot()
Definition document.h:208
void changeFilenameAndHrefs(char const *filename)
Changes the base, name and filename members of document, and updates any relative hrefs in the docume...
TODO: insert short description here.
Inkscape::Extension::Extension: Frontend to certain, possibly pluggable, actions.
ModuleImpType
Definition extension.h:88
@ MODULE_UNKNOWN_IMP
Definition extension.h:92
@ MODULE_PLUGIN
Definition extension.h:91
@ MODULE_EXTENSION
Definition extension.h:89
@ MODULE_XSLT
Definition extension.h:90
ModuleFuncType
Definition extension.h:96
@ MODULE_UNKNOWN_FUNC
Definition extension.h:103
@ MODULE_OUTPUT
Definition extension.h:99
@ MODULE_FILTER
Definition extension.h:100
@ MODULE_INPUT
Definition extension.h:98
@ MODULE_PATH_EFFECT
Definition extension.h:102
@ MODULE_PRINT
Definition extension.h:101
@ MODULE_TEMPLATE
Definition extension.h:97
void sp_ui_error_dialog(char const *message)
Definition interface.cpp:56
bool sp_ui_overwrite_file(std::string const &filename)
If necessary, ask the user if a file may be overwritten.
Definition interface.cpp:74
Loader for external plug-ins.
Extension support.
DB db
This is the actual database object.
Definition db.cpp:32
Glib::ustring get_file_save_extension(Inkscape::Extension::FileSaveMethod method)
Determine the desired default file extension depending on the given file save method.
Definition system.cpp:479
std::unique_ptr< SPDocument > open(Extension *key, char const *filename, bool is_importing)
This is a generic function to use the open function of a module (including Autodetect)
Definition system.cpp:66
void store_file_extension_in_prefs(Glib::ustring extension, FileSaveMethod method)
Write the given file extension back to prefs so that it can be used later on.
Definition system.cpp:558
void save(Extension *key, SPDocument *doc, gchar const *filename, bool check_overwrite, bool official, Inkscape::Extension::FileSaveMethod save_method)
This is a generic function to use the save function of a module (including Autodetect)
Definition system.cpp:162
void store_save_path_in_prefs(Glib::ustring path, FileSaveMethod method)
Write the given path back to prefs so that it can be used later on.
Definition system.cpp:576
Print * get_print(gchar const *key)
Definition system.cpp:282
bool build_from_reprdoc(Inkscape::XML::Document *doc, std::unique_ptr< Implementation::Implementation > in_imp, std::string *baseDir, std::string *file_name)
Creates a module from a Inkscape::XML::Document describing the module.
Definition system.cpp:305
void build_from_mem(gchar const *buffer, std::unique_ptr< Implementation::Implementation > in_imp)
Create a module from a buffer holding an XML description.
Definition system.cpp:459
FileSaveMethod
Used to distinguish between the various invocations of the save dialogs (and thus to determine the fi...
Definition system.h:34
@ FILE_SAVE_METHOD_SAVE_COPY
Definition system.h:36
@ FILE_SAVE_METHOD_INKSCAPE_SVG
Definition system.h:40
@ FILE_SAVE_METHOD_TEMPORARY
Definition system.h:42
void build_from_file(gchar const *filename)
This function creates a module from a filename of an XML description.
Definition system.cpp:432
Glib::ustring get_file_save_path(SPDocument *doc, FileSaveMethod method)
Determine the desired default save path depending on the given FileSaveMethod.
Definition system.cpp:506
static R & release(R &r)
Decrements the reference count of a anchored object.
bool file_test(char const *utf8name, GFileTest test)
Definition sys.cpp:116
bool file_is_writable(char const *utf8name)
Definition sys.cpp:149
static cairo_user_data_key_t key
Singleton class to access the preferences file in a convenient way.
TODO: insert short description here.
Document * sp_repr_read_file(const gchar *filename, const gchar *default_ns, bool xinclude)
Reads XML from a file, and returns the Document.
Definition repr-io.cpp:274
Document * sp_repr_read_mem(const gchar *buffer, gint length, const gchar *default_ns)
Reads and parses XML from a buffer, returning it as an Document.
Definition repr-io.cpp:324
Interface for XML documents.
Definition document.h:43