1 #define __SP_FILE_C__
3 /*
4 * File/Print operations
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 * Chema Celorio <chema@celorio.com>
9 * bulia byak <buliabyak@users.sf.net>
10 * Bruno Dilly <bruno.dilly@gmail.com>
11 * Stephen Silver <sasilver@users.sourceforge.net>
12 *
13 * Copyright (C) 2006 Johan Engelen <johan@shouraizou.nl>
14 * Copyright (C) 1999-2008 Authors
15 * Copyright (C) 2004 David Turner
16 * Copyright (C) 2001-2002 Ximian, Inc.
17 *
18 * Released under GNU GPL, read the file 'COPYING' for more information
19 */
21 /**
22 * Note: This file needs to be cleaned up extensively.
23 * What it probably needs is to have one .h file for
24 * the API, and two or more .cpp files for the implementations.
25 */
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <glib/gmem.h>
32 #include <libnr/nr-pixops.h>
34 #include "document-private.h"
35 #include "selection-chemistry.h"
36 #include "ui/view/view-widget.h"
37 #include "dir-util.h"
38 #include "helper/png-write.h"
39 #include "dialogs/export.h"
40 #include <glibmm/i18n.h>
41 #include "inkscape.h"
42 #include "desktop.h"
43 #include "selection.h"
44 #include "interface.h"
45 #include "style.h"
46 #include "print.h"
47 #include "file.h"
48 #include "message.h"
49 #include "message-stack.h"
50 #include "ui/dialog/filedialog.h"
51 #include "ui/dialog/ocaldialogs.h"
52 #include "prefs-utils.h"
53 #include "path-prefix.h"
55 #include "sp-namedview.h"
56 #include "desktop-handles.h"
58 #include "extension/db.h"
59 #include "extension/input.h"
60 #include "extension/output.h"
61 /* #include "extension/menu.h" */
62 #include "extension/system.h"
64 #include "io/sys.h"
65 #include "application/application.h"
66 #include "application/editor.h"
67 #include "inkscape.h"
68 #include "uri.h"
69 #include "id-clash.h"
71 #ifdef WITH_GNOME_VFS
72 # include <libgnomevfs/gnome-vfs.h>
73 #endif
75 #ifdef WITH_INKBOARD
76 #include "jabber_whiteboard/session-manager.h"
77 #endif
79 #ifdef WIN32
80 #include <windows.h>
81 #endif
83 //#define INK_DUMP_FILENAME_CONV 1
84 #undef INK_DUMP_FILENAME_CONV
86 //#define INK_DUMP_FOPEN 1
87 #undef INK_DUMP_FOPEN
89 void dump_str(gchar const *str, gchar const *prefix);
90 void dump_ustr(Glib::ustring const &ustr);
93 /*######################
94 ## N E W
95 ######################*/
97 /**
98 * Create a blank document and add it to the desktop
99 */
100 SPDesktop*
101 sp_file_new(const Glib::ustring &templ)
102 {
103 char *templName = NULL;
104 if (templ.size()>0)
105 templName = (char *)templ.c_str();
106 SPDocument *doc = sp_document_new(templName, TRUE, true);
107 g_return_val_if_fail(doc != NULL, NULL);
109 SPDesktop *dt;
110 if (Inkscape::NSApplication::Application::getNewGui())
111 {
112 dt = Inkscape::NSApplication::Editor::createDesktop (doc);
113 } else {
114 SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
115 g_return_val_if_fail(dtw != NULL, NULL);
116 sp_document_unref(doc);
118 sp_create_window(dtw, TRUE);
119 dt = static_cast<SPDesktop*>(dtw->view);
120 sp_namedview_window_from_document(dt);
121 sp_namedview_update_layers_from_document(dt);
122 }
123 return dt;
124 }
126 SPDesktop*
127 sp_file_new_default()
128 {
129 std::list<gchar *> sources;
130 sources.push_back( profile_path("templates") ); // first try user's local dir
131 sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
133 while (!sources.empty()) {
134 gchar *dirname = sources.front();
135 if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
137 // TRANSLATORS: default.svg is localizable - this is the name of the default document
138 // template. This way you can localize the default pagesize, translate the name of
139 // the default layer, etc. If you wish to localize this file, please create a
140 // localized share/templates/default.xx.svg file, where xx is your language code.
141 char *default_template = g_build_filename(dirname, _("default.svg"), NULL);
142 if (Inkscape::IO::file_test(default_template, G_FILE_TEST_IS_REGULAR)) {
143 return sp_file_new(default_template);
144 }
145 }
146 g_free(dirname);
147 sources.pop_front();
148 }
150 return sp_file_new("");
151 }
154 /*######################
155 ## D E L E T E
156 ######################*/
158 /**
159 * Perform document closures preceding an exit()
160 */
161 void
162 sp_file_exit()
163 {
164 sp_ui_close_all();
165 // no need to call inkscape_exit here; last document being closed will take care of that
166 }
169 /*######################
170 ## O P E N
171 ######################*/
173 /**
174 * Open a file, add the document to the desktop
175 *
176 * \param replace_empty if true, and the current desktop is empty, this document
177 * will replace the empty one.
178 */
179 bool
180 sp_file_open(const Glib::ustring &uri,
181 Inkscape::Extension::Extension *key,
182 bool add_to_recent, bool replace_empty)
183 {
184 SPDocument *doc = NULL;
185 try {
186 doc = Inkscape::Extension::open(key, uri.c_str());
187 } catch (Inkscape::Extension::Input::no_extension_found &e) {
188 doc = NULL;
189 } catch (Inkscape::Extension::Input::open_failed &e) {
190 doc = NULL;
191 }
193 if (doc) {
194 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
195 SPDocument *existing = desktop ? sp_desktop_document(desktop) : NULL;
197 if (existing && existing->virgin && replace_empty) {
198 // If the current desktop is empty, open the document there
199 sp_document_ensure_up_to_date (doc);
200 desktop->change_document(doc);
201 sp_document_resized_signal_emit (doc, sp_document_width(doc), sp_document_height(doc));
202 } else {
203 if (!Inkscape::NSApplication::Application::getNewGui()) {
204 // create a whole new desktop and window
205 SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL));
206 sp_create_window(dtw, TRUE);
207 desktop = static_cast<SPDesktop*>(dtw->view);
208 } else {
209 desktop = Inkscape::NSApplication::Editor::createDesktop (doc);
210 }
211 }
213 doc->virgin = FALSE;
214 // everyone who cares now has a reference, get rid of ours
215 sp_document_unref(doc);
216 // resize the window to match the document properties
217 sp_namedview_window_from_document(desktop);
218 sp_namedview_update_layers_from_document(desktop);
220 if (add_to_recent) {
221 prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
222 }
224 return TRUE;
225 } else {
226 gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
227 gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), safeUri);
228 sp_ui_error_dialog(text);
229 g_free(text);
230 g_free(safeUri);
231 return FALSE;
232 }
233 }
235 /**
236 * Handle prompting user for "do you want to revert"? Revert on "OK"
237 */
238 void
239 sp_file_revert_dialog()
240 {
241 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
242 g_assert(desktop != NULL);
244 SPDocument *doc = sp_desktop_document(desktop);
245 g_assert(doc != NULL);
247 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
248 g_assert(repr != NULL);
250 gchar const *uri = doc->uri;
251 if (!uri) {
252 desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved yet. Cannot revert."));
253 return;
254 }
256 bool do_revert = true;
257 if (doc->isModifiedSinceSave()) {
258 gchar *text = g_strdup_printf(_("Changes will be lost! Are you sure you want to reload document %s?"), uri);
260 bool response = desktop->warnDialog (text);
261 g_free(text);
263 if (!response) {
264 do_revert = false;
265 }
266 }
268 bool reverted;
269 if (do_revert) {
270 // Allow overwriting of current document.
271 doc->virgin = TRUE;
273 // remember current zoom and view
274 double zoom = desktop->current_zoom();
275 NR::Point c = desktop->get_display_area().midpoint();
277 reverted = sp_file_open(uri,NULL);
278 if (reverted) {
279 // restore zoom and view
280 desktop->zoom_absolute(c[NR::X], c[NR::Y], zoom);
281 }
282 } else {
283 reverted = false;
284 }
286 if (reverted) {
287 desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Document reverted."));
288 } else {
289 desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not reverted."));
290 }
291 }
293 void dump_str(gchar const *str, gchar const *prefix)
294 {
295 Glib::ustring tmp;
296 tmp = prefix;
297 tmp += " [";
298 size_t const total = strlen(str);
299 for (unsigned i = 0; i < total; i++) {
300 gchar *const tmp2 = g_strdup_printf(" %02x", (0x0ff & str[i]));
301 tmp += tmp2;
302 g_free(tmp2);
303 }
305 tmp += "]";
306 g_message("%s", tmp.c_str());
307 }
309 void dump_ustr(Glib::ustring const &ustr)
310 {
311 char const *cstr = ustr.c_str();
312 char const *data = ustr.data();
313 Glib::ustring::size_type const byteLen = ustr.bytes();
314 Glib::ustring::size_type const dataLen = ustr.length();
315 Glib::ustring::size_type const cstrLen = strlen(cstr);
317 g_message(" size: %lu\n length: %lu\n bytes: %lu\n clen: %lu",
318 gulong(ustr.size()), gulong(dataLen), gulong(byteLen), gulong(cstrLen) );
319 g_message( " ASCII? %s", (ustr.is_ascii() ? "yes":"no") );
320 g_message( " UTF-8? %s", (ustr.validate() ? "yes":"no") );
322 try {
323 Glib::ustring tmp;
324 for (Glib::ustring::size_type i = 0; i < ustr.bytes(); i++) {
325 tmp = " ";
326 if (i < dataLen) {
327 Glib::ustring::value_type val = ustr.at(i);
328 gchar* tmp2 = g_strdup_printf( (((val & 0xff00) == 0) ? " %02x" : "%04x"), val );
329 tmp += tmp2;
330 g_free( tmp2 );
331 } else {
332 tmp += " ";
333 }
335 if (i < byteLen) {
336 int val = (0x0ff & data[i]);
337 gchar *tmp2 = g_strdup_printf(" %02x", val);
338 tmp += tmp2;
339 g_free( tmp2 );
340 if ( val > 32 && val < 127 ) {
341 tmp2 = g_strdup_printf( " '%c'", (gchar)val );
342 tmp += tmp2;
343 g_free( tmp2 );
344 } else {
345 tmp += " . ";
346 }
347 } else {
348 tmp += " ";
349 }
351 if ( i < cstrLen ) {
352 int val = (0x0ff & cstr[i]);
353 gchar* tmp2 = g_strdup_printf(" %02x", val);
354 tmp += tmp2;
355 g_free(tmp2);
356 if ( val > 32 && val < 127 ) {
357 tmp2 = g_strdup_printf(" '%c'", (gchar) val);
358 tmp += tmp2;
359 g_free( tmp2 );
360 } else {
361 tmp += " . ";
362 }
363 } else {
364 tmp += " ";
365 }
367 g_message( "%s", tmp.c_str() );
368 }
369 } catch (...) {
370 g_message("XXXXXXXXXXXXXXXXXX Exception" );
371 }
372 g_message("---------------");
373 }
375 /**
376 * Display an file Open selector. Open a document if OK is pressed.
377 * Can select single or multiple files for opening.
378 */
379 void
380 sp_file_open_dialog(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*data*/)
381 {
382 //# Get the current directory for finding files
383 static Glib::ustring open_path;
385 if(open_path.empty())
386 {
387 gchar const *attr = prefs_get_string_attribute("dialogs.open", "path");
388 if (attr)
389 open_path = attr;
390 }
392 //# Test if the open_path directory exists
393 if (!Inkscape::IO::file_test(open_path.c_str(),
394 (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
395 open_path = "";
397 #ifdef WIN32
398 //# If no open path, default to our win32 documents folder
399 if (open_path.empty())
400 {
401 // The path to the My Documents folder is read from the
402 // value "HKEY_CURRENT_USER\Software\Windows\CurrentVersion\Explorer\Shell Folders\Personal"
403 HKEY key = NULL;
404 if(RegOpenKeyExA(HKEY_CURRENT_USER,
405 "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
406 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
407 {
408 WCHAR utf16path[_MAX_PATH];
409 DWORD value_type;
410 DWORD data_size = sizeof(utf16path);
411 if(RegQueryValueExW(key, L"Personal", NULL, &value_type,
412 (BYTE*)utf16path, &data_size) == ERROR_SUCCESS)
413 {
414 g_assert(value_type == REG_SZ);
415 gchar *utf8path = g_utf16_to_utf8(
416 (const gunichar2*)utf16path, -1, NULL, NULL, NULL);
417 if(utf8path)
418 {
419 open_path = Glib::ustring(utf8path);
420 g_free(utf8path);
421 }
422 }
423 }
424 }
425 #endif
427 //# If no open path, default to our home directory
428 if (open_path.empty())
429 {
430 open_path = g_get_home_dir();
431 open_path.append(G_DIR_SEPARATOR_S);
432 }
434 //# Create a dialog if we don't already have one
435 Inkscape::UI::Dialog::FileOpenDialog *openDialogInstance =
436 Inkscape::UI::Dialog::FileOpenDialog::create(
437 parentWindow, open_path,
438 Inkscape::UI::Dialog::SVG_TYPES,
439 _("Select file to open"));
441 //# Show the dialog
442 bool const success = openDialogInstance->show();
444 //# Save the folder the user selected for later
445 open_path = openDialogInstance->getCurrentDirectory();
447 if (!success)
448 {
449 delete openDialogInstance;
450 return;
451 }
453 //# User selected something. Get name and type
454 Glib::ustring fileName = openDialogInstance->getFilename();
456 Inkscape::Extension::Extension *selection =
457 openDialogInstance->getSelectionType();
459 //# Code to check & open if multiple files.
460 std::vector<Glib::ustring> flist = openDialogInstance->getFilenames();
462 //# We no longer need the file dialog object - delete it
463 delete openDialogInstance;
464 openDialogInstance = NULL;
466 //# Iterate through filenames if more than 1
467 if (flist.size() > 1)
468 {
469 for (unsigned int i = 0; i < flist.size(); i++)
470 {
471 fileName = flist[i];
473 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
474 if ( newFileName.size() > 0 )
475 fileName = newFileName;
476 else
477 g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
479 #ifdef INK_DUMP_FILENAME_CONV
480 g_message("Opening File %s\n", fileName.c_str());
481 #endif
482 sp_file_open(fileName, selection);
483 }
485 return;
486 }
489 if (!fileName.empty())
490 {
491 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
493 if ( newFileName.size() > 0)
494 fileName = newFileName;
495 else
496 g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
498 open_path = Glib::path_get_dirname (fileName);
499 open_path.append(G_DIR_SEPARATOR_S);
500 prefs_set_string_attribute("dialogs.open", "path", open_path.c_str());
502 sp_file_open(fileName, selection);
503 }
505 return;
506 }
509 /*######################
510 ## V A C U U M
511 ######################*/
513 /**
514 * Remove unreferenced defs from the defs section of the document.
515 */
518 void
519 sp_file_vacuum()
520 {
521 SPDocument *doc = SP_ACTIVE_DOCUMENT;
523 unsigned int diff = vacuum_document (doc);
525 sp_document_done(doc, SP_VERB_FILE_VACUUM,
526 _("Vacuum <defs>"));
528 SPDesktop *dt = SP_ACTIVE_DESKTOP;
529 if (diff > 0) {
530 dt->messageStack()->flashF(Inkscape::NORMAL_MESSAGE,
531 ngettext("Removed <b>%i</b> unused definition in <defs>.",
532 "Removed <b>%i</b> unused definitions in <defs>.",
533 diff),
534 diff);
535 } else {
536 dt->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("No unused definitions in <defs>."));
537 }
538 }
542 /*######################
543 ## S A V E
544 ######################*/
546 /**
547 * This 'save' function called by the others below
548 *
549 * \param official whether to set :output_module and :modified in the
550 * document; is true for normal save, false for temporary saves
551 */
552 static bool
553 file_save(Gtk::Window &parentWindow, SPDocument *doc, const Glib::ustring &uri,
554 Inkscape::Extension::Extension *key, bool saveas, bool official)
555 {
556 if (!doc || uri.size()<1) //Safety check
557 return false;
559 try {
560 Inkscape::Extension::save(key, doc, uri.c_str(),
561 false,
562 saveas, official);
563 } catch (Inkscape::Extension::Output::no_extension_found &e) {
564 gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
565 gchar *text = g_strdup_printf(_("No Inkscape extension found to save document (%s). This may have been caused by an unknown filename extension."), safeUri);
566 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
567 sp_ui_error_dialog(text);
568 g_free(text);
569 g_free(safeUri);
570 return FALSE;
571 } catch (Inkscape::Extension::Output::save_failed &e) {
572 gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str());
573 gchar *text = g_strdup_printf(_("File %s could not be saved."), safeUri);
574 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved."));
575 sp_ui_error_dialog(text);
576 g_free(text);
577 g_free(safeUri);
578 return FALSE;
579 } catch (Inkscape::Extension::Output::no_overwrite &e) {
580 return sp_file_save_dialog(parentWindow, doc);
581 }
583 SP_ACTIVE_DESKTOP->event_log->rememberFileSave();
584 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Document saved."));
585 return true;
586 }
588 /*
589 * Used only for remote saving using VFS and a specific uri. Gets the file at the /tmp.
590 */
591 bool
592 file_save_remote(SPDocument */*doc*/,
593 #ifdef WITH_GNOME_VFS
594 const Glib::ustring &uri,
595 #else
596 const Glib::ustring &/*uri*/,
597 #endif
598 Inkscape::Extension::Extension */*key*/, bool /*saveas*/, bool /*official*/)
599 {
600 #ifdef WITH_GNOME_VFS
602 #define BUF_SIZE 8192
603 gnome_vfs_init();
605 GnomeVFSHandle *from_handle = NULL;
606 GnomeVFSHandle *to_handle = NULL;
607 GnomeVFSFileSize bytes_read;
608 GnomeVFSFileSize bytes_written;
609 GnomeVFSResult result;
610 guint8 buffer[8192];
612 gchar* uri_local = g_filename_from_utf8( uri.c_str(), -1, NULL, NULL, NULL);
614 if ( uri_local == NULL ) {
615 g_warning( "Error converting filename to locale encoding.");
616 }
618 // Gets the temp file name.
619 Glib::ustring fileName = Glib::get_tmp_dir ();
620 fileName.append(G_DIR_SEPARATOR_S);
621 fileName.append((gnome_vfs_uri_extract_short_name(gnome_vfs_uri_new(uri_local))));
623 // Open the temp file to send.
624 result = gnome_vfs_open (&from_handle, fileName.c_str(), GNOME_VFS_OPEN_READ);
626 if (result != GNOME_VFS_OK) {
627 g_warning("Could not find the temp saving.");
628 return false;
629 }
631 result = gnome_vfs_create (&to_handle, uri_local, GNOME_VFS_OPEN_WRITE, FALSE, GNOME_VFS_PERM_USER_ALL);
632 result = gnome_vfs_open (&to_handle, uri_local, GNOME_VFS_OPEN_WRITE);
634 if (result != GNOME_VFS_OK) {
635 g_warning("file creating: %s", gnome_vfs_result_to_string(result));
636 return false;
637 }
639 while (1) {
641 result = gnome_vfs_read (from_handle, buffer, 8192, &bytes_read);
643 if ((result == GNOME_VFS_ERROR_EOF) &&(!bytes_read)){
644 result = gnome_vfs_close (from_handle);
645 result = gnome_vfs_close (to_handle);
646 return true;
647 }
649 if (result != GNOME_VFS_OK) {
650 g_warning("%s", gnome_vfs_result_to_string(result));
651 return false;
652 }
653 result = gnome_vfs_write (to_handle, buffer, bytes_read, &bytes_written);
654 if (result != GNOME_VFS_OK) {
655 g_warning("%s", gnome_vfs_result_to_string(result));
656 return false;
657 }
660 if (bytes_read != bytes_written){
661 return false;
662 }
664 }
665 return true;
666 #else
667 // in case we do not have GNOME_VFS
668 return false;
669 #endif
671 }
674 /**
675 * Display a SaveAs dialog. Save the document if OK pressed.
676 *
677 * \param ascopy (optional) wether to set the documents->uri to the new filename or not
678 */
679 bool
680 sp_file_save_dialog(Gtk::Window &parentWindow, SPDocument *doc, bool is_copy)
681 {
683 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
685 Inkscape::Extension::Output *extension = 0;
687 //# Get the default extension name
688 Glib::ustring default_extension;
689 char *attr = (char *)repr->attribute("inkscape:output_extension");
690 if (!attr)
691 attr = (char *)prefs_get_string_attribute("dialogs.save_as", "default");
692 if (attr)
693 default_extension = attr;
694 //g_message("%s: extension name: '%s'", __FUNCTION__, default_extension);
696 Glib::ustring save_path;
697 Glib::ustring save_loc;
699 if (doc->uri == NULL) {
700 char formatBuf[256];
701 int i = 1;
703 Glib::ustring filename_extension = ".svg";
704 extension = dynamic_cast<Inkscape::Extension::Output *>
705 (Inkscape::Extension::db.get(default_extension.c_str()));
706 //g_warning("%s: extension ptr: 0x%x", __FUNCTION__, (unsigned int)extension);
707 if (extension)
708 filename_extension = extension->get_extension();
710 attr = (char *)prefs_get_string_attribute("dialogs.save_as", "path");
711 if (attr)
712 save_path = attr;
714 if (!Inkscape::IO::file_test(save_path.c_str(),
715 (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
716 save_path = "";
718 if (save_path.size()<1)
719 save_path = g_get_home_dir();
721 save_loc = save_path;
722 save_loc.append(G_DIR_SEPARATOR_S);
723 snprintf(formatBuf, 255, _("drawing%s"), filename_extension.c_str());
724 save_loc.append(formatBuf);
726 while (Inkscape::IO::file_test(save_loc.c_str(), G_FILE_TEST_EXISTS)) {
727 save_loc = save_path;
728 save_loc.append(G_DIR_SEPARATOR_S);
729 snprintf(formatBuf, 255, _("drawing-%d%s"), i++, filename_extension.c_str());
730 save_loc.append(formatBuf);
731 }
732 } else {
733 save_loc = Glib::build_filename(Glib::path_get_dirname(doc->uri),
734 Glib::path_get_basename(doc->uri));
735 }
737 // convert save_loc from utf-8 to locale
738 // is this needed any more, now that everything is handled in
739 // Inkscape::IO?
740 Glib::ustring save_loc_local = Glib::filename_from_utf8(save_loc);
742 if ( save_loc_local.size() > 0)
743 save_loc = save_loc_local;
745 //# Show the SaveAs dialog
746 char const * dialog_title;
747 if (is_copy) {
748 dialog_title = (char const *) _("Select file to save a copy to");
749 } else {
750 dialog_title = (char const *) _("Select file to save to");
751 }
752 Inkscape::UI::Dialog::FileSaveDialog *saveDialog =
753 Inkscape::UI::Dialog::FileSaveDialog::create(
754 parentWindow,
755 save_loc,
756 Inkscape::UI::Dialog::SVG_TYPES,
757 dialog_title,
758 default_extension
759 );
761 saveDialog->setSelectionType(extension);
763 bool success = saveDialog->show();
764 if (!success) {
765 delete saveDialog;
766 return success;
767 }
769 Glib::ustring fileName = saveDialog->getFilename();
770 Inkscape::Extension::Extension *selectionType = saveDialog->getSelectionType();
772 delete saveDialog;
774 saveDialog = 0;
776 if (fileName.size() > 0) {
777 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
779 if ( newFileName.size()>0 )
780 fileName = newFileName;
781 else
782 g_warning( "Error converting save filename to UTF-8." );
784 success = file_save(parentWindow, doc, fileName, selectionType, TRUE, !is_copy);
786 if (success)
787 prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
789 save_path = Glib::path_get_dirname(fileName);
790 prefs_set_string_attribute("dialogs.save_as", "path", save_path.c_str());
792 return success;
793 }
796 return false;
797 }
800 /**
801 * Save a document, displaying a SaveAs dialog if necessary.
802 */
803 bool
804 sp_file_save_document(Gtk::Window &parentWindow, SPDocument *doc)
805 {
806 bool success = true;
808 if (doc->isModifiedSinceSave()) {
809 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
810 if ( doc->uri == NULL
811 || repr->attribute("inkscape:output_extension") == NULL )
812 {
813 return sp_file_save_dialog(parentWindow, doc, FALSE);
814 } else {
815 gchar const *fn = g_strdup(doc->uri);
816 gchar const *ext = repr->attribute("inkscape:output_extension");
817 success = file_save(parentWindow, doc, fn, Inkscape::Extension::db.get(ext), FALSE, TRUE);
818 g_free((void *) fn);
819 }
820 } else {
821 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("No changes need to be saved."));
822 success = TRUE;
823 }
825 return success;
826 }
829 /**
830 * Save a document.
831 */
832 bool
833 sp_file_save(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*data*/)
834 {
835 if (!SP_ACTIVE_DOCUMENT)
836 return false;
838 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Saving document..."));
840 sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
841 return sp_file_save_document(parentWindow, SP_ACTIVE_DOCUMENT);
842 }
845 /**
846 * Save a document, always displaying the SaveAs dialog.
847 */
848 bool
849 sp_file_save_as(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*data*/)
850 {
851 if (!SP_ACTIVE_DOCUMENT)
852 return false;
853 sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
854 return sp_file_save_dialog(parentWindow, SP_ACTIVE_DOCUMENT, FALSE);
855 }
859 /**
860 * Save a copy of a document, always displaying a sort of SaveAs dialog.
861 */
862 bool
863 sp_file_save_a_copy(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*data*/)
864 {
865 if (!SP_ACTIVE_DOCUMENT)
866 return false;
867 sp_namedview_document_from_window(SP_ACTIVE_DESKTOP);
868 return sp_file_save_dialog(parentWindow, SP_ACTIVE_DOCUMENT, TRUE);
869 }
872 /*######################
873 ## I M P O R T
874 ######################*/
876 /**
877 * Import a resource. Called by sp_file_import()
878 */
879 void
880 file_import(SPDocument *in_doc, const Glib::ustring &uri,
881 Inkscape::Extension::Extension *key)
882 {
883 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
885 //DEBUG_MESSAGE( fileImport, "file_import( in_doc:%p uri:[%s], key:%p", in_doc, uri, key );
886 SPDocument *doc;
887 try {
888 doc = Inkscape::Extension::open(key, uri.c_str());
889 } catch (Inkscape::Extension::Input::no_extension_found &e) {
890 doc = NULL;
891 } catch (Inkscape::Extension::Input::open_failed &e) {
892 doc = NULL;
893 }
895 if (doc != NULL) {
896 Inkscape::IO::fixupHrefs(doc, in_doc->base, true);
897 Inkscape::XML::Document *xml_in_doc = sp_document_repr_doc(in_doc);
899 prevent_id_clashes(doc, in_doc);
901 SPObject *in_defs = SP_DOCUMENT_DEFS(in_doc);
902 Inkscape::XML::Node *last_def = SP_OBJECT_REPR(in_defs)->lastChild();
904 SPCSSAttr *style = sp_css_attr_from_object(SP_DOCUMENT_ROOT(doc));
906 // Count the number of top-level items in the imported document.
907 guint items_count = 0;
908 for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc));
909 child != NULL; child = SP_OBJECT_NEXT(child))
910 {
911 if (SP_IS_ITEM(child)) items_count++;
912 }
914 // Create a new group if necessary.
915 Inkscape::XML::Node *newgroup = NULL;
916 if ((style && style->firstChild()) || items_count > 1) {
917 newgroup = xml_in_doc->createElement("svg:g");
918 sp_repr_css_set(newgroup, style, "style");
919 }
921 // Determine the place to insert the new object.
922 // This will be the current layer, if possible.
923 // FIXME: If there's no desktop (command line run?) we need
924 // a document:: method to return the current layer.
925 // For now, we just use the root in this case.
926 SPObject *place_to_insert;
927 if (desktop) place_to_insert = desktop->currentLayer();
928 else place_to_insert = SP_DOCUMENT_ROOT(in_doc);
930 // Construct a new object representing the imported image,
931 // and insert it into the current document.
932 SPObject *new_obj = NULL;
933 for (SPObject *child = sp_object_first_child(SP_DOCUMENT_ROOT(doc));
934 child != NULL; child = SP_OBJECT_NEXT(child) )
935 {
936 if (SP_IS_ITEM(child)) {
937 Inkscape::XML::Node *newitem = SP_OBJECT_REPR(child)->duplicate(xml_in_doc);
939 // convert layers to groups, and make sure they are unlocked
940 // FIXME: add "preserve layers" mode where each layer from
941 // import is copied to the same-named layer in host
942 newitem->setAttribute("inkscape:groupmode", NULL);
943 newitem->setAttribute("sodipodi:insensitive", NULL);
945 if (newgroup) newgroup->appendChild(newitem);
946 else new_obj = place_to_insert->appendChildRepr(newitem);
947 }
949 // don't lose top-level defs or style elements
950 else if (SP_OBJECT_REPR(child)->type() == Inkscape::XML::ELEMENT_NODE) {
951 const gchar *tag = SP_OBJECT_REPR(child)->name();
952 if (!strcmp(tag, "svg:defs")) {
953 for (SPObject *x = sp_object_first_child(child);
954 x != NULL; x = SP_OBJECT_NEXT(x))
955 {
956 SP_OBJECT_REPR(in_defs)->addChild(SP_OBJECT_REPR(x)->duplicate(xml_in_doc), last_def);
957 }
958 }
959 else if (!strcmp(tag, "svg:style")) {
960 SP_DOCUMENT_ROOT(in_doc)->appendChildRepr(SP_OBJECT_REPR(child)->duplicate(xml_in_doc));
961 }
962 }
963 }
964 if (newgroup) new_obj = place_to_insert->appendChildRepr(newgroup);
966 // release some stuff
967 if (newgroup) Inkscape::GC::release(newgroup);
968 if (style) sp_repr_css_attr_unref(style);
970 // select and move the imported item
971 if (new_obj && SP_IS_ITEM(new_obj)) {
972 Inkscape::Selection *selection = sp_desktop_selection(desktop);
973 selection->set(SP_ITEM(new_obj));
975 // To move the imported object, we must temporarily set the "transform pattern with
976 // object" option.
977 {
978 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
979 prefs_set_int_attribute("options.transform", "pattern", 1);
980 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
981 boost::optional<NR::Rect> sel_bbox = selection->bounds();
982 if (sel_bbox) {
983 NR::Point m( desktop->point() - sel_bbox->midpoint() );
984 sp_selection_move_relative(selection, m);
985 }
986 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
987 }
988 }
990 sp_document_unref(doc);
991 sp_document_done(in_doc, SP_VERB_FILE_IMPORT,
992 _("Import"));
994 } else {
995 gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), uri.c_str());
996 sp_ui_error_dialog(text);
997 g_free(text);
998 }
1000 return;
1001 }
1004 static Inkscape::UI::Dialog::FileOpenDialog *importDialogInstance = NULL;
1006 /**
1007 * Display an Open dialog, import a resource if OK pressed.
1008 */
1009 void
1010 sp_file_import(Gtk::Window &parentWindow)
1011 {
1012 static Glib::ustring import_path;
1014 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1015 if (!doc)
1016 return;
1018 if (!importDialogInstance) {
1019 importDialogInstance =
1020 Inkscape::UI::Dialog::FileOpenDialog::create(
1021 parentWindow,
1022 import_path,
1023 Inkscape::UI::Dialog::IMPORT_TYPES,
1024 (char const *)_("Select file to import"));
1025 }
1027 bool success = importDialogInstance->show();
1028 if (!success)
1029 return;
1031 //# Get file name and extension type
1032 Glib::ustring fileName = importDialogInstance->getFilename();
1033 Inkscape::Extension::Extension *selection =
1034 importDialogInstance->getSelectionType();
1037 if (fileName.size() > 0) {
1039 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
1041 if ( newFileName.size() > 0)
1042 fileName = newFileName;
1043 else
1044 g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
1047 import_path = fileName;
1048 if (import_path.size()>0)
1049 import_path.append(G_DIR_SEPARATOR_S);
1051 file_import(doc, fileName, selection);
1052 }
1054 return;
1055 }
1059 /*######################
1060 ## E X P O R T
1061 ######################*/
1063 //#define NEW_EXPORT_DIALOG
1067 #ifdef NEW_EXPORT_DIALOG
1069 static Inkscape::UI::Dialog::FileExportDialog *exportDialogInstance = NULL;
1071 /**
1072 * Display an Export dialog, export as the selected type if OK pressed
1073 */
1074 bool
1075 sp_file_export_dialog(void *widget)
1076 {
1077 //# temp hack for 'doc' until we can switch to this dialog
1078 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1080 Glib::ustring export_path;
1081 Glib::ustring export_loc;
1083 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
1085 Inkscape::Extension::Output *extension;
1087 //# Get the default extension name
1088 Glib::ustring default_extension;
1089 char *attr = (char *)repr->attribute("inkscape:output_extension");
1090 if (!attr)
1091 attr = (char *)prefs_get_string_attribute("dialogs.save_as", "default");
1092 if (attr)
1093 default_extension = attr;
1094 //g_message("%s: extension name: '%s'", __FUNCTION__, default_extension);
1096 if (doc->uri == NULL)
1097 {
1098 char formatBuf[256];
1100 Glib::ustring filename_extension = ".svg";
1101 extension = dynamic_cast<Inkscape::Extension::Output *>
1102 (Inkscape::Extension::db.get(default_extension.c_str()));
1103 //g_warning("%s: extension ptr: 0x%x", __FUNCTION__, (unsigned int)extension);
1104 if (extension)
1105 filename_extension = extension->get_extension();
1107 attr = (char *)prefs_get_string_attribute("dialogs.save_as", "path");
1108 if (attr)
1109 export_path = attr;
1111 if (!Inkscape::IO::file_test(export_path.c_str(),
1112 (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)))
1113 export_path = "";
1115 if (export_path.size()<1)
1116 export_path = g_get_home_dir();
1118 export_loc = export_path;
1119 export_loc.append(G_DIR_SEPARATOR_S);
1120 snprintf(formatBuf, 255, _("drawing%s"), filename_extension.c_str());
1121 export_loc.append(formatBuf);
1123 }
1124 else
1125 {
1126 export_path = Glib::path_get_dirname(doc->uri);
1127 }
1129 // convert save_loc from utf-8 to locale
1130 // is this needed any more, now that everything is handled in
1131 // Inkscape::IO?
1132 Glib::ustring export_path_local = Glib::filename_from_utf8(export_path);
1133 if ( export_path_local.size() > 0)
1134 export_path = export_path_local;
1136 //# Show the SaveAs dialog
1137 if (!exportDialogInstance)
1138 exportDialogInstance =
1139 Inkscape::UI::Dialog::FileExportDialog::create(
1140 export_path,
1141 Inkscape::UI::Dialog::EXPORT_TYPES,
1142 (char const *) _("Select file to export to"),
1143 default_extension
1144 );
1146 bool success = exportDialogInstance->show();
1147 if (!success)
1148 return success;
1150 Glib::ustring fileName = exportDialogInstance->getFilename();
1152 Inkscape::Extension::Extension *selectionType =
1153 exportDialogInstance->getSelectionType();
1156 if (fileName.size() > 0) {
1157 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
1159 if ( newFileName.size()>0 )
1160 fileName = newFileName;
1161 else
1162 g_warning( "Error converting save filename to UTF-8." );
1164 success = file_save(doc, fileName, selectionType, TRUE, FALSE);
1166 if (success)
1167 prefs_set_recent_file(SP_DOCUMENT_URI(doc), SP_DOCUMENT_NAME(doc));
1169 export_path = fileName;
1170 prefs_set_string_attribute("dialogs.save_as", "path", export_path.c_str());
1172 return success;
1173 }
1176 return false;
1177 }
1179 #else
1181 /**
1182 *
1183 */
1184 bool
1185 sp_file_export_dialog(void */*widget*/)
1186 {
1187 sp_export_dialog();
1188 return true;
1189 }
1191 #endif
1193 /*######################
1194 ## E X P O R T T O O C A L
1195 ######################*/
1197 /**
1198 * Display an Export dialog, export as the selected type if OK pressed
1199 */
1200 bool
1201 sp_file_export_to_ocal_dialog(Gtk::Window &parentWindow)
1202 {
1204 if (!SP_ACTIVE_DOCUMENT)
1205 return false;
1207 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1209 Glib::ustring export_path;
1210 Glib::ustring export_loc;
1211 Glib::ustring fileName;
1212 Inkscape::Extension::Extension *selectionType;
1214 bool success = false;
1216 static Inkscape::UI::Dialog::FileExportToOCALDialog *exportDialogInstance = NULL;
1217 static Inkscape::UI::Dialog::FileExportToOCALPasswordDialog *exportPasswordDialogInstance = NULL;
1218 static bool gotSuccess = false;
1220 Inkscape::XML::Node *repr = sp_document_repr_root(doc);
1221 (void)repr;
1223 if (!doc->uri && !doc->isModifiedSinceSave())
1224 return false;
1226 // Get the default extension name
1227 Glib::ustring default_extension = "org.inkscape.output.svg.inkscape";
1228 char formatBuf[256];
1230 Glib::ustring filename_extension = ".svg";
1231 selectionType = Inkscape::Extension::db.get(default_extension.c_str());
1233 export_path = Glib::get_tmp_dir ();
1235 export_loc = export_path;
1236 export_loc.append(G_DIR_SEPARATOR_S);
1237 snprintf(formatBuf, 255, _("drawing%s"), filename_extension.c_str());
1238 export_loc.append(formatBuf);
1240 // convert save_loc from utf-8 to locale
1241 // is this needed any more, now that everything is handled in
1242 // Inkscape::IO?
1243 Glib::ustring export_path_local = Glib::filename_from_utf8(export_path);
1244 if ( export_path_local.size() > 0)
1245 export_path = export_path_local;
1247 // Show the Export To OCAL dialog
1248 if (!exportDialogInstance)
1249 exportDialogInstance = new Inkscape::UI::Dialog::FileExportToOCALDialog(
1250 parentWindow,
1251 Inkscape::UI::Dialog::EXPORT_TYPES,
1252 (char const *) _("Select file to export to")
1253 );
1255 success = exportDialogInstance->show();
1256 if (!success)
1257 return success;
1259 fileName = exportDialogInstance->getFilename();
1261 fileName.append(filename_extension.c_str());
1262 if (fileName.size() > 0) {
1263 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
1265 if ( newFileName.size()>0 )
1266 fileName = newFileName;
1267 else
1268 g_warning( "Error converting save filename to UTF-8." );
1269 }
1270 Glib::ustring filePath = export_path;
1271 filePath.append(G_DIR_SEPARATOR_S);
1272 filePath.append(Glib::path_get_basename(fileName));
1274 fileName = filePath;
1276 success = file_save(parentWindow, doc, filePath, selectionType, FALSE, FALSE);
1278 if (!success){
1279 gchar *text = g_strdup_printf(_("Error saving a temporary copy"));
1280 sp_ui_error_dialog(text);
1282 return success;
1283 }
1285 // Start now the submition
1287 // Create the uri
1288 Glib::ustring uri = "dav://";
1289 char *username = (char *)prefs_get_string_attribute("options.ocalusername", "str");
1290 char *password = (char *)prefs_get_string_attribute("options.ocalpassword", "str");
1291 if ((username == NULL) || (!strcmp(username, "")) || (password == NULL) || (!strcmp(password, "")))
1292 {
1293 if(!gotSuccess)
1294 {
1295 if (!exportPasswordDialogInstance)
1296 exportPasswordDialogInstance = new Inkscape::UI::Dialog::FileExportToOCALPasswordDialog(
1297 parentWindow,
1298 (char const *) _("Open Clip Art Login"));
1299 success = exportPasswordDialogInstance->show();
1300 if (!success)
1301 return success;
1302 }
1303 username = (char *)exportPasswordDialogInstance->getUsername().c_str();
1304 password = (char *)exportPasswordDialogInstance->getPassword().c_str();
1305 }
1306 uri.append(username);
1307 uri.append(":");
1308 uri.append(password);
1309 uri.append("@");
1310 uri.append(prefs_get_string_attribute("options.ocalurl", "str"));
1311 uri.append("/dav.php/");
1312 uri.append(Glib::path_get_basename(fileName));
1314 // Save as a remote file using the dav protocol.
1315 success = file_save_remote(doc, uri, selectionType, FALSE, FALSE);
1316 remove(fileName.c_str());
1317 if (!success)
1318 {
1319 gchar *text = g_strdup_printf(_("Error exporting the document. Verify if the server name, username and password are correct, if the server has support for webdav and verify if you didn't forget to choose a license."));
1320 sp_ui_error_dialog(text);
1321 }
1322 else
1323 gotSuccess = true;
1325 return success;
1326 }
1328 /**
1329 * Export the current document to OCAL
1330 */
1331 void
1332 sp_file_export_to_ocal(Gtk::Window &parentWindow)
1333 {
1335 // Try to execute the new code and return;
1336 if (!SP_ACTIVE_DOCUMENT)
1337 return;
1338 bool success = sp_file_export_to_ocal_dialog(parentWindow);
1339 if (success)
1340 SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Document exported..."));
1341 }
1344 /*######################
1345 ## I M P O R T F R O M O C A L
1346 ######################*/
1348 /**
1349 * Display an ImportToOcal Dialog, and the selected document from OCAL
1350 */
1351 void
1352 sp_file_import_from_ocal(Gtk::Window &parentWindow)
1353 {
1354 static Glib::ustring import_path;
1356 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1357 if (!doc)
1358 return;
1360 static Inkscape::UI::Dialog::FileImportFromOCALDialog *importDialogInstance = NULL;
1362 if (!importDialogInstance) {
1363 importDialogInstance = new
1364 Inkscape::UI::Dialog::FileImportFromOCALDialog(
1365 parentWindow,
1366 import_path,
1367 Inkscape::UI::Dialog::IMPORT_TYPES,
1368 (char const *)_("Import From Open Clip Art Library"));
1369 }
1371 bool success = importDialogInstance->show();
1372 if (!success)
1373 return;
1375 // Get file name and extension type
1376 Glib::ustring fileName = importDialogInstance->getFilename();
1377 Inkscape::Extension::Extension *selection =
1378 importDialogInstance->getSelectionType();
1380 if (fileName.size() > 0) {
1382 Glib::ustring newFileName = Glib::filename_to_utf8(fileName);
1384 if ( newFileName.size() > 0)
1385 fileName = newFileName;
1386 else
1387 g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" );
1389 import_path = fileName;
1390 if (import_path.size()>0)
1391 import_path.append(G_DIR_SEPARATOR_S);
1393 file_import(doc, fileName, selection);
1394 }
1396 return;
1397 }
1399 /*######################
1400 ## P R I N T
1401 ######################*/
1404 /**
1405 * Print the current document, if any.
1406 */
1407 void
1408 sp_file_print(Gtk::Window& parentWindow)
1409 {
1410 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1411 if (doc)
1412 sp_print_document(parentWindow, doc);
1413 }
1415 /**
1416 * Display what the drawing would look like, if
1417 * printed.
1418 */
1419 void
1420 sp_file_print_preview(gpointer /*object*/, gpointer /*data*/)
1421 {
1423 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1424 if (doc)
1425 sp_print_preview_document(doc);
1427 }
1429 void Inkscape::IO::fixupHrefs( SPDocument *doc, const gchar *base, gboolean spns )
1430 {
1431 //g_message("Inkscape::IO::fixupHrefs( , [%s], )", base );
1433 if ( 0 ) {
1434 gchar const* things[] = {
1435 "data:foo,bar",
1436 "http://www.google.com/image.png",
1437 "ftp://ssd.com/doo",
1438 "/foo/dee/bar.svg",
1439 "foo.svg",
1440 "file:/foo/dee/bar.svg",
1441 "file:///foo/dee/bar.svg",
1442 "file:foo.svg",
1443 "/foo/bar\xe1\x84\x92.svg",
1444 "file:///foo/bar\xe1\x84\x92.svg",
1445 "file:///foo/bar%e1%84%92.svg",
1446 "/foo/bar%e1%84%92.svg",
1447 "bar\xe1\x84\x92.svg",
1448 "bar%e1%84%92.svg",
1449 NULL
1450 };
1451 g_message("+------");
1452 for ( int i = 0; things[i]; i++ )
1453 {
1454 try
1455 {
1456 URI uri(things[i]);
1457 gboolean isAbs = g_path_is_absolute( things[i] );
1458 gchar *str = uri.toString();
1459 g_message( "abs:%d isRel:%d scheme:[%s] path:[%s][%s] uri[%s] / [%s]", (int)isAbs,
1460 (int)uri.isRelative(),
1461 uri.getScheme(),
1462 uri.getPath(),
1463 uri.getOpaque(),
1464 things[i],
1465 str );
1466 g_free(str);
1467 }
1468 catch ( MalformedURIException err )
1469 {
1470 dump_str( things[i], "MalformedURIException" );
1471 xmlChar *redo = xmlURIEscape((xmlChar const *)things[i]);
1472 g_message(" gone from [%s] to [%s]", things[i], redo );
1473 if ( redo == NULL )
1474 {
1475 URI again = URI::fromUtf8( things[i] );
1476 gboolean isAbs = g_path_is_absolute( things[i] );
1477 gchar *str = again.toString();
1478 g_message( "abs:%d isRel:%d scheme:[%s] path:[%s][%s] uri[%s] / [%s]", (int)isAbs,
1479 (int)again.isRelative(),
1480 again.getScheme(),
1481 again.getPath(),
1482 again.getOpaque(),
1483 things[i],
1484 str );
1485 g_free(str);
1486 g_message(" ----");
1487 }
1488 }
1489 }
1490 g_message("+------");
1491 }
1493 GSList const *images = sp_document_get_resource_list(doc, "image");
1494 for (GSList const *l = images; l != NULL; l = l->next) {
1495 Inkscape::XML::Node *ir = SP_OBJECT_REPR(l->data);
1497 const gchar *href = ir->attribute("xlink:href");
1499 // First try to figure out an absolute path to the asset
1500 //g_message("image href [%s]", href );
1501 if (spns && !g_path_is_absolute(href)) {
1502 const gchar *absref = ir->attribute("sodipodi:absref");
1503 const gchar *base_href = g_build_filename(base, href, NULL);
1504 //g_message(" absr [%s]", absref );
1506 if ( absref && Inkscape::IO::file_test(absref, G_FILE_TEST_EXISTS) && !Inkscape::IO::file_test(base_href, G_FILE_TEST_EXISTS))
1507 {
1508 // only switch over if the absref is valid while href is not
1509 href = absref;
1510 //g_message(" copied absref to href");
1511 }
1512 }
1514 // Once we have an absolute path, convert it relative to the new location
1515 if (href && g_path_is_absolute(href)) {
1516 const gchar *relname = sp_relative_path_from_path(href, base);
1517 //g_message(" setting to [%s]", relname );
1518 ir->setAttribute("xlink:href", relname);
1519 }
1520 // TODO next refinement is to make the first choice keeping the relative path as-is if
1521 // based on the new location it gives us a valid file.
1522 }
1523 }
1526 /*
1527 Local Variables:
1528 mode:c++
1529 c-file-style:"stroustrup"
1530 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1531 indent-tabs-mode:nil
1532 fill-column:99
1533 End:
1534 */
1535 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :