Code

Indent support for XSLT extensions output.
[inkscape.git] / src / main.cpp
index 9da8c304858fc1c65a4eeebb67c82f25d387273b..a1b21cc4d0f65b1cf1fb3ad2cba4787ab2648ec0 100644 (file)
@@ -1,5 +1,3 @@
-#define __MAIN_C__
-
 /** \file
  * Inkscape - an ambitious vector drawing program
  *
@@ -15,6 +13,8 @@
  *   Pawel Palucha
  *   Bryce Harrington <bryce@bryceharrington.com>
  * ... and various people who have worked with various projects
+ *   Jon A. Cruz <jon@oncruz.org>
+ *   Abhishek Sharma
  *
  * Copyright (C) 1999-2004 authors
  * Copyright (C) 2001-2002 Ximian, Inc.
@@ -28,6 +28,9 @@
 #endif
 #include "path-prefix.h"
 
+// This has to be included prior to anything that includes setjmp.h, it croaks otherwise
+#include <png.h>
+
 #include <gtk/gtkmessagedialog.h>
 
 #ifdef HAVE_IEEEFP_H
@@ -44,6 +47,8 @@
 #endif /* Not def: POPT_TABLEEND */
 
 #include <libxml/tree.h>
+#include <glib.h>
+#include <glib/gprintf.h>
 #include <glib-object.h>
 #include <gtk/gtk.h>
 #include <gtk/gtkmain.h>
 
 #include "gc-core.h"
 
+#ifdef AND
+#undef AND
+#endif
+
 #include "macros.h"
 #include "file.h"
 #include "document.h"
 #include <extension/system.h>
 #include <extension/db.h>
 #include <extension/output.h>
+#include <extension/input.h>
 
 #ifdef WIN32
-//#define REPLACEARGS_ANSI
-//#define REPLACEARGS_DEBUG
-
 #include "registrytool.h"
-
 #include "extension/internal/win32.h"
 using Inkscape::Extension::Internal::PrintWin32;
-
 #endif // WIN32
 
 #include "extension/init.h"
@@ -109,13 +114,10 @@ using Inkscape::Extension::Internal::PrintWin32;
 #define bind_textdomain_codeset(p,c)
 #endif
 
-#include "application/application.h"
 #include "main-cmdlineact.h"
 #include "widgets/icon.h"
 #include "ui/widget/panel.h"
 
-
-#include <png.h>
 #include <errno.h>
 
 enum {
@@ -128,7 +130,7 @@ enum {
     SP_ARG_EXPORT_DPI,
     SP_ARG_EXPORT_AREA,
     SP_ARG_EXPORT_AREA_DRAWING,
-    SP_ARG_EXPORT_AREA_CANVAS,
+    SP_ARG_EXPORT_AREA_PAGE,
     SP_ARG_EXPORT_AREA_SNAP,
     SP_ARG_EXPORT_WIDTH,
     SP_ARG_EXPORT_HEIGHT,
@@ -141,6 +143,7 @@ enum {
     SP_ARG_EXPORT_PS,
     SP_ARG_EXPORT_EPS,
     SP_ARG_EXPORT_PDF,
+    SP_ARG_EXPORT_LATEX,
 #ifdef WIN32
     SP_ARG_EXPORT_EMF,
 #endif //WIN32
@@ -178,7 +181,8 @@ static gchar *sp_export_png = NULL;
 static gchar *sp_export_dpi = NULL;
 static gchar *sp_export_area = NULL;
 static gboolean sp_export_area_drawing = FALSE;
-static gboolean sp_export_area_canvas = FALSE;
+static gboolean sp_export_area_page = FALSE;
+static gboolean sp_export_latex = FALSE;
 static gchar *sp_export_width = NULL;
 static gchar *sp_export_height = NULL;
 static gchar *sp_export_id = NULL;
@@ -203,7 +207,6 @@ static gboolean sp_query_width = FALSE;
 static gboolean sp_query_height = FALSE;
 static gboolean sp_query_all = FALSE;
 static gchar *sp_query_id = NULL;
-static int sp_new_gui = FALSE;
 static gboolean sp_shell = FALSE;
 static gboolean sp_vacuum_defs = FALSE;
 
@@ -221,7 +224,8 @@ static void resetCommandlineGlobals() {
         sp_export_dpi = NULL;
         sp_export_area = NULL;
         sp_export_area_drawing = FALSE;
-        sp_export_area_canvas = FALSE;
+        sp_export_area_page = FALSE;
+        sp_export_latex = FALSE;
         sp_export_width = NULL;
         sp_export_height = NULL;
         sp_export_id = NULL;
@@ -290,22 +294,22 @@ struct poptOption options[] = {
 
     {"export-dpi", 'd',
      POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
-     N_("The resolution used for exporting SVG into bitmap (default 90)"),
+     N_("Resolution for exporting to bitmap and for rasterization of filters in PS/EPS/PDF (default 90)"),
      N_("DPI")},
 
     {"export-area", 'a',
      POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
-     N_("Exported area in SVG user units (default is the canvas; 0,0 is lower-left corner)"),
+     N_("Exported area in SVG user units (default is the page; 0,0 is lower-left corner)"),
      N_("x0:y0:x1:y1")},
 
     {"export-area-drawing", 'D',
      POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
-     N_("Exported area is the entire drawing (not canvas)"),
+     N_("Exported area is the entire drawing (not page)"),
      NULL},
 
-    {"export-area-canvas", 'C',
-     POPT_ARG_NONE, &sp_export_area_canvas, SP_ARG_EXPORT_AREA_CANVAS,
-     N_("Exported area is the entire canvas"),
+    {"export-area-page", 'C',
+     POPT_ARG_NONE, &sp_export_area_page, SP_ARG_EXPORT_AREA_PAGE,
+     N_("Exported area is the entire page"),
      NULL},
 
     {"export-area-snap", 0,
@@ -370,6 +374,11 @@ struct poptOption options[] = {
      N_("Export document to a PDF file"),
      N_("FILENAME")},
 
+    {"export-latex", 0,
+     POPT_ARG_NONE, &sp_export_latex, SP_ARG_EXPORT_LATEX,
+     N_("Export PDF/PS/EPS without text. Besides the PDF/PS/EPS, a LaTeX file is exported, putting the text on top of the PDF/PS/EPS file. Include the result in LaTeX like: \\input{latexfile.tex}"),
+     NULL},
+
 #ifdef WIN32
     {"export-emf", 'M',
      POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
@@ -449,7 +458,7 @@ struct poptOption options[] = {
 
     {"shell", 0,
      POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
-     N_("Start Inkscape in interative shell mode."),
+     N_("Start Inkscape in interactive shell mode."),
      NULL},
 
     POPT_AUTOHELP POPT_TABLEEND
@@ -463,77 +472,109 @@ gchar * blankParam = g_strdup("");
 #ifdef WIN32
 
 /**
- * Return the directory of the .exe that is currently running
- */
-static Glib::ustring _win32_getExePath()
-{
-    char exeName[MAX_PATH+1];
-    GetModuleFileName(NULL, exeName, MAX_PATH);
-    char *slashPos = strrchr(exeName, '\\');
-    if (slashPos)
-        *slashPos = '\0';
-    Glib::ustring s = exeName;
-    return s;
-}
-
-/**
- * Set up the PATH and PYTHONPATH environment variables on
- * win32
+ * Set up the PATH and PYTHONPATH environment variables on Windows
+ * @param exe Inkscape executable directory in UTF-8
  */
-static int _win32_set_inkscape_env(const Glib::ustring &exePath)
+static void _win32_set_inkscape_env(gchar const *exe)
 {
-
-    char *oldenv = getenv("PATH");
-    Glib::ustring tmp = "PATH=";
-    tmp += exePath;
-    tmp += ";";
-    tmp += exePath;
-    tmp += "\\python;";
-    tmp += exePath;
-    tmp += "\\python\\Scripts;";  // for uniconv.cmd
-    tmp += exePath;
-    tmp += "\\perl";
-    if(oldenv != NULL) {
-        tmp += ";";
-        tmp += oldenv;
+    gchar const *path = g_getenv("PATH");
+    gchar const *pythonpath = g_getenv("PYTHONPATH");
+
+    gchar *python = g_build_filename(exe, "python", NULL);
+    gchar *scripts = g_build_filename(exe, "python", "Scripts", NULL);
+    gchar *perl = g_build_filename(exe, "python", NULL);
+    gchar *pythonlib = g_build_filename(exe, "python", "Lib", NULL);
+    gchar *pythondll = g_build_filename(exe, "python", "DLLs", NULL);
+    
+    // Python 2.x needs short paths in PYTHONPATH.
+    // Otherwise it doesn't work when Inkscape is installed in Unicode directories.
+    // g_win32_locale_filename_from_utf8 is the GLib wrapper for GetShortPathName.
+    // Remove this once we move to Python 3.0.
+    gchar *python_s = g_win32_locale_filename_from_utf8(python);
+    gchar *pythonlib_s = g_win32_locale_filename_from_utf8(pythonlib);
+    gchar *pythondll_s = g_win32_locale_filename_from_utf8(pythondll);
+
+    gchar *new_path;
+    gchar *new_pythonpath;
+    if (path) {
+        new_path = g_strdup_printf("%s;%s;%s;%s;%s", exe, python, scripts, perl, path);
+    } else {
+        new_path = g_strdup_printf("%s;%s;%s;%s", exe, python, scripts, perl);
     }
-    _putenv(tmp.c_str());
-
-    oldenv = getenv("PYTHONPATH");
-    tmp = "PYTHONPATH=";
-    tmp += exePath;
-    tmp += "\\python;";
-    tmp += exePath;
-    tmp += "\\python\\Lib;";
-    tmp += exePath;
-    tmp += "\\python\\DLLs";
-    if(oldenv != NULL) {
-        tmp += ";";
-        tmp += oldenv;
+    if (pythonpath) {
+        new_pythonpath = g_strdup_printf("%s;%s;%s;%s",
+             python_s, pythonlib_s, pythondll_s, pythonpath);
+    } else {
+        new_pythonpath = g_strdup_printf("%s;%s;%s",
+            python_s, pythonlib_s, pythondll_s);
     }
-    _putenv(tmp.c_str());
 
-    return 0;
+    g_setenv("PATH", new_path, TRUE);
+    g_setenv("PYTHONPATH", new_pythonpath, TRUE);
+
+    /*
+    printf("PATH = %s\n\n", g_getenv("PATH"));
+    printf("PYTHONPATH = %s\n\n", g_getenv("PYTHONPATH"));
+
+    gchar *p = g_find_program_in_path("python");
+    if (p) {
+        printf("python in %s\n\n", p);
+        g_free(p);
+    } else {
+        printf("python not found\n\n");
+    }*/
+
+    g_free(python);
+    g_free(scripts);
+    g_free(perl);
+    g_free(pythonlib);
+    g_free(pythondll);
+    
+    g_free(python_s);
+    g_free(pythonlib_s);
+    g_free(pythondll_s);
+
+    g_free(new_path);
+    g_free(new_pythonpath);
 }
 #endif
 
-/**
- * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
- * can find inkex.py et al. (Bug #197475)
- */
-static int set_extensions_env()
+static void set_extensions_env()
 {
-    char *oldenv = getenv("PYTHONPATH");
-    Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
-    if (oldenv != NULL) {
-        tmp += G_SEARCHPATH_SEPARATOR;
-        tmp += oldenv;
-    }
-    g_setenv("PYTHONPATH", tmp.c_str(), TRUE);
+    gchar const *pythonpath = g_getenv("PYTHONPATH");
+    gchar *extdir;
+    gchar *new_pythonpath;
     
-    return 0;
-}
+#ifdef WIN32
+    extdir = g_win32_locale_filename_from_utf8(INKSCAPE_EXTENSIONDIR);
+#else
+    extdir = g_strdup(INKSCAPE_EXTENSIONDIR);
+#endif
+
+    // On some platforms, INKSCAPE_EXTENSIONDIR is not absolute,
+    // but relative to the directory that contains the Inkscape executable.
+    // Since we spawn Python chdir'ed into the script's directory,
+    // we need to obtain the absolute path here.
+    if (!g_path_is_absolute(extdir)) {
+        gchar *curdir = g_get_current_dir();
+        gchar *extdir_new = g_build_filename(curdir, extdir, NULL);
+        g_free(extdir);
+        g_free(curdir);
+        extdir = extdir_new;
+    }
 
+    if (pythonpath) {
+        new_pythonpath = g_strdup_printf("%s" G_SEARCHPATH_SEPARATOR_S "%s",
+                                         extdir, pythonpath);
+        g_free(extdir);
+    } else {
+        new_pythonpath = extdir;
+    }
+
+    g_setenv("PYTHONPATH", new_pythonpath, TRUE);
+    g_free(new_pythonpath);
+    //printf("PYTHONPATH = %s\n", g_getenv("PYTHONPATH"));
+}
 
 /**
  * This is the classic main() entry point of the program, though on some
@@ -550,56 +591,53 @@ main(int argc, char **argv)
 #endif
 
 #ifdef WIN32
-    /*
-      Set the current directory to the directory of the
-      executable.  This seems redundant, but is needed for
-      when inkscape.exe is executed from another directory.
-      We use relative paths on win32.
-      HKCR\svgfile\shell\open\command is a good example
-    */
-    Glib::ustring homedir = _win32_getExePath();
-    SetCurrentDirectory(homedir.c_str());
-    _win32_set_inkscape_env(homedir);
-    RegistryTool rt;
-    rt.setPathInfo();
-#endif
-
-    // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
-    Gtk::Main::init_gtkmm_internals();
-
-    // Bug #197475
-    set_extensions_env();
-
-   /**
-    * Call bindtextdomain() for various machines's paths
-    */
-#ifdef ENABLE_NLS
-#ifdef WIN32
-    Glib::ustring localePath = homedir;
-    localePath += "\\";
-    localePath += PACKAGE_LOCALE_DIR;
-    bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
-#else
-#ifdef ENABLE_BINRELOC
+    gchar *exedir = g_strdup(win32_getExePath().data());
+    _win32_set_inkscape_env(exedir);
+
+# ifdef ENABLE_NLS
+    // obtain short path to executable dir and pass it
+    // to bindtextdomain (it doesn't understand UTF-8)
+    gchar *shortexedir = g_win32_locale_filename_from_utf8(exedir);
+    gchar *localepath = g_build_filename(shortexedir, PACKAGE_LOCALE_DIR, NULL);
+    bindtextdomain(GETTEXT_PACKAGE, localepath);
+    g_free(shortexedir);
+    g_free(localepath);
+# endif
+    g_free(exedir);
+
+    // Don't touch the registry (works fine without it) for Inkscape Portable
+    gchar const *val = g_getenv("INKSCAPE_PORTABLE_PROFILE_DIR");
+    if (!val) {
+        RegistryTool rt;
+        rt.setPathInfo();
+    }
+#elif defined(ENABLE_NLS)
+# ifdef ENABLE_BINRELOC
     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
-#else
+# else
     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
+# endif
 #endif
-#endif
+
+    // the bit below compiles regardless of platform
+#ifdef ENABLE_NLS
     // Allow the user to override the locale directory by setting
     // the environment variable INKSCAPE_LOCALEDIR.
-    char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
+    char const *inkscape_localedir = g_getenv("INKSCAPE_LOCALEDIR");
     if (inkscape_localedir != NULL) {
         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
     }
-#endif
 
+    // common setup
     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
-
-#ifdef ENABLE_NLS
     textdomain(GETTEXT_PACKAGE);
 #endif
 
+    set_extensions_env();
+
+    // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
+    Gtk::Main::init_gtkmm_internals();
+
     LIBXML_TEST_VERSION
 
     Inkscape::GC::init();
@@ -609,7 +647,7 @@ main(int argc, char **argv)
     gboolean use_gui;
 
 #ifndef WIN32
-    use_gui = (getenv("DISPLAY") != NULL);
+    use_gui = (g_getenv("DISPLAY") != NULL);
 #else
     use_gui = TRUE;
 #endif
@@ -622,19 +660,20 @@ main(int argc, char **argv)
             || !strcmp(argv[i], "-e")
             || !strncmp(argv[i], "--export-png", 12)
             || !strcmp(argv[i], "-l")
-            || !strncmp(argv[i], "--export-plain-svg", 12)
+            || !strncmp(argv[i], "--export-plain-svg", 18)
             || !strcmp(argv[i], "-i")
             || !strncmp(argv[i], "--export-area-drawing", 21)
             || !strcmp(argv[i], "-D")
-            || !strncmp(argv[i], "--export-area-canvas", 20)
+            || !strncmp(argv[i], "--export-area-page", 18)
             || !strcmp(argv[i], "-C")
-            || !strncmp(argv[i], "--export-id", 12)
+            || !strncmp(argv[i], "--export-id", 11)
             || !strcmp(argv[i], "-P")
             || !strncmp(argv[i], "--export-ps", 11)
             || !strcmp(argv[i], "-E")
             || !strncmp(argv[i], "--export-eps", 12)
             || !strcmp(argv[i], "-A")
             || !strncmp(argv[i], "--export-pdf", 12)
+            || !strncmp(argv[i], "--export-latex", 14)
 #ifdef WIN32
             || !strcmp(argv[i], "-M")
             || !strncmp(argv[i], "--export-emf", 12)
@@ -646,11 +685,11 @@ main(int argc, char **argv)
             || !strcmp(argv[i], "-S")
             || !strncmp(argv[i], "--query-all", 11)
             || !strcmp(argv[i], "-X")
-            || !strncmp(argv[i], "--query-x", 13)
+            || !strncmp(argv[i], "--query-x", 9)
             || !strcmp(argv[i], "-Y")
-            || !strncmp(argv[i], "--query-y", 14)
+            || !strncmp(argv[i], "--query-y", 9)
             || !strcmp(argv[i], "--vacuum-defs")
-            || !strncmp(argv[i], "--shell", 7)
+            || !strcmp(argv[i], "--shell")
            )
         {
             /* main_console handles any exports -- not the gui */
@@ -672,10 +711,15 @@ main(int argc, char **argv)
     }
 #endif // WIN32
 
-    /// \todo  Should this be a static object (see inkscape.cpp)?
-    Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
+    int retcode;
+
+    if (use_gui) {
+        retcode = sp_main_gui(argc, (const char **) argv);
+    } else {
+        retcode = sp_main_console(argc, (const char **) argv);
+    }
 
-    return app.run();
+    return retcode;
 }
 
 
@@ -800,7 +844,7 @@ int sp_common_main( int argc, char const **argv, GSList **flDest )
 
 static void
 snooper(GdkEvent *event, gpointer /*data*/) {
-    if(inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
+    if (inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
     {
         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
         switch (event->type) {
@@ -823,9 +867,69 @@ snooper(GdkEvent *event, gpointer /*data*/) {
             break;
         }
     }
+
+    if (inkscape_trackalt()) {
+        // MacOS X with X11 has some problem with the default
+        // xmodmapping.  A ~/.xmodmap solution does not work reliably due
+        // to the way we package our executable in a .app that can launch
+        // X11 or use an already-running X11.  The same problem has been
+        // reported on Linux but there is no .app/X11 to get in the way
+        // of ~/.xmodmap fixes.  So we make this a preference.
+        //
+        // For some reason, Gdk senses changes in Alt (Mod1) state for
+        // many message types, but not for keystrokes!  So this ugly hack
+        // tracks what the state of Alt-pressing is, and ensures
+        // GDK_MOD1_MASK is in the event->key.state as appropriate.
+        //
+        static gboolean altL_pressed = FALSE;
+        static gboolean altR_pressed = FALSE;
+        static gboolean alt_pressed = FALSE;
+        guint get_group0_keyval(GdkEventKey* event);
+        guint keyval = 0;
+        switch (event->type) {
+        case GDK_MOTION_NOTIFY:
+            alt_pressed = TRUE && (event->motion.state & GDK_MOD1_MASK);
+            break;
+        case GDK_BUTTON_PRESS:
+            alt_pressed = TRUE && (event->button.state & GDK_MOD1_MASK);
+            break;
+        case GDK_KEY_PRESS:
+            keyval = get_group0_keyval(&event->key);
+            if (keyval == GDK_Alt_L) altL_pressed = TRUE;
+            if (keyval == GDK_Alt_R) altR_pressed = TRUE;
+            alt_pressed = alt_pressed || altL_pressed || altR_pressed;
+            alt_pressed = alt_pressed || (event->button.state & GDK_MOD1_MASK);
+            if (alt_pressed)
+                event->key.state |= GDK_MOD1_MASK;
+            else
+                event->key.state &= ~GDK_MOD1_MASK;
+            break;
+        case GDK_KEY_RELEASE:
+            keyval = get_group0_keyval(&event->key);
+            if (keyval == GDK_Alt_L) altL_pressed = FALSE;
+            if (keyval == GDK_Alt_R) altR_pressed = FALSE;
+            if (!altL_pressed && !altR_pressed)
+                alt_pressed = FALSE;
+            break;
+        default:
+            break;
+        }
+        //printf("alt_pressed: %s\n", alt_pressed? "+" : "-");
+    }
+
     gtk_main_do_event (event);
 }
 
+static std::vector<Glib::ustring> getDirectorySet(const gchar* userDir, const gchar* const * systemDirs) {
+    std::vector<Glib::ustring> listing;
+    listing.push_back(userDir);
+    for ( const char* const* cur = systemDirs; *cur; cur++ )
+    {
+        listing.push_back(*cur);
+    }
+    return listing;
+}
+
 int
 sp_main_gui(int argc, char const **argv)
 {
@@ -835,8 +939,24 @@ sp_main_gui(int argc, char const **argv)
     int retVal = sp_common_main( argc, argv, &fl );
     g_return_val_if_fail(retVal == 0, 1);
 
+    // Add possible icon entry directories
+    std::vector<Glib::ustring> dataDirs = getDirectorySet( g_get_user_data_dir(),
+                                                           g_get_system_data_dirs() );
+    for (std::vector<Glib::ustring>::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
+    {
+        std::vector<Glib::ustring> listing;
+        listing.push_back(*it);
+        listing.push_back("inkscape");
+        listing.push_back("icons");
+        Glib::ustring dir = Glib::build_filename(listing);
+        gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir.c_str());
+    }
+
     // Add our icon directory to the search path for icon theme lookups.
+    gchar *usericondir = profile_path("icons");
+    gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), usericondir);
     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
+    g_free(usericondir);
 
     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
     Inkscape::Debug::log_display_config();
@@ -880,15 +1000,30 @@ void sp_process_file_list(GSList *fl)
 {
     while (fl) {
         const gchar *filename = (gchar *)fl->data;
-        SPDocument *doc = Inkscape::Extension::open(NULL, filename);
+
+        SPDocument *doc = NULL;
+        try {
+            doc = Inkscape::Extension::open(NULL, filename);
+        } catch (Inkscape::Extension::Input::no_extension_found &e) {
+            doc = NULL;
+        } catch (Inkscape::Extension::Input::open_failed &e) {
+            doc = NULL;
+        }
+
         if (doc == NULL) {
-            doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
+            try {
+                doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
+            } catch (Inkscape::Extension::Input::no_extension_found &e) {
+                doc = NULL;
+            } catch (Inkscape::Extension::Input::open_failed &e) {
+                doc = NULL;
+            }
         }
         if (doc == NULL) {
-            g_warning("Specified document %s cannot be opened (is it a valid SVG file?)", filename);
+            g_warning("Specified document %s cannot be opened (does not exist or not a valid SVG file)", filename);
         } else {
             if (sp_vacuum_defs) {
-                vacuum_document(doc);
+                doc->vacuumDocument();
             }
             if (sp_vacuum_defs && !sp_export_svg) {
                 // save under the name given in the command line
@@ -897,7 +1032,7 @@ void sp_process_file_list(GSList *fl)
             if (sp_global_printer) {
                 sp_print_document_to_file(doc, sp_global_printer);
             }
-            if (sp_export_png) {
+            if (sp_export_png || (sp_export_id && sp_export_use_hints)) {
                 sp_do_export_png(doc);
             }
             if (sp_export_svg) {
@@ -905,8 +1040,9 @@ void sp_process_file_list(GSList *fl)
                 Inkscape::XML::Node *repr;
                 rdoc = sp_repr_document_new("svg:svg");
                 repr = rdoc->root();
-                repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
-                sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
+                repr = doc->getRoot()->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
+                sp_repr_save_rebased_file(repr->document(), sp_export_svg, SP_SVG_NS_URI,
+                                          doc->getBase(), sp_export_svg);
             }
             if (sp_export_ps) {
                 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
@@ -1060,15 +1196,15 @@ do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const g
             return;
         }
     } else {
-        o = SP_DOCUMENT_ROOT(doc);
+        o = doc->getRoot();
     }
 
     if (o) {
-        sp_document_ensure_up_to_date (doc);
+        doc->ensureUpToDate();
         SPItem *item = ((SPItem *) o);
 
         // "true" SVG bbox for scripting
-        Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
+        Geom::OptRect area = item->getBounds(item->i2doc_affine());
         if (area) {
             Inkscape::SVGOStringStream os;
             if (extent) {
@@ -1083,15 +1219,12 @@ do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const g
     }
 }
 
-static void
-do_query_all (SPDocument *doc)
+static void do_query_all(SPDocument *doc)
 {
-    SPObject *o = NULL;
-
-    o = SP_DOCUMENT_ROOT(doc);
+    SPObject *o = doc->getRoot();
 
     if (o) {
-        sp_document_ensure_up_to_date (doc);
+        doc->ensureUpToDate();
         do_query_all_recurse(o);
     }
 }
@@ -1100,11 +1233,11 @@ static void
 do_query_all_recurse (SPObject *o)
 {
     SPItem *item = ((SPItem *) o);
-    if (o->id && SP_IS_ITEM(item)) {
-        Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
+    if (o->getId() && SP_IS_ITEM(item)) {
+        Geom::OptRect area = item->getBounds(item->i2doc_affine());
         if (area) {
             Inkscape::SVGOStringStream os;
-            os << o->id;
+            os << o->getId();
             os << "," << area->min()[Geom::X];
             os << "," << area->min()[Geom::Y];
             os << "," << area->dimensions()[Geom::X];
@@ -1125,6 +1258,7 @@ static void
 sp_do_export_png(SPDocument *doc)
 {
     const gchar *filename = NULL;
+    bool filename_from_hint = false;
     gdouble dpi = 0.0;
 
     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
@@ -1140,12 +1274,12 @@ sp_do_export_png(SPDocument *doc)
         SPObject *o_area = NULL;
         if (sp_export_id && sp_export_area_drawing) {
             o = doc->getObjectById(sp_export_id);
-            o_area = SP_DOCUMENT_ROOT (doc);
+            o_area = doc->getRoot();
         } else if (sp_export_id) {
             o = doc->getObjectById(sp_export_id);
             o_area = o;
         } else if (sp_export_area_drawing) {
-            o = SP_DOCUMENT_ROOT (doc);
+            o = doc->getRoot();
             o_area = o;
         }
 
@@ -1171,6 +1305,7 @@ sp_do_export_png(SPDocument *doc)
                         filename = sp_export_png;
                     } else {
                         filename = fn_hint;
+                        filename_from_hint = true;
                     }
                 } else {
                     g_warning ("Export filename hint not found for the object.");
@@ -1192,9 +1327,9 @@ sp_do_export_png(SPDocument *doc)
             }
 
             // write object bbox to area
-            sp_document_ensure_up_to_date (doc);
+            doc->ensureUpToDate();
             Geom::OptRect areaMaybe;
-            sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
+            static_cast<SPItem *>(o_area)->invoke_bbox( areaMaybe, static_cast<SPItem *>(o_area)->i2d_affine(), TRUE);
             if (areaMaybe) {
                 area = *areaMaybe;
             } else {
@@ -1210,16 +1345,16 @@ sp_do_export_png(SPDocument *doc)
     if (sp_export_area) {
         /* Try to parse area (given in SVG pixels) */
         gdouble x0,y0,x1,y1;
-        if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
+        if (sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) != 4) {
             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
             return;
         }
         area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
-    } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
-        /* Export the whole canvas */
-        sp_document_ensure_up_to_date (doc);
+    } else if (sp_export_area_page || !(sp_export_id || sp_export_area_drawing)) {
+        /* Export the whole page: note: Inkscape uses 'page' in all menus and dialogs, not 'canvas' */
+        doc->ensureUpToDate();
         Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
-        area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
+        area = Geom::Rect(origin, origin + doc->getDimensions());
     }
 
     // set filename and dpi from options, if not yet set from the hints
@@ -1310,6 +1445,23 @@ sp_do_export_png(SPDocument *doc)
         }
     }
 
+    gchar *path = 0;
+    if (filename_from_hint) {
+        //Make relative paths go from the document location, if possible:
+        if (!g_path_is_absolute(filename) && doc->getURI()) {
+            gchar *dirname = g_path_get_dirname(doc->getURI());
+            if (dirname) {
+                path = g_build_filename(dirname, filename, NULL);
+                g_free(dirname);
+            }
+        }
+        if (!path) {
+            path = g_strdup(filename);
+        }
+    } else {
+        path = g_strdup(filename);
+    }
+
     g_print("Background RRGGBBAA: %08x\n", bgcolor);
 
     g_print("Area %g:%g:%g:%g exported to %lu x %lu pixels (%g dpi)\n", area[Geom::X][0], area[Geom::Y][0], area[Geom::X][1], area[Geom::Y][1], width, height, dpi);
@@ -1317,11 +1469,12 @@ sp_do_export_png(SPDocument *doc)
     g_print("Bitmap saved as: %s\n", filename);
 
     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
-        sp_export_png_file(doc, filename, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
+        sp_export_png_file(doc, path, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
     } else {
         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
     }
 
+    g_free (path);
     g_slist_free (items);
 }
 
@@ -1360,8 +1513,8 @@ static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime
         (*i)->set_param_string ("exportId", "");
     }
 
-    if (sp_export_area_canvas && sp_export_area_drawing) {
-        g_warning ("You cannot use --export-area-canvas and --export-area-drawing at the same time; only the former will take effect.");
+    if (sp_export_area_page && sp_export_area_drawing) {
+        g_warning ("You cannot use --export-area-page and --export-area-drawing at the same time; only the former will take effect.");
         sp_export_area_drawing = false;
     }
 
@@ -1371,22 +1524,22 @@ static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime
         (*i)->set_param_bool ("areaDrawing", FALSE);
     }
 
-    if (sp_export_area_canvas) {
+    if (sp_export_area_page) {
         if (sp_export_eps) {
-            g_warning ("EPS cannot have its bounding box extend beyond its content, so if your drawing is smaller than the canvas, --export-area-canvas will clip it to drawing.");
-        } 
-        (*i)->set_param_bool ("areaCanvas", TRUE);
+            g_warning ("EPS cannot have its bounding box extend beyond its content, so if your drawing is smaller than the page, --export-area-page will clip it to drawing.");
+        }
+        (*i)->set_param_bool ("areaPage", TRUE);
     } else {
-        (*i)->set_param_bool ("areaCanvas", FALSE);
+        (*i)->set_param_bool ("areaPage", FALSE);
     }
 
-    if (!sp_export_area_drawing && !sp_export_area_canvas && !sp_export_id) { 
-        // neither is set, set canvas as default for ps/pdf and drawing for eps
+    if (!sp_export_area_drawing && !sp_export_area_page && !sp_export_id) {
+        // neither is set, set page as default for ps/pdf and drawing for eps
         if (sp_export_eps) {
             try {
                (*i)->set_param_bool("areaDrawing", TRUE);
             } catch (...) {}
-        } 
+        }
     }
 
     if (sp_export_text_to_path) {
@@ -1395,10 +1548,27 @@ static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime
         (*i)->set_param_bool("textToPath", FALSE);
     }
 
+    if (sp_export_latex) {
+        (*i)->set_param_bool("textToLaTeX", TRUE);
+    } else {
+        (*i)->set_param_bool("textToLaTeX", FALSE);
+    }
+
     if (sp_export_ignore_filters) {
         (*i)->set_param_bool("blurToBitmap", FALSE);
     } else {
         (*i)->set_param_bool("blurToBitmap", TRUE);
+
+        gdouble dpi = 90.0;
+        if (sp_export_dpi) {
+            dpi = atof(sp_export_dpi);
+            if ((dpi < 1) || (dpi > 10000.0)) {
+                g_warning("DPI value %s out of range [1 - 10000]. Using 90 dpi instead.", sp_export_dpi);
+                dpi = 90;
+            }
+        }
+
+        (*i)->set_param_int("resolution", (int) dpi);
     }
 
     (*i)->save(doc, uri);