/** * @file * File/Print operations. */ /* Authors: * Lauris Kaplinski * Chema Celorio * bulia byak * Bruno Dilly * Stephen Silver * Jon A. Cruz * Abhishek Sharma * David Xiong * Tavmjong Bah * * Copyright (C) 2006 Johan Engelen * Copyright (C) 1999-2016 Authors * Copyright (C) 2004 David Turner * Copyright (C) 2001-2002 Ximian, Inc. * * Released under GNU GPL, read the file 'COPYING' for more information */ /** @file * @note This file needs to be cleaned up extensively. * What it probably needs is to have one .h file for * the API, and two or more .cpp files for the implementations. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include "ui/dialog/ocaldialogs.h" #include "desktop.h" #include "extension/effect.h" #include "document-private.h" #include "document-undo.h" #include "ui/tools/tool-base.h" #include "extension/db.h" #include "extension/input.h" #include "extension/output.h" #include "file.h" #include "helper/png-write.h" #include "id-clash.h" #include "inkscape.h" #include "inkscape-version.h" #include "ui/interface.h" #include "io/sys.h" #include "message-stack.h" #include "path-prefix.h" #include "print.h" #include "resource-manager.h" #include "rdf.h" #include "selection-chemistry.h" #include "sp-namedview.h" #include "style.h" #include "ui/view/view-widget.h" #include "xml/rebase-hrefs.h" #include "xml/sp-css-attr.h" #include "verbs.h" #include "event-log.h" #include "ui/dialog/font-substitution.h" // For updating old Inkscape SVG files #include "display/canvas-grid.h" #include "sp-guide.h" #include "selection-chemistry.h" #include "persp3d.h" #include "proj_pt.h" #include "ui/shape-editor.h" using Inkscape::DocumentUndo; #ifdef WITH_GNOME_VFS # include #endif #ifdef WITH_DBUS #include "extension/dbus/dbus-init.h" #endif #ifdef WIN32 #include #endif //#define INK_DUMP_FILENAME_CONV 1 #undef INK_DUMP_FILENAME_CONV //#define INK_DUMP_FOPEN 1 #undef INK_DUMP_FOPEN void dump_str(gchar const *str, gchar const *prefix); void dump_ustr(Glib::ustring const &ustr); // what gets passed here is not actually an URI... it is an UTF-8 encoded filename (!) static void sp_file_add_recent(gchar const *uri) { if(uri == NULL) { g_warning("sp_file_add_recent: uri == NULL"); return; } GtkRecentManager *recent = gtk_recent_manager_get_default(); gchar *fn = g_filename_from_utf8(uri, -1, NULL, NULL, NULL); if (fn) { gchar *uri_to_add = g_filename_to_uri(fn, NULL, NULL); if (uri_to_add) { gtk_recent_manager_add_item(recent, uri_to_add); g_free(uri_to_add); } g_free(fn); } } /*###################### ## N E W ######################*/ /** * Create a blank document and add it to the desktop */ SPDesktop *sp_file_new(const std::string &templ) { SPDocument *doc = SPDocument::createNewDoc( !templ.empty() ? templ.c_str() : 0 , TRUE, true ); g_return_val_if_fail(doc != NULL, NULL); // Remove all the template info from xml tree Inkscape::XML::Node *myRoot = doc->getReprRoot(); Inkscape::XML::Node *nodeToRemove = sp_repr_lookup_name(myRoot, "inkscape:_templateinfo"); if (nodeToRemove != NULL){ DocumentUndo::setUndoSensitive(doc, false); sp_repr_unparent(nodeToRemove); delete nodeToRemove; DocumentUndo::setUndoSensitive(doc, true); } SPDesktop *olddesktop = SP_ACTIVE_DESKTOP; if (olddesktop) olddesktop->setWaitingCursor(); SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL)); // TODO this will trigger broken link warnings, etc. g_return_val_if_fail(dtw != NULL, NULL); sp_create_window(dtw, TRUE); SPDesktop* desktop = static_cast(dtw->view); doc->doUnref(); sp_namedview_window_from_document(desktop); sp_namedview_update_layers_from_document(desktop); #ifdef WITH_DBUS Inkscape::Extension::Dbus::dbus_init_desktop_interface(desktop); #endif if (olddesktop) olddesktop->clearWaitingCursor(); if (desktop) desktop->clearWaitingCursor(); return desktop; } Glib::ustring sp_file_default_template_uri() { std::list sources; sources.push_back( Inkscape::Application::profile_path("templates") ); // first try user's local dir sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir std::list baseNames; gchar const* localized = _("default.svg"); if (strcmp("default.svg", localized) != 0) { baseNames.push_back(localized); } baseNames.push_back("default.svg"); gchar *foundTemplate = 0; for (std::list::iterator it = sources.begin(); (it != sources.end()) && !foundTemplate; ++it) { for (std::list::iterator nameIt = baseNames.begin(); (nameIt != baseNames.end()) && !foundTemplate; ++nameIt) { gchar *dirname = *it; if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) { // TRANSLATORS: default.svg is localizable - this is the name of the default document // template. This way you can localize the default pagesize, translate the name of // the default layer, etc. If you wish to localize this file, please create a // localized share/templates/default.xx.svg file, where xx is your language code. char *tmp = g_build_filename(dirname, *nameIt, NULL); if (Inkscape::IO::file_test(tmp, G_FILE_TEST_IS_REGULAR)) { foundTemplate = tmp; } else { g_free(tmp); } } } } for (std::list::iterator it = sources.begin(); it != sources.end(); ++it) { g_free(*it); } Glib::ustring templateUri = foundTemplate ? foundTemplate : ""; if (foundTemplate) { g_free(foundTemplate); foundTemplate = 0; } return templateUri; } SPDesktop* sp_file_new_default() { Glib::ustring templateUri = sp_file_default_template_uri(); SPDesktop* desk = sp_file_new(sp_file_default_template_uri()); //rdf_add_from_preferences( SP_ACTIVE_DOCUMENT ); return desk; } /*###################### ## D E L E T E ######################*/ /** * Perform document closures preceding an exit() */ void sp_file_exit() { if (SP_ACTIVE_DESKTOP == NULL) { // We must be in console mode Gtk::Main::quit(); } else { sp_ui_close_all(); // no need to call inkscape_exit here; last document being closed will take care of that } } // Quick and dirty internal backup function bool sp_file_save_backup( Glib::ustring uri ) { Glib::ustring out = uri; out.insert(out.find(".svg"),"_backup"); FILE *filein = Inkscape::IO::fopen_utf8name(uri.c_str(), "rb"); if (!filein) { std::cerr << "sp_file_save_backup: failed to open: " << uri << std::endl; return false; } FILE *fileout = Inkscape::IO::fopen_utf8name(out.c_str(), "wb"); if (!fileout) { std::cerr << "sp_file_save_backup: failed to open: " << out << std::endl; fclose( filein ); return false; } int ch; while ((ch = fgetc(filein)) != EOF) { fputc(ch, fileout); } fflush(fileout); bool return_value = true; if (ferror(fileout)) { std::cerr << "sp_file_save_backup: error when writing to: " << out << std::endl; return_value = false; } fclose(filein); fclose(fileout); return return_value; } /*###################### ## O P E N ######################*/ /** * Open a file, add the document to the desktop * * \param replace_empty if true, and the current desktop is empty, this document * will replace the empty one. */ bool sp_file_open(const Glib::ustring &uri, Inkscape::Extension::Extension *key, bool add_to_recent, bool replace_empty) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; if (desktop) { desktop->setWaitingCursor(); } SPDocument *doc = NULL; bool cancelled = false; try { doc = Inkscape::Extension::open(key, uri.c_str()); } catch (Inkscape::Extension::Input::no_extension_found &e) { doc = NULL; } catch (Inkscape::Extension::Input::open_failed &e) { doc = NULL; } catch (Inkscape::Extension::Input::open_cancelled &e) { doc = NULL; cancelled = true; } if (desktop) { desktop->clearWaitingCursor(); } if (doc) { SPDocument *existing = desktop ? desktop->getDocument() : NULL; if (existing && existing->virgin && replace_empty) { // If the current desktop is empty, open the document there doc->ensureUpToDate(); // TODO this will trigger broken link warnings, etc. desktop->change_document(doc); doc->emitResizedSignal(doc->getWidth().value("px"), doc->getHeight().value("px")); } else { // create a whole new desktop and window SPViewWidget *dtw = sp_desktop_widget_new(sp_document_namedview(doc, NULL)); // TODO this will trigger broken link warnings, etc. sp_create_window(dtw, TRUE); desktop = static_cast(dtw->view); } doc->virgin = FALSE; // everyone who cares now has a reference, get rid of our`s doc->doUnref(); SPRoot *root = doc->getRoot(); // This is the only place original values should be set. root->original.inkscape = root->version.inkscape; root->original.svg = root->version.svg; if (INKSCAPE.use_gui()) { // See if we need to offer the user a fix for the 90->96 px per inch change. // std::cout << "SPFileOpen:" << std::endl; // std::cout << " Version: " << sp_version_to_string(root->version.inkscape) << std::endl; if ( sp_version_inside_range( root->version.inkscape, 0, 1, 0, 92 ) ) { // std::cout << " SVG file from old Inkscape version detected: " // << sp_version_to_string(root->version.inkscape) << std::endl; static const double ratio = 90.0/96.0; bool need_fix_viewbox = false; bool need_fix_units = false; bool need_fix_guides = false; bool need_fix_grid_mm = false; bool need_fix_box3d = false; bool did_scaling = false; // Check if potentially need viewbox or unit fix switch (root->width.unit) { case SP_CSS_UNIT_PC: case SP_CSS_UNIT_PT: case SP_CSS_UNIT_MM: case SP_CSS_UNIT_CM: case SP_CSS_UNIT_IN: need_fix_viewbox = true; break; case SP_CSS_UNIT_NONE: case SP_CSS_UNIT_PX: need_fix_units = true; break; case SP_CSS_UNIT_EM: case SP_CSS_UNIT_EX: case SP_CSS_UNIT_PERCENT: // OK break; default: std::cerr << "sp_file_open: Unhandled width unit!" << std::endl; } switch (root->height.unit) { case SP_CSS_UNIT_PC: case SP_CSS_UNIT_PT: case SP_CSS_UNIT_MM: case SP_CSS_UNIT_CM: case SP_CSS_UNIT_IN: need_fix_viewbox = true; break; case SP_CSS_UNIT_NONE: case SP_CSS_UNIT_PX: need_fix_units = true; break; case SP_CSS_UNIT_EM: case SP_CSS_UNIT_EX: case SP_CSS_UNIT_PERCENT: // OK break; default: std::cerr << "sp_file_open: Unhandled height unit!" << std::endl; } // std::cout << "Absolute SVG units in root? " << (need_fix_viewbox?"true":"false") << std::endl; // std::cout << "User units in root? " << (need_fix_units ?"true":"false") << std::endl; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if (!root->viewBox_set && need_fix_viewbox) { Glib::ustring msg = _( "Old Inkscape files use 1in == 90px. CSS requires 1in == 96px.\n" "Drawing elements may be too small. This can be corrected by\n" "either setting the SVG 'viewBox' to compensate or by scaling\n" "all the elements in the drawing."); Gtk::Dialog scaleDialog( _("Old Inkscape file detected (90 DPI)"), false); Gtk::Label info; info.set_markup(msg.c_str()); info.show(); scaleDialog.get_content_area()->pack_start(info, false, false, 20); Gtk::CheckButton backupButton( _("Create backup file (in same directory).") ); bool backup = prefs->getBool("/options/dpifixbackup", true); backupButton.set_active( backup ); backupButton.show(); scaleDialog.get_content_area()->pack_start(backupButton, false, false, 20); scaleDialog.add_button(_("Set 'viewBox'"), 1); scaleDialog.add_button(_("Scale elements"), 2); scaleDialog.add_button(_("Ignore"), 3); scaleDialog.add_button("Scale test - group", 4); scaleDialog.add_button("Scale test - children", 5); scaleDialog.add_button("Scale test - all", 6); gint response = scaleDialog.run(); backup = backupButton.get_active(); prefs->setBool("/options/dpifixbackup", backup); if ( backup && response != 3) { sp_file_save_backup( uri ); } if (response == 1) { doc->setViewBox(Geom::Rect::from_xywh( 0, 0, doc->getWidth().value("px") * ratio, doc->getHeight().value("px") * ratio)); } else if (response == 2 ) { std::list effects; Inkscape::Extension::db.get_effect_list(effects); std::list::iterator it = effects.begin(); bool did = false; while (it != effects.end()) { if (strcmp((*it)->get_id(), "org.inkscape.dpi90to96") == 0) { Inkscape::UI::View::View *view = desktop; (*it)->effect(view); did = true; break; } ++it; } if (!did) { std::cerr << "sp_file_open: Failed to find dpi90to96 extension." << std::endl; } did_scaling = true; } else if (response == 4) { // Save preferences bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive",true); bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true); prefs->setBool("/options/kbselection/onlysensitive", false); prefs->setBool("/options/kbselection/onlyvisible", false); Inkscape::Selection *selection = desktop->getSelection(); Inkscape::SelectionHelper::selectAllInAll( desktop ); selection->group(); SPItem * group = selection->singleItem(); if (group) { group->setAttribute("transform","scale(1.06666667,1.06666667)"); } else { std::cerr << "sp_file_open: Failed to get group!" << std::endl; } selection->clear(); selection->add( group ); selection->ungroup(); selection->clear(); prefs->setBool("/options/kbselection/onlysensitive", onlysensitive); prefs->setBool("/options/kbselection/onlyvisible", onlyvisible ); did_scaling = true; } else if (response == 5) { // Save preferences bool transform_stroke = prefs->getBool("/options/transform/stroke", true); bool transform_rectcorners = prefs->getBool("/options/transform/rectcorners", true); bool transform_pattern = prefs->getBool("/options/transform/pattern", true); bool transform_gradient = prefs->getBool("/options/transform/gradient", true); prefs->setBool("/options/transform/stroke", true); prefs->setBool("/options/transform/rectcorners", true); prefs->setBool("/options/transform/pattern", true); prefs->setBool("/options/transform/gradient", true); Inkscape::UI::ShapeEditor::blockSetItem(true); doc->getRoot()->scaleChildItemsRec(Geom::Scale(1/ratio),Geom::Point(0, 0), false); Inkscape::UI::ShapeEditor::blockSetItem(false); // Restore preferences prefs->setBool("/options/transform/stroke", transform_stroke); prefs->setBool("/options/transform/rectcorners", transform_rectcorners); prefs->setBool("/options/transform/pattern", transform_pattern); prefs->setBool("/options/transform/gradient", transform_gradient); } else if (response == 6) { // Save preferences bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive",true); bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true); prefs->setBool("/options/kbselection/onlysensitive", false); prefs->setBool("/options/kbselection/onlyvisible", false); Inkscape::Selection *selection = desktop->getSelection(); Inkscape::SelectionHelper::selectAllInAll( desktop ); double height = root->height.computed; selection->setScaleRelative( Geom::Point(0,height), Geom::Scale(96.0/90.0,96.0/90.0) ); selection->clear(); prefs->setBool("/options/kbselection/onlysensitive", onlysensitive); prefs->setBool("/options/kbselection/onlyvisible", onlyvisible ); did_scaling = true; } need_fix_box3d = false; // setScaleRelative() handles box3d need_fix_guides = true; // Always fix guides } else if (need_fix_units) { Glib::ustring msg = _( "Old Inkscape files use 1in == 90px. CSS requires 1in == 96px.\n" "Drawings meant to match a physical size (e.g. Letter or A4)\n" "will be too small. Scaling the drawing can correct for this.\n" "Internal scaling can be handled either by setting the SVG 'viewBox'\n" "attribute to compensate or by scaling all objects in the drawing."); Gtk::Dialog scaleDialog( _("Old Inkscape file detected (90 DPI)"), false); Gtk::Label info; info.set_markup(msg.c_str()); info.show(); scaleDialog.get_content_area()->pack_start(info, false, false, 20); Gtk::CheckButton backupButton( _("Create backup file (in same directory).") ); bool backup = prefs->getBool("/options/dpifixbackup", true); backupButton.set_active( backup ); backupButton.show(); scaleDialog.get_content_area()->pack_start(backupButton, false, false, 20); scaleDialog.add_button(_("Set 'viewBox'"), 1); scaleDialog.add_button(_("Scale elements"), 2); scaleDialog.add_button(_("Ignore"), 3); scaleDialog.add_button("Scale test - group", 4); scaleDialog.add_button("Scale test - children", 5); scaleDialog.add_button("Scale test - all", 6); gint response = scaleDialog.run(); backup = backupButton.get_active(); prefs->setBool("/options/dpifixbackup", backup); if ( backup && response != 3) { sp_file_save_backup( uri ); } if (response == 1) { if (!root->viewBox_set) { doc->setViewBox(Geom::Rect::from_xywh( 0, 0, doc->getWidth().value("px"), doc->getHeight().value("px"))); } Inkscape::Util::Quantity width = Inkscape::Util::Quantity(doc->getWidth().value("px")/ratio, "px" ); Inkscape::Util::Quantity height = Inkscape::Util::Quantity(doc->getHeight().value("px")/ratio,"px" ); doc->setWidthAndHeight( width, height, false ); need_fix_guides = true; // Only fix guides if drawing scaled need_fix_box3d = true; } else if (response == 2) { std::list effects; Inkscape::Extension::db.get_effect_list(effects); std::list::iterator it = effects.begin(); bool did = false; while (it != effects.end()){ if (strcmp((*it)->get_id(), "org.inkscape.dpi90to96") == 0) { Inkscape::UI::View::View *view = desktop; (*it)->effect(view); did = true; break; } ++it; } if (!did) { std::cerr << "sp_file_open: Failed to find dpi90to96 extension." << std::endl; } need_fix_guides = true; // Only fix guides if drawing scaled did_scaling = true; } else if (response == 4) { Inkscape::Util::Quantity width = Inkscape::Util::Quantity(doc->getWidth().value("px")/ratio, "px" ); Inkscape::Util::Quantity height = Inkscape::Util::Quantity(doc->getHeight().value("px")/ratio,"px" ); doc->setWidthAndHeight( width, height, false ); if (!root->viewBox_set) { // Save preferences Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive",true); bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true); prefs->setBool("/options/kbselection/onlysensitive", false); prefs->setBool("/options/kbselection/onlyvisible", false); Inkscape::Selection *selection = desktop->getSelection(); Inkscape::SelectionHelper::selectAllInAll( desktop ); selection->group(); SPItem * group = selection->singleItem(); if (group) { group->setAttribute("transform","scale(1.06666667,1.06666667)"); } else { std::cerr << "sp_file_open: Failed to get group!" << std::endl; } selection->clear(); selection->add( group ); selection->ungroup(); selection->clear(); prefs->setBool("/options/kbselection/onlysensitive", onlysensitive); prefs->setBool("/options/kbselection/onlyvisible", onlyvisible ); did_scaling = true; } need_fix_box3d = true; need_fix_guides = true; // Only fix guides if drawing scaled } else if (response == 5) { Inkscape::Util::Quantity width = Inkscape::Util::Quantity(doc->getWidth().value("px")/ratio, "px" ); Inkscape::Util::Quantity height = Inkscape::Util::Quantity(doc->getHeight().value("px")/ratio,"px" ); doc->setWidthAndHeight( width, height, false ); if (!root->viewBox_set) { // Save preferences Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool transform_stroke = prefs->getBool("/options/transform/stroke", true); bool transform_rectcorners = prefs->getBool("/options/transform/rectcorners", true); bool transform_pattern = prefs->getBool("/options/transform/pattern", true); bool transform_gradient = prefs->getBool("/options/transform/gradient", true); prefs->setBool("/options/transform/stroke", true); prefs->setBool("/options/transform/rectcorners", true); prefs->setBool("/options/transform/pattern", true); prefs->setBool("/options/transform/gradient", true); Inkscape::UI::ShapeEditor::blockSetItem(true); doc->getRoot()->scaleChildItemsRec(Geom::Scale(1/ratio),Geom::Point(0, 0), false); Inkscape::UI::ShapeEditor::blockSetItem(false); // Restore preferences prefs->setBool("/options/transform/stroke", transform_stroke); prefs->setBool("/options/transform/rectcorners", transform_rectcorners); prefs->setBool("/options/transform/pattern", transform_pattern); prefs->setBool("/options/transform/gradient", transform_gradient); did_scaling = true; } } else if (response == 6) { double old_height = root->height.computed; Inkscape::Util::Quantity width = Inkscape::Util::Quantity(doc->getWidth().value("px")/ratio, "px" ); Inkscape::Util::Quantity height = Inkscape::Util::Quantity(doc->getHeight().value("px")/ratio,"px" ); doc->setWidthAndHeight( width, height, false ); if (!root->viewBox_set) { // Save preferences Inkscape::Preferences *prefs = Inkscape::Preferences::get(); bool onlysensitive = prefs->getBool("/options/kbselection/onlysensitive",true); bool onlyvisible = prefs->getBool("/options/kbselection/onlyvisible", true); prefs->setBool("/options/kbselection/onlysensitive", false); prefs->setBool("/options/kbselection/onlyvisible", false); Inkscape::Selection *selection = desktop->getSelection(); Inkscape::SelectionHelper::selectAllInAll( desktop ); double height = root->height.computed; // So far we have just enlarged the drawing but due to the // inverted coordinate system we must scale around the old // height position. selection->setScaleRelative( Geom::Point(0,old_height), Geom::Scale(96.0/90.0,96.0/90.0) ); selection->clear(); prefs->setBool("/options/kbselection/onlysensitive", onlysensitive); prefs->setBool("/options/kbselection/onlyvisible", onlyvisible ); did_scaling = true; } need_fix_box3d = false; // setScaleRelative() handls box3s need_fix_guides = true; // Only fix guides if drawing scaled } else { // Ignore need_fix_grid_mm = true; } } // Fix guides and grids and perspective for (SPObject *child = root->firstChild() ; child; child = child->getNext() ) { SPNamedView *nv = dynamic_cast(child); if (nv) { if (need_fix_guides) { // std::cout << "Fixing guides" << std::endl; for (SPObject *child2 = nv->firstChild() ; child2; child2 = child2->getNext() ) { SPGuide *gd = dynamic_cast(child2); if (gd) { gd->moveto( gd->getPoint() / ratio, true ); } } } for(std::vector::const_iterator it=nv->grids.begin();it!=nv->grids.end();++it ) { Inkscape::CanvasXYGrid *xy = dynamic_cast(*it); if (xy) { // std::cout << "A grid: " << xy->getSVGName() << std::endl; // std::cout << " Origin: " << xy->origin // << " Spacing: " << xy->spacing << std::endl; // std::cout << (xy->isLegacy()?" Legacy":" Not Legacy") << std::endl; Geom::Scale scale = doc->getDocumentScale(); if (xy->isLegacy()) { if (xy->isPixel()) { if (need_fix_grid_mm) { xy->Scale( Geom::Scale(1,1) ); // See note below } else { scale *= Geom::Scale(ratio,ratio); xy->Scale( scale.inverse() ); /* *** */ } } else { if (need_fix_grid_mm) { xy->Scale( Geom::Scale(ratio,ratio) ); } else { xy->Scale( scale.inverse() ); /* *** */ } } } else { if (need_fix_guides) { if(did_scaling){ xy->Scale( Geom::Scale(ratio,ratio).inverse() ); } else { // HACK: Scaling the document does not seem to cause // grids defined in document units to be updated. // This forces an update. xy->Scale( Geom::Scale(1,1) ); } } } } } } // If SPNamedView SPDefs *defs = dynamic_cast(child); if (defs && need_fix_box3d) { for (SPObject *child = defs->firstChild() ; child; child = child->getNext() ) { Persp3D* persp3d = dynamic_cast(child); if (persp3d) { std::vector tokens; const gchar* vp_x = persp3d->getAttribute("inkscape:vp_x"); const gchar* vp_y = persp3d->getAttribute("inkscape:vp_y"); const gchar* vp_z = persp3d->getAttribute("inkscape:vp_z"); const gchar* vp_o = persp3d->getAttribute("inkscape:persp3d-origin"); // std::cout << "Found Persp3d: " // << " vp_x: " << vp_x // << " vp_y: " << vp_y // << " vp_z: " << vp_z << std::endl; Proj::Pt2 pt_x (vp_x); Proj::Pt2 pt_y (vp_y); Proj::Pt2 pt_z (vp_z); Proj::Pt2 pt_o (vp_o); pt_x = pt_x * (1.0/ratio); pt_y = pt_y * (1.0/ratio); pt_z = pt_z * (1.0/ratio); pt_o = pt_o * (1.0/ratio); persp3d->setAttribute("inkscape:vp_x",pt_x.coord_string()); persp3d->setAttribute("inkscape:vp_y",pt_y.coord_string()); persp3d->setAttribute("inkscape:vp_z",pt_z.coord_string()); persp3d->setAttribute("inkscape:persp3d-origin",pt_o.coord_string()); } } } } // Look for SPNamedView and SPDefs loop // desktop->getDocument()->ensureUpToDate(); // Does not update box3d! DocumentUndo::done(desktop->getDocument(), SP_VERB_NONE, _("Update Document")); } // If old Inkscape version } // If use_gui // resize the window to match the document properties sp_namedview_window_from_document(desktop); sp_namedview_update_layers_from_document(desktop); if (add_to_recent) { sp_file_add_recent( doc->getURI() ); } if ( INKSCAPE.use_gui() ) { // Perform a fixup pass for hrefs. if ( Inkscape::ResourceManager::getManager().fixupBrokenLinks(doc) ) { Glib::ustring msg = _("Broken links have been changed to point to existing files."); desktop->showInfoDialog(msg); } // Check for font substitutions Inkscape::UI::Dialog::FontSubstitution::getInstance().checkFontSubstitutions(doc); } return TRUE; } else if (!cancelled) { gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str()); gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), safeUri); sp_ui_error_dialog(text); g_free(text); g_free(safeUri); return FALSE; } return FALSE; } /** * Handle prompting user for "do you want to revert"? Revert on "OK" */ void sp_file_revert_dialog() { SPDesktop *desktop = SP_ACTIVE_DESKTOP; g_assert(desktop != NULL); SPDocument *doc = desktop->getDocument(); g_assert(doc != NULL); Inkscape::XML::Node *repr = doc->getReprRoot(); g_assert(repr != NULL); gchar const *uri = doc->getURI(); if (!uri) { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved yet. Cannot revert.")); return; } bool do_revert = true; if (doc->isModifiedSinceSave()) { Glib::ustring tmpString = Glib::ustring::compose(_("Changes will be lost! Are you sure you want to reload document %1?"), uri); bool response = desktop->warnDialog (tmpString); if (!response) { do_revert = false; } } bool reverted; if (do_revert) { // Allow overwriting of current document. doc->virgin = TRUE; // remember current zoom and view double zoom = desktop->current_zoom(); Geom::Point c = desktop->get_display_area().midpoint(); reverted = sp_file_open(uri,NULL); if (reverted) { // restore zoom and view desktop->zoom_absolute(c[Geom::X], c[Geom::Y], zoom); } } else { reverted = false; } if (reverted) { desktop->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("Document reverted.")); } else { desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not reverted.")); } } void dump_str(gchar const *str, gchar const *prefix) { Glib::ustring tmp; tmp = prefix; tmp += " ["; size_t const total = strlen(str); for (unsigned i = 0; i < total; i++) { gchar *const tmp2 = g_strdup_printf(" %02x", (0x0ff & str[i])); tmp += tmp2; g_free(tmp2); } tmp += "]"; g_message("%s", tmp.c_str()); } void dump_ustr(Glib::ustring const &ustr) { char const *cstr = ustr.c_str(); char const *data = ustr.data(); Glib::ustring::size_type const byteLen = ustr.bytes(); Glib::ustring::size_type const dataLen = ustr.length(); Glib::ustring::size_type const cstrLen = strlen(cstr); g_message(" size: %lu\n length: %lu\n bytes: %lu\n clen: %lu", gulong(ustr.size()), gulong(dataLen), gulong(byteLen), gulong(cstrLen) ); g_message( " ASCII? %s", (ustr.is_ascii() ? "yes":"no") ); g_message( " UTF-8? %s", (ustr.validate() ? "yes":"no") ); try { Glib::ustring tmp; for (Glib::ustring::size_type i = 0; i < ustr.bytes(); i++) { tmp = " "; if (i < dataLen) { Glib::ustring::value_type val = ustr.at(i); gchar* tmp2 = g_strdup_printf( (((val & 0xff00) == 0) ? " %02x" : "%04x"), val ); tmp += tmp2; g_free( tmp2 ); } else { tmp += " "; } if (i < byteLen) { int val = (0x0ff & data[i]); gchar *tmp2 = g_strdup_printf(" %02x", val); tmp += tmp2; g_free( tmp2 ); if ( val > 32 && val < 127 ) { tmp2 = g_strdup_printf( " '%c'", (gchar)val ); tmp += tmp2; g_free( tmp2 ); } else { tmp += " . "; } } else { tmp += " "; } if ( i < cstrLen ) { int val = (0x0ff & cstr[i]); gchar* tmp2 = g_strdup_printf(" %02x", val); tmp += tmp2; g_free(tmp2); if ( val > 32 && val < 127 ) { tmp2 = g_strdup_printf(" '%c'", (gchar) val); tmp += tmp2; g_free( tmp2 ); } else { tmp += " . "; } } else { tmp += " "; } g_message( "%s", tmp.c_str() ); } } catch (...) { g_message("XXXXXXXXXXXXXXXXXX Exception" ); } g_message("---------------"); } /** * Display an file Open selector. Open a document if OK is pressed. * Can select single or multiple files for opening. */ void sp_file_open_dialog(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*data*/) { //# Get the current directory for finding files static Glib::ustring open_path; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if(open_path.empty()) { Glib::ustring attr = prefs->getString("/dialogs/open/path"); if (!attr.empty()) open_path = attr; } //# Test if the open_path directory exists if (!Inkscape::IO::file_test(open_path.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) open_path = ""; #ifdef WIN32 //# If no open path, default to our win32 documents folder if (open_path.empty()) { // The path to the My Documents folder is read from the // value "HKEY_CURRENT_USER\Software\Windows\CurrentVersion\Explorer\Shell Folders\Personal" HKEY key = NULL; if(RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 0, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) { WCHAR utf16path[_MAX_PATH]; DWORD value_type; DWORD data_size = sizeof(utf16path); if(RegQueryValueExW(key, L"Personal", NULL, &value_type, (BYTE*)utf16path, &data_size) == ERROR_SUCCESS) { g_assert(value_type == REG_SZ); gchar *utf8path = g_utf16_to_utf8( (const gunichar2*)utf16path, -1, NULL, NULL, NULL); if(utf8path) { open_path = Glib::ustring(utf8path); g_free(utf8path); } } } } #endif //# If no open path, default to our home directory if (open_path.empty()) { open_path = g_get_home_dir(); open_path.append(G_DIR_SEPARATOR_S); } //# Create a dialog Inkscape::UI::Dialog::FileOpenDialog *openDialogInstance = Inkscape::UI::Dialog::FileOpenDialog::create( parentWindow, open_path, Inkscape::UI::Dialog::SVG_TYPES, _("Select file to open")); //# Show the dialog bool const success = openDialogInstance->show(); //# Save the folder the user selected for later open_path = openDialogInstance->getCurrentDirectory(); if (!success) { delete openDialogInstance; return; } //# User selected something. Get name and type Glib::ustring fileName = openDialogInstance->getFilename(); Inkscape::Extension::Extension *selection = openDialogInstance->getSelectionType(); //# Code to check & open if multiple files. std::vector flist = openDialogInstance->getFilenames(); //# We no longer need the file dialog object - delete it delete openDialogInstance; openDialogInstance = NULL; //# Iterate through filenames if more than 1 if (flist.size() > 1) { for (unsigned int i = 0; i < flist.size(); i++) { fileName = flist[i]; Glib::ustring newFileName = Glib::filename_to_utf8(fileName); if ( newFileName.size() > 0 ) fileName = newFileName; else g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" ); #ifdef INK_DUMP_FILENAME_CONV g_message("Opening File %s\n", fileName.c_str()); #endif sp_file_open(fileName, selection); } return; } if (!fileName.empty()) { Glib::ustring newFileName = Glib::filename_to_utf8(fileName); if ( newFileName.size() > 0) fileName = newFileName; else g_warning( "ERROR CONVERTING OPEN FILENAME TO UTF-8" ); open_path = Glib::path_get_dirname (fileName); open_path.append(G_DIR_SEPARATOR_S); prefs->setString("/dialogs/open/path", open_path); sp_file_open(fileName, selection); } return; } /*###################### ## V A C U U M ######################*/ /** * Remove unreferenced defs from the defs section of the document. */ void sp_file_vacuum(SPDocument *doc) { unsigned int diff = doc->vacuumDocument(); DocumentUndo::done(doc, SP_VERB_FILE_VACUUM, _("Clean up document")); SPDesktop *dt = SP_ACTIVE_DESKTOP; if (dt != NULL) { // Show status messages when in GUI mode if (diff > 0) { dt->messageStack()->flashF(Inkscape::NORMAL_MESSAGE, ngettext("Removed %i unused definition in <defs>.", "Removed %i unused definitions in <defs>.", diff), diff); } else { dt->messageStack()->flash(Inkscape::NORMAL_MESSAGE, _("No unused definitions in <defs>.")); } } } /*###################### ## S A V E ######################*/ /** * This 'save' function called by the others below * * \param official whether to set :output_module and :modified in the * document; is true for normal save, false for temporary saves */ static bool file_save(Gtk::Window &parentWindow, SPDocument *doc, const Glib::ustring &uri, Inkscape::Extension::Extension *key, bool checkoverwrite, bool official, Inkscape::Extension::FileSaveMethod save_method) { if (!doc || uri.size()<1) //Safety check return false; Inkscape::Version save = doc->getRoot()->version.inkscape; doc->getReprRoot()->setAttribute("inkscape:version", Inkscape::version_string); try { Inkscape::Extension::save(key, doc, uri.c_str(), false, checkoverwrite, official, save_method); } catch (Inkscape::Extension::Output::no_extension_found &e) { gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str()); gchar *text = g_strdup_printf(_("No Inkscape extension found to save document (%s). This may have been caused by an unknown filename extension."), safeUri); SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved.")); sp_ui_error_dialog(text); g_free(text); g_free(safeUri); // Restore Inkscape version doc->getReprRoot()->setAttribute("inkscape:version", sp_version_to_string( save )); return false; } catch (Inkscape::Extension::Output::file_read_only &e) { gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str()); gchar *text = g_strdup_printf(_("File %s is write protected. Please remove write protection and try again."), safeUri); SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved.")); sp_ui_error_dialog(text); g_free(text); g_free(safeUri); doc->getReprRoot()->setAttribute("inkscape:version", sp_version_to_string( save )); return false; } catch (Inkscape::Extension::Output::save_failed &e) { gchar *safeUri = Inkscape::IO::sanitizeString(uri.c_str()); gchar *text = g_strdup_printf(_("File %s could not be saved."), safeUri); SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved.")); sp_ui_error_dialog(text); g_free(text); g_free(safeUri); doc->getReprRoot()->setAttribute("inkscape:version", sp_version_to_string( save )); return false; } catch (Inkscape::Extension::Output::save_cancelled &e) { SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved.")); doc->getReprRoot()->setAttribute("inkscape:version", sp_version_to_string( save )); return false; } catch (Inkscape::Extension::Output::no_overwrite &e) { return sp_file_save_dialog(parentWindow, doc, save_method); } catch (...) { SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Document not saved.")); doc->getReprRoot()->setAttribute("inkscape:version", sp_version_to_string( save )); return false; } if (SP_ACTIVE_DESKTOP) { if (! SP_ACTIVE_DESKTOP->event_log) { g_message("file_save: ->event_log == NULL. please report to bug #967416"); } if (! SP_ACTIVE_DESKTOP->messageStack()) { g_message("file_save: ->messageStack() == NULL. please report to bug #967416"); } } else { g_message("file_save: SP_ACTIVE_DESKTOP == NULL. please report to bug #967416"); } SP_ACTIVE_DESKTOP->event_log->rememberFileSave(); Glib::ustring msg; if (doc->getURI() == NULL) { msg = Glib::ustring::format(_("Document saved.")); } else { msg = Glib::ustring::format(_("Document saved."), " ", doc->getURI()); } SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::NORMAL_MESSAGE, msg.c_str()); return true; } /* * Used only for remote saving using VFS and a specific uri. Gets the file at the /tmp. */ bool file_save_remote(SPDocument */*doc*/, #ifdef WITH_GNOME_VFS const Glib::ustring &uri, #else const Glib::ustring &/*uri*/, #endif Inkscape::Extension::Extension */*key*/, bool /*saveas*/, bool /*official*/) { #ifdef WITH_GNOME_VFS #define BUF_SIZE 8192 gnome_vfs_init(); GnomeVFSHandle *from_handle = NULL; GnomeVFSHandle *to_handle = NULL; GnomeVFSFileSize bytes_read; GnomeVFSFileSize bytes_written; GnomeVFSResult result; guint8 buffer[8192]; gchar* uri_local = g_filename_from_utf8( uri.c_str(), -1, NULL, NULL, NULL); if ( uri_local == NULL ) { g_warning( "Error converting filename to locale encoding."); } // Gets the temp file name. Glib::ustring fileName = Glib::get_tmp_dir (); fileName.append(G_DIR_SEPARATOR_S); fileName.append((gnome_vfs_uri_extract_short_name(gnome_vfs_uri_new(uri_local)))); // Open the temp file to send. result = gnome_vfs_open (&from_handle, fileName.c_str(), GNOME_VFS_OPEN_READ); if (result != GNOME_VFS_OK) { g_warning("Could not find the temp saving."); return false; } gnome_vfs_create (&to_handle, uri_local, GNOME_VFS_OPEN_WRITE, FALSE, GNOME_VFS_PERM_USER_ALL); result = gnome_vfs_open (&to_handle, uri_local, GNOME_VFS_OPEN_WRITE); if (result != GNOME_VFS_OK) { g_warning("file creating: %s", gnome_vfs_result_to_string(result)); return false; } while (1) { result = gnome_vfs_read (from_handle, buffer, 8192, &bytes_read); if ((result == GNOME_VFS_ERROR_EOF) &&(!bytes_read)){ gnome_vfs_close (from_handle); gnome_vfs_close (to_handle); return true; } if (result != GNOME_VFS_OK) { g_warning("%s", gnome_vfs_result_to_string(result)); return false; } result = gnome_vfs_write (to_handle, buffer, bytes_read, &bytes_written); if (result != GNOME_VFS_OK) { g_warning("%s", gnome_vfs_result_to_string(result)); return false; } if (bytes_read != bytes_written){ return false; } } return true; #else // in case we do not have GNOME_VFS return false; #endif } /** * Check if a string ends with another string. * \todo Find a better code file to put this general purpose method */ static bool hasEnding (Glib::ustring const &fullString, Glib::ustring const &ending) { if (fullString.length() > ending.length()) { return (0 == fullString.compare (fullString.length() - ending.length(), ending.length(), ending)); } else { return false; } } /** * Display a SaveAs dialog. Save the document if OK pressed. */ bool sp_file_save_dialog(Gtk::Window &parentWindow, SPDocument *doc, Inkscape::Extension::FileSaveMethod save_method) { Inkscape::Extension::Output *extension = 0; bool is_copy = (save_method == Inkscape::Extension::FILE_SAVE_METHOD_SAVE_COPY); // Note: default_extension has the format "org.inkscape.output.svg.inkscape", whereas // filename_extension only uses ".svg" Glib::ustring default_extension; Glib::ustring filename_extension = ".svg"; default_extension= Inkscape::Extension::get_file_save_extension(save_method); //g_message("%s: extension name: '%s'", __FUNCTION__, default_extension); extension = dynamic_cast (Inkscape::Extension::db.get(default_extension.c_str())); if (extension) filename_extension = extension->get_extension(); Glib::ustring save_path = Inkscape::Extension::get_file_save_path(doc, save_method); if (!Inkscape::IO::file_test(save_path.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) save_path.clear(); if (save_path.empty()) save_path = g_get_home_dir(); Glib::ustring save_loc = save_path; save_loc.append(G_DIR_SEPARATOR_S); int i = 1; if ( !doc->getURI() ) { // We are saving for the first time; create a unique default filename save_loc = save_loc + _("drawing") + filename_extension; while (Inkscape::IO::file_test(save_loc.c_str(), G_FILE_TEST_EXISTS)) { save_loc = save_path; save_loc.append(G_DIR_SEPARATOR_S); save_loc = save_loc + Glib::ustring::compose(_("drawing-%1"), i++) + filename_extension; } } else { save_loc.append(Glib::path_get_basename(doc->getURI())); } // convert save_loc from utf-8 to locale // is this needed any more, now that everything is handled in // Inkscape::IO? Glib::ustring save_loc_local = Glib::filename_from_utf8(save_loc); if (!save_loc_local.empty()) save_loc = save_loc_local; //# Show the SaveAs dialog char const * dialog_title; if (is_copy) { dialog_title = (char const *) _("Select file to save a copy to"); } else { dialog_title = (char const *) _("Select file to save to"); } gchar* doc_title = doc->getRoot()->title(); Inkscape::UI::Dialog::FileSaveDialog *saveDialog = Inkscape::UI::Dialog::FileSaveDialog::create( parentWindow, save_loc, Inkscape::UI::Dialog::SVG_TYPES, dialog_title, default_extension, doc_title ? doc_title : "", save_method ); saveDialog->setSelectionType(extension); bool success = saveDialog->show(); if (!success) { delete saveDialog; if(doc_title) g_free(doc_title); return success; } // set new title here (call RDF to ensure metadata and title element are updated) rdf_set_work_entity(doc, rdf_find_entity("title"), saveDialog->getDocTitle().c_str()); Glib::ustring fileName = saveDialog->getFilename(); Inkscape::Extension::Extension *selectionType = saveDialog->getSelectionType(); delete saveDialog; saveDialog = 0; if(doc_title) g_free(doc_title); if (!fileName.empty()) { Glib::ustring newFileName = Glib::filename_to_utf8(fileName); if (!newFileName.empty()) fileName = newFileName; else g_warning( "Error converting filename for saving to UTF-8." ); Inkscape::Extension::Output *omod = dynamic_cast(selectionType); if (omod) { Glib::ustring save_extension = (omod->get_extension()) ? (omod->get_extension()) : ""; if ( !hasEnding(fileName, save_extension) ) { fileName += save_extension; } } // FIXME: does the argument !is_copy really convey the correct meaning here? success = file_save(parentWindow, doc, fileName, selectionType, TRUE, !is_copy, save_method); if (success && doc->getURI()) { sp_file_add_recent( doc->getURI() ); } save_path = Glib::path_get_dirname(fileName); Inkscape::Extension::store_save_path_in_prefs(save_path, save_method); return success; } return false; } /** * Save a document, displaying a SaveAs dialog if necessary. */ bool sp_file_save_document(Gtk::Window &parentWindow, SPDocument *doc) { bool success = true; if (doc->isModifiedSinceSave()) { if ( doc->getURI() == NULL ) { // Hier sollte in Argument mitgegeben werden, das anzeigt, da� das Dokument das erste // Mal gespeichert wird, so da� als default .svg ausgew�hlt wird und nicht die zuletzt // benutzte "Save as ..."-Endung return sp_file_save_dialog(parentWindow, doc, Inkscape::Extension::FILE_SAVE_METHOD_INKSCAPE_SVG); } else { Glib::ustring extension = Inkscape::Extension::get_file_save_extension(Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS); Glib::ustring fn = g_strdup(doc->getURI()); // Try to determine the extension from the uri; this may not lead to a valid extension, // but this case is caught in the file_save method below (or rather in Extension::save() // further down the line). Glib::ustring ext = ""; Glib::ustring::size_type pos = fn.rfind('.'); if (pos != Glib::ustring::npos) { // FIXME: this could/should be more sophisticated (see FileSaveDialog::appendExtension()), // but hopefully it's a reasonable workaround for now ext = fn.substr( pos ); } success = file_save(parentWindow, doc, fn, Inkscape::Extension::db.get(ext.c_str()), FALSE, TRUE, Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS); if (success == false) { // give the user the chance to change filename or extension return sp_file_save_dialog(parentWindow, doc, Inkscape::Extension::FILE_SAVE_METHOD_INKSCAPE_SVG); } } } else { Glib::ustring msg; if ( doc->getURI() == NULL ) { msg = Glib::ustring::format(_("No changes need to be saved.")); } else { msg = Glib::ustring::format(_("No changes need to be saved."), " ", doc->getURI()); } SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::WARNING_MESSAGE, msg.c_str()); success = TRUE; } return success; } /** * Save a document. */ bool sp_file_save(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*data*/) { if (!SP_ACTIVE_DOCUMENT) return false; SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Saving document...")); sp_namedview_document_from_window(SP_ACTIVE_DESKTOP); return sp_file_save_document(parentWindow, SP_ACTIVE_DOCUMENT); } /** * Save a document, always displaying the SaveAs dialog. */ bool sp_file_save_as(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*data*/) { if (!SP_ACTIVE_DOCUMENT) return false; sp_namedview_document_from_window(SP_ACTIVE_DESKTOP); return sp_file_save_dialog(parentWindow, SP_ACTIVE_DOCUMENT, Inkscape::Extension::FILE_SAVE_METHOD_SAVE_AS); } /** * Save a copy of a document, always displaying a sort of SaveAs dialog. */ bool sp_file_save_a_copy(Gtk::Window &parentWindow, gpointer /*object*/, gpointer /*data*/) { if (!SP_ACTIVE_DOCUMENT) return false; sp_namedview_document_from_window(SP_ACTIVE_DESKTOP); return sp_file_save_dialog(parentWindow, SP_ACTIVE_DOCUMENT, Inkscape::Extension::FILE_SAVE_METHOD_SAVE_COPY); } /*###################### ## I M P O R T ######################*/ /** * Paste the contents of a document into the active desktop. * @param clipdoc The document to paste * @param in_place Whether to paste the selection where it was when copied * @pre @c clipdoc is not empty and items can be added to the current layer */ void sp_import_document(SPDesktop *desktop, SPDocument *clipdoc, bool in_place) { //TODO: merge with file_import() SPDocument *target_document = desktop->getDocument(); Inkscape::XML::Node *root = clipdoc->getReprRoot(); Inkscape::XML::Node *target_parent = desktop->currentLayer()->getRepr(); // copy definitions desktop->doc()->importDefs(clipdoc); Inkscape::XML::Node* clipboard = NULL; // copy objects std::vector pasted_objects; for (Inkscape::XML::Node *obj = root->firstChild() ; obj ; obj = obj->next()) { // Don't copy metadata, defs, named views and internal clipboard contents to the document if (!strcmp(obj->name(), "svg:defs")) { continue; } if (!strcmp(obj->name(), "svg:metadata")) { continue; } if (!strcmp(obj->name(), "sodipodi:namedview")) { continue; } if (!strcmp(obj->name(), "inkscape:clipboard")) { clipboard = obj; continue; } Inkscape::XML::Node *obj_copy = obj->duplicate(target_document->getReprDoc()); target_parent->appendChild(obj_copy); Inkscape::GC::release(obj_copy); pasted_objects.push_back(obj_copy); } /* take that stuff into account: * if( use && selection->includes(use->get_original()) ){//we are copying something whose parent is also copied (!) * transform = ((SPItem*)(use->get_original()->parent))->i2doc_affine().inverse() * transform; * } * */ std::vector pasted_objects_not; if(clipboard) for (Inkscape::XML::Node *obj = clipboard->firstChild() ; obj ; obj = obj->next()) { if(target_document->getObjectById(obj->attribute("id"))) continue; Inkscape::XML::Node *obj_copy = obj->duplicate(target_document->getReprDoc()); target_parent->appendChild(obj_copy); Inkscape::GC::release(obj_copy); pasted_objects_not.push_back(obj_copy); } Inkscape::Selection *selection = desktop->getSelection(); selection->setReprList(pasted_objects_not); Geom::Affine doc2parent = SP_ITEM(desktop->currentLayer())->i2doc_affine().inverse(); selection->applyAffine(desktop->dt2doc() * doc2parent * desktop->doc2dt(), true, false, false); selection->deleteItems(); // Change the selection to the freshly pasted objects selection->setReprList(pasted_objects); // Apply inverse of parent transform selection->applyAffine(desktop->dt2doc() * doc2parent * desktop->doc2dt(), true, false, false); // Update (among other things) all curves in paths, for bounds() to work target_document->ensureUpToDate(); // move selection either to original position (in_place) or to mouse pointer Geom::OptRect sel_bbox = selection->visualBounds(); if (sel_bbox) { // get offset of selection to original position of copied elements Geom::Point pos_original; Inkscape::XML::Node *clipnode = sp_repr_lookup_name(root, "inkscape:clipboard", 1); if (clipnode) { Geom::Point min, max; sp_repr_get_point(clipnode, "min", &min); sp_repr_get_point(clipnode, "max", &max); pos_original = Geom::Point(min[Geom::X], max[Geom::Y]); } Geom::Point offset = pos_original - sel_bbox->corner(3); if (!in_place) { SnapManager &m = desktop->namedview->snap_manager; m.setup(desktop); sp_event_context_discard_delayed_snap_event(desktop->event_context); // get offset from mouse pointer to bbox center, snap to grid if enabled Geom::Point mouse_offset = desktop->point() - sel_bbox->midpoint(); offset = m.multipleOfGridPitch(mouse_offset - offset, sel_bbox->midpoint() + offset) + offset; m.unSetup(); } selection->moveRelative(offset); } target_document->emitReconstructionFinish(); } /** * Import a resource. Called by sp_file_import() */ SPObject * file_import(SPDocument *in_doc, const Glib::ustring &uri, Inkscape::Extension::Extension *key) { SPDesktop *desktop = SP_ACTIVE_DESKTOP; bool cancelled = false; //DEBUG_MESSAGE( fileImport, "file_import( in_doc:%p uri:[%s], key:%p", in_doc, uri, key ); SPDocument *doc; try { doc = Inkscape::Extension::open(key, uri.c_str()); } catch (Inkscape::Extension::Input::no_extension_found &e) { doc = NULL; } catch (Inkscape::Extension::Input::open_failed &e) { doc = NULL; } catch (Inkscape::Extension::Input::open_cancelled &e) { doc = NULL; cancelled = true; } if (doc != NULL) { Inkscape::XML::rebase_hrefs(doc, in_doc->getBase(), true); Inkscape::XML::Document *xml_in_doc = in_doc->getReprDoc(); prevent_id_clashes(doc, in_doc); SPCSSAttr *style = sp_css_attr_from_object(doc->getRoot()); // Count the number of top-level items in the imported document. guint items_count = 0; SPObject *o = NULL; for (auto& child: doc->getRoot()->children) { if (SP_IS_ITEM(&child)) { items_count++; o = &child; } } //ungroup if necessary bool did_ungroup = false; while(items_count==1 && o && SP_IS_GROUP(o) && o->children.size()==1){ std::vectorv; sp_item_group_ungroup(SP_GROUP(o),v,false); o = v.empty() ? NULL : v[0]; did_ungroup=true; } // Create a new group if necessary. Inkscape::XML::Node *newgroup = NULL; if ((style && style->attributeList()) || items_count > 1) { newgroup = xml_in_doc->createElement("svg:g"); sp_repr_css_set(newgroup, style, "style"); } // Determine the place to insert the new object. // This will be the current layer, if possible. // FIXME: If there's no desktop (command line run?) we need // a document:: method to return the current layer. // For now, we just use the root in this case. SPObject *place_to_insert; if (desktop) { place_to_insert = desktop->currentLayer(); } else { place_to_insert = in_doc->getRoot(); } in_doc->importDefs(doc); // Construct a new object representing the imported image, // and insert it into the current document. SPObject *new_obj = NULL; for (auto& child: doc->getRoot()->children) { if (SP_IS_ITEM(&child)) { Inkscape::XML::Node *newitem = did_ungroup ? o->getRepr()->duplicate(xml_in_doc) : child.getRepr()->duplicate(xml_in_doc); // convert layers to groups, and make sure they are unlocked // FIXME: add "preserve layers" mode where each layer from // import is copied to the same-named layer in host newitem->setAttribute("inkscape:groupmode", NULL); newitem->setAttribute("sodipodi:insensitive", NULL); if (newgroup) newgroup->appendChild(newitem); else new_obj = place_to_insert->appendChildRepr(newitem); } // don't lose top-level defs or style elements else if (child.getRepr()->type() == Inkscape::XML::ELEMENT_NODE) { const gchar *tag = child.getRepr()->name(); if (!strcmp(tag, "svg:style")) { in_doc->getRoot()->appendChildRepr(child.getRepr()->duplicate(xml_in_doc)); } } } in_doc->emitReconstructionFinish(); if (newgroup) new_obj = place_to_insert->appendChildRepr(newgroup); // release some stuff if (newgroup) Inkscape::GC::release(newgroup); if (style) sp_repr_css_attr_unref(style); // select and move the imported item if (new_obj && SP_IS_ITEM(new_obj)) { Inkscape::Selection *selection = desktop->getSelection(); selection->set(SP_ITEM(new_obj)); // preserve parent and viewBox transformations // c2p is identity matrix at this point unless ensureUpToDate is called doc->ensureUpToDate(); Geom::Affine affine = doc->getRoot()->c2p * SP_ITEM(place_to_insert)->i2doc_affine().inverse(); selection->applyAffine(desktop->dt2doc() * affine * desktop->doc2dt(), true, false, false); // move to mouse pointer { desktop->getDocument()->ensureUpToDate(); Geom::OptRect sel_bbox = selection->visualBounds(); if (sel_bbox) { Geom::Point m( desktop->point() - sel_bbox->midpoint() ); selection->moveRelative(m, false); } } } doc->doUnref(); DocumentUndo::done(in_doc, SP_VERB_FILE_IMPORT, _("Import")); return new_obj; } else if (!cancelled) { gchar *text = g_strdup_printf(_("Failed to load the requested file %s"), uri.c_str()); sp_ui_error_dialog(text); g_free(text); } return NULL; } /** * Display an Open dialog, import a resource if OK pressed. */ void sp_file_import(Gtk::Window &parentWindow) { static Glib::ustring import_path; SPDocument *doc = SP_ACTIVE_DOCUMENT; if (!doc) return; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); if(import_path.empty()) { Glib::ustring attr = prefs->getString("/dialogs/import/path"); if (!attr.empty()) import_path = attr; } //# Test if the import_path directory exists if (!Inkscape::IO::file_test(import_path.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) import_path = ""; //# If no open path, default to our home directory if (import_path.empty()) { import_path = g_get_home_dir(); import_path.append(G_DIR_SEPARATOR_S); } // Create new dialog (don't use an old one, because parentWindow has probably changed) Inkscape::UI::Dialog::FileOpenDialog *importDialogInstance = Inkscape::UI::Dialog::FileOpenDialog::create( parentWindow, import_path, Inkscape::UI::Dialog::IMPORT_TYPES, (char const *)_("Select file to import")); bool success = importDialogInstance->show(); if (!success) { delete importDialogInstance; return; } typedef std::vector pathnames; pathnames flist(importDialogInstance->getFilenames()); // Get file name and extension type Glib::ustring fileName = importDialogInstance->getFilename(); Inkscape::Extension::Extension *selection = importDialogInstance->getSelectionType(); delete importDialogInstance; importDialogInstance = NULL; //# Iterate through filenames if more than 1 if (flist.size() > 1) { for (unsigned int i = 0; i < flist.size(); i++) { fileName = flist[i]; Glib::ustring newFileName = Glib::filename_to_utf8(fileName); if (!newFileName.empty()) fileName = newFileName; else g_warning("ERROR CONVERTING IMPORT FILENAME TO UTF-8"); #ifdef INK_DUMP_FILENAME_CONV g_message("Importing File %s\n", fileName.c_str()); #endif file_import(doc, fileName, selection); } return; } if (!fileName.empty()) { Glib::ustring newFileName = Glib::filename_to_utf8(fileName); if (!newFileName.empty()) fileName = newFileName; else g_warning("ERROR CONVERTING IMPORT FILENAME TO UTF-8"); import_path = Glib::path_get_dirname(fileName); import_path.append(G_DIR_SEPARATOR_S); prefs->setString("/dialogs/import/path", import_path); file_import(doc, fileName, selection); } return; } /*###################### ## E X P O R T ######################*/ #ifdef NEW_EXPORT_DIALOG /** * Display an Export dialog, export as the selected type if OK pressed */ bool sp_file_export_dialog(Gtk::Window &parentWindow) { //# temp hack for 'doc' until we can switch to this dialog SPDocument *doc = SP_ACTIVE_DOCUMENT; Glib::ustring export_path; Glib::ustring export_loc; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Inkscape::Extension::Output *extension; //# Get the default extension name Glib::ustring default_extension = prefs->getString("/dialogs/save_export/default"); if(default_extension.empty()) { // FIXME: Is this a good default? Should there be a macro for the string? default_extension = "org.inkscape.output.png.cairo"; } //g_message("%s: extension name: '%s'", __FUNCTION__, default_extension); if (doc->uri == NULL) { Glib::ustring filename_extension = ".svg"; extension = dynamic_cast (Inkscape::Extension::db.get(default_extension.c_str())); //g_warning("%s: extension ptr: 0x%x", __FUNCTION__, (unsigned int)extension); if (extension) filename_extension = extension->get_extension(); Glib::ustring attr3 = prefs->getString("/dialogs/save_export/path"); if (!attr3.empty()) export_path = attr3; if (!Inkscape::IO::file_test(export_path.c_str(), (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR))) export_path = Glib::ustring(""); if (export_path.empty()) export_path = g_get_home_dir(); export_loc = export_path + G_DIR_SEPARATOR_S + _("drawing") + filename_extension; } else { export_path = Glib::path_get_dirname(doc->uri); } // convert save_loc from utf-8 to locale // is this needed any more, now that everything is handled in // Inkscape::IO? Glib::ustring export_path_local = Glib::filename_from_utf8(export_path); if (!export_path_local.empty()) export_path = export_path_local; //# Show the Export dialog Inkscape::UI::Dialog::FileExportDialog *exportDialogInstance = Inkscape::UI::Dialog::FileExportDialog::create( parentWindow, export_path, Inkscape::UI::Dialog::EXPORT_TYPES, (char const *) _("Select file to export to"), default_extension ); bool success = exportDialogInstance->show(); if (!success) { delete exportDialogInstance; return success; } Glib::ustring fileName = exportDialogInstance->getFilename(); Inkscape::Extension::Extension *selectionType = exportDialogInstance->getSelectionType(); delete exportDialogInstance; exportDialogInstance = NULL; if (fileName.size() > 0) { Glib::ustring newFileName = Glib::filename_to_utf8(fileName); if ( newFileName.size()>0 ) fileName = newFileName; else g_warning( "Error converting save filename to UTF-8." ); success = file_save(parentWindow, doc, fileName, selectionType, TRUE, FALSE, Inkscape::Extension::FILE_SAVE_METHOD_EXPORT); if (success) { Glib::RefPtr recent = Gtk::RecentManager::get_default(); recent->add_item( doc->getURI() ); } export_path = fileName; prefs->setString("/dialogs/save_export/path", export_path); return success; } return false; } #else /** * TODO Delete * This is now a dialog called from dialog manager * bool sp_file_export_dialog(Gtk::Window &parentWindow) { // sp_export_dialog(); return true; } */ #endif /*###################### ## E X P O R T T O O C A L ######################*/ /** * Display an Export dialog, export as the selected type if OK pressed */ /* bool sp_file_export_to_ocal_dialog(Gtk::Window &parentWindow) { if (!SP_ACTIVE_DOCUMENT) return false; SPDocument *doc = SP_ACTIVE_DOCUMENT; Glib::ustring export_path; Glib::ustring export_loc; Glib::ustring fileName; Inkscape::Extension::Extension *selectionType; bool success = false; static bool gotSuccess = false; Inkscape::XML::Node *repr = doc->getReprRoot(); (void)repr; if (!doc->uri && !doc->isModifiedSinceSave()) return false; // Get the default extension name Glib::ustring default_extension = "org.inkscape.output.svg.inkscape"; char formatBuf[256]; Glib::ustring filename_extension = ".svg"; selectionType = Inkscape::Extension::db.get(default_extension.c_str()); export_path = Glib::get_tmp_dir (); export_loc = export_path; export_loc.append(G_DIR_SEPARATOR_S); snprintf(formatBuf, 255, _("drawing%s"), filename_extension.c_str()); export_loc.append(formatBuf); // convert save_loc from utf-8 to locale // is this needed any more, now that everything is handled in // Inkscape::IO? Glib::ustring export_path_local = Glib::filename_from_utf8(export_path); if ( export_path_local.size() > 0) export_path = export_path_local; // Show the Export To OCAL dialog Inkscape::UI::Dialog::OCAL:ExportDialog *exportDialogInstance = new Inkscape::UI::Dialog::OCAL:ExportDialog parentWindow, Inkscape::UI::Dialog::EXPORT_TYPES, (char const *) _("Select file to export to") ); success = exportDialogInstance->show(); if (!success) { delete exportDialogInstance; return success; } fileName = exportDialogInstance->getFilename(); delete exportDialogInstance; exportDialogInstance = NULL;; fileName.append(filename_extension.c_str()); if (fileName.size() > 0) { Glib::ustring newFileName = Glib::filename_to_utf8(fileName); if ( newFileName.size()>0 ) fileName = newFileName; else g_warning( "Error converting save filename to UTF-8." ); } Glib::ustring filePath = export_path; filePath.append(G_DIR_SEPARATOR_S); filePath.append(Glib::path_get_basename(fileName)); fileName = filePath; success = file_save(parentWindow, doc, filePath, selectionType, FALSE, FALSE, Inkscape::Extension::FILE_SAVE_METHOD_EXPORT); if (!success){ gchar *text = g_strdup_printf(_("Error saving a temporary copy")); sp_ui_error_dialog(text); return success; } // Start now the submition // Create the uri Inkscape::Preferences *prefs = Inkscape::Preferences::get(); Glib::ustring uri = "dav://"; Glib::ustring username = prefs->getString("/options/ocalusername/str"); Glib::ustring password = prefs->getString("/options/ocalpassword/str"); if (username.empty() || password.empty()) { Inkscape::UI::Dialog::FileExportToOCALPasswordDialog *exportPasswordDialogInstance = NULL; if(!gotSuccess) { exportPasswordDialogInstance = new Inkscape::UI::Dialog::FileExportToOCALPasswordDialog( parentWindow, (char const *) _("Open Clip Art Login")); success = exportPasswordDialogInstance->show(); if (!success) { delete exportPasswordDialogInstance; return success; } } username = exportPasswordDialogInstance->getUsername(); password = exportPasswordDialogInstance->getPassword(); delete exportPasswordDialogInstance; exportPasswordDialogInstance = NULL; } uri.append(username); uri.append(":"); uri.append(password); uri.append("@"); uri.append(prefs->getString("/options/ocalurl/str")); uri.append("/dav.php/"); uri.append(Glib::path_get_basename(fileName)); // Save as a remote file using the dav protocol. success = file_save_remote(doc, uri, selectionType, FALSE, FALSE); remove(fileName.c_str()); if (!success) { 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.")); sp_ui_error_dialog(text); } else gotSuccess = true; return success; } */ /** * Export the current document to OCAL */ /* void sp_file_export_to_ocal(Gtk::Window &parentWindow) { // Try to execute the new code and return; if (!SP_ACTIVE_DOCUMENT) return; bool success = sp_file_export_to_ocal_dialog(parentWindow); if (success) SP_ACTIVE_DESKTOP->messageStack()->flash(Inkscape::IMMEDIATE_MESSAGE, _("Document exported...")); } */ /*###################### ## I M P O R T F R O M O C A L ######################*/ Inkscape::UI::Dialog::OCAL::ImportDialog* import_ocal_dialog = NULL; /** * Display an ImportFromOcal Dialog, and the selected document from OCAL */ void on_import_from_ocal_response(Glib::ustring filename) { SPDocument *doc = SP_ACTIVE_DOCUMENT; if (!filename.empty()) { Inkscape::Extension::Extension *selection = import_ocal_dialog->get_selection_type(); file_import(doc, filename, selection); } } void sp_file_import_from_ocal(Gtk::Window &parent_window) { static Glib::ustring import_path; SPDocument *doc = SP_ACTIVE_DOCUMENT; if (!doc) return; if (import_ocal_dialog == NULL) { import_ocal_dialog = new Inkscape::UI::Dialog::OCAL::ImportDialog( parent_window, Inkscape::UI::Dialog::IMPORT_TYPES, (char const *)_("Import Clip Art")); import_ocal_dialog->signal_response().connect( sigc::ptr_fun(&on_import_from_ocal_response)); } import_ocal_dialog->show_all(); } /*###################### ## P R I N T ######################*/ /** * Print the current document, if any. */ void sp_file_print(Gtk::Window& parentWindow) { SPDocument *doc = SP_ACTIVE_DOCUMENT; if (doc) sp_print_document(parentWindow, doc); } /* Local Variables: mode:c++ c-file-style:"stroustrup" c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +)) indent-tabs-mode:nil fill-column:99 End: */ // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :