Code

* Merge from trunk
[inkscape.git] / src / main.cpp
1 #define __MAIN_C__
3 /** \file
4  * Inkscape - an ambitious vector drawing program
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Frank Felfe <innerspace@iname.com>
9  *   Davide Puricelli <evo@debian.org>
10  *   Mitsuru Oka <oka326@parkcity.ne.jp>
11  *   Masatake YAMATO  <jet@gyve.org>
12  *   F.J.Franklin <F.J.Franklin@sheffield.ac.uk>
13  *   Michael Meeks <michael@helixcode.com>
14  *   Chema Celorio <chema@celorio.com>
15  *   Pawel Palucha
16  *   Bryce Harrington <bryce@bryceharrington.com>
17  * ... and various people who have worked with various projects
18  *
19  * Copyright (C) 1999-2004 authors
20  * Copyright (C) 2001-2002 Ximian, Inc.
21  *
22  * Released under GNU GPL, read the file 'COPYING' for more information
23  */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 #include "path-prefix.h"
31 // This has to be included prior to anything that includes setjmp.h, it croaks otherwise
32 #include <png.h>
34 #include <gtk/gtkmessagedialog.h>
36 #ifdef HAVE_IEEEFP_H
37 #include <ieeefp.h>
38 #endif
39 #include <cstring>
40 #include <string>
41 #include <locale.h>
42 #include <stdlib.h>
44 #include <popt.h>
45 #ifndef POPT_TABLEEND
46 #define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
47 #endif /* Not def: POPT_TABLEEND */
49 #include <libxml/tree.h>
50 #include <glib-object.h>
51 #include <gtk/gtk.h>
52 #include <gtk/gtkmain.h>
53 #include <gtk/gtksignal.h>
54 #include <gtk/gtkwindow.h>
55 #include <gtk/gtkbox.h>
57 #include "gc-core.h"
59 #include "macros.h"
60 #include "file.h"
61 #include "document.h"
62 #include "sp-object.h"
63 #include "interface.h"
64 #include "print.h"
65 #include "color.h"
66 #include "sp-item.h"
67 #include "sp-root.h"
68 #include "unit-constants.h"
70 #include "svg/svg.h"
71 #include "svg/svg-color.h"
72 #include "svg/stringstream.h"
74 #include "inkscape-private.h"
75 #include "inkscape-version.h"
77 #include "sp-namedview.h"
78 #include "sp-guide.h"
79 #include "sp-object-repr.h"
80 #include "xml/repr.h"
82 #include "io/sys.h"
84 #include "debug/logger.h"
85 #include "debug/log-display-config.h"
87 #include "helper/png-write.h"
88 #include "helper/geom.h"
90 #include <extension/extension.h>
91 #include <extension/system.h>
92 #include <extension/db.h>
93 #include <extension/output.h>
94 #include <extension/input.h>
96 #ifdef WIN32
97 //#define REPLACEARGS_ANSI
98 //#define REPLACEARGS_DEBUG
100 #include "registrytool.h"
102 #include "extension/internal/win32.h"
103 using Inkscape::Extension::Internal::PrintWin32;
105 #endif // WIN32
107 #include "extension/init.h"
109 #include <glibmm/i18n.h>
110 #include <gtkmm/main.h>
112 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
113 #define bind_textdomain_codeset(p,c)
114 #endif
116 #include "application/application.h"
117 #include "main-cmdlineact.h"
118 #include "widgets/icon.h"
119 #include "ui/widget/panel.h"
121 #include <errno.h>
123 enum {
124     SP_ARG_NONE,
125     SP_ARG_NOGUI,
126     SP_ARG_GUI,
127     SP_ARG_FILE,
128     SP_ARG_PRINT,
129     SP_ARG_EXPORT_PNG,
130     SP_ARG_EXPORT_DPI,
131     SP_ARG_EXPORT_AREA,
132     SP_ARG_EXPORT_AREA_DRAWING,
133     SP_ARG_EXPORT_AREA_PAGE,
134     SP_ARG_EXPORT_AREA_SNAP,
135     SP_ARG_EXPORT_WIDTH,
136     SP_ARG_EXPORT_HEIGHT,
137     SP_ARG_EXPORT_ID,
138     SP_ARG_EXPORT_ID_ONLY,
139     SP_ARG_EXPORT_USE_HINTS,
140     SP_ARG_EXPORT_BACKGROUND,
141     SP_ARG_EXPORT_BACKGROUND_OPACITY,
142     SP_ARG_EXPORT_SVG,
143     SP_ARG_EXPORT_PS,
144     SP_ARG_EXPORT_EPS,
145     SP_ARG_EXPORT_PDF,
146 #ifdef WIN32
147     SP_ARG_EXPORT_EMF,
148 #endif //WIN32
149     SP_ARG_EXPORT_TEXT_TO_PATH,
150     SP_ARG_EXPORT_IGNORE_FILTERS,
151     SP_ARG_EXTENSIONDIR,
152     SP_ARG_QUERY_X,
153     SP_ARG_QUERY_Y,
154     SP_ARG_QUERY_WIDTH,
155     SP_ARG_QUERY_HEIGHT,
156     SP_ARG_QUERY_ALL,
157     SP_ARG_QUERY_ID,
158     SP_ARG_SHELL,
159     SP_ARG_VERSION,
160     SP_ARG_VACUUM_DEFS,
161     SP_ARG_VERB_LIST,
162     SP_ARG_VERB,
163     SP_ARG_SELECT,
164     SP_ARG_LAST
165 };
167 int sp_main_gui(int argc, char const **argv);
168 int sp_main_console(int argc, char const **argv);
169 static void sp_do_export_png(SPDocument *doc);
170 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime);
171 #ifdef WIN32
172 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
173 #endif //WIN32
174 static void do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id);
175 static void do_query_all (SPDocument *doc);
176 static void do_query_all_recurse (SPObject *o);
178 static gchar *sp_global_printer = NULL;
179 static gchar *sp_export_png = NULL;
180 static gchar *sp_export_dpi = NULL;
181 static gchar *sp_export_area = NULL;
182 static gboolean sp_export_area_drawing = FALSE;
183 static gboolean sp_export_area_page = FALSE;
184 static gchar *sp_export_width = NULL;
185 static gchar *sp_export_height = NULL;
186 static gchar *sp_export_id = NULL;
187 static gchar *sp_export_background = NULL;
188 static gchar *sp_export_background_opacity = NULL;
189 static gboolean sp_export_area_snap = FALSE;
190 static gboolean sp_export_use_hints = FALSE;
191 static gboolean sp_export_id_only = FALSE;
192 static gchar *sp_export_svg = NULL;
193 static gchar *sp_export_ps = NULL;
194 static gchar *sp_export_eps = NULL;
195 static gchar *sp_export_pdf = NULL;
196 #ifdef WIN32
197 static gchar *sp_export_emf = NULL;
198 #endif //WIN32
199 static gboolean sp_export_text_to_path = FALSE;
200 static gboolean sp_export_ignore_filters = FALSE;
201 static gboolean sp_export_font = FALSE;
202 static gboolean sp_query_x = FALSE;
203 static gboolean sp_query_y = FALSE;
204 static gboolean sp_query_width = FALSE;
205 static gboolean sp_query_height = FALSE;
206 static gboolean sp_query_all = FALSE;
207 static gchar *sp_query_id = NULL;
208 static int sp_new_gui = FALSE;
209 static gboolean sp_shell = FALSE;
210 static gboolean sp_vacuum_defs = FALSE;
212 static gchar *sp_export_png_utf8 = NULL;
213 static gchar *sp_export_svg_utf8 = NULL;
214 static gchar *sp_global_printer_utf8 = NULL;
217 /**
218  *  Reset variables to default values.
219  */
220 static void resetCommandlineGlobals() {
221         sp_global_printer = NULL;
222         sp_export_png = NULL;
223         sp_export_dpi = NULL;
224         sp_export_area = NULL;
225         sp_export_area_drawing = FALSE;
226         sp_export_area_page = FALSE;
227         sp_export_width = NULL;
228         sp_export_height = NULL;
229         sp_export_id = NULL;
230         sp_export_background = NULL;
231         sp_export_background_opacity = NULL;
232         sp_export_area_snap = FALSE;
233         sp_export_use_hints = FALSE;
234         sp_export_id_only = FALSE;
235         sp_export_svg = NULL;
236         sp_export_ps = NULL;
237         sp_export_eps = NULL;
238         sp_export_pdf = NULL;
239 #ifdef WIN32
240         sp_export_emf = NULL;
241 #endif //WIN32
242         sp_export_text_to_path = FALSE;
243         sp_export_ignore_filters = FALSE;
244         sp_export_font = FALSE;
245         sp_query_x = FALSE;
246         sp_query_y = FALSE;
247         sp_query_width = FALSE;
248         sp_query_height = FALSE;
249         sp_query_all = FALSE;
250         sp_query_id = NULL;
251         sp_vacuum_defs = FALSE;
253         sp_export_png_utf8 = NULL;
254         sp_export_svg_utf8 = NULL;
255         sp_global_printer_utf8 = NULL;
258 #ifdef WIN32
259 static bool replaceArgs( int& argc, char**& argv );
260 #endif
261 static GSList *sp_process_args(poptContext ctx);
262 struct poptOption options[] = {
263     {"version", 'V',
264      POPT_ARG_NONE, NULL, SP_ARG_VERSION,
265      N_("Print the Inkscape version number"),
266      NULL},
268     {"without-gui", 'z',
269      POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
270      N_("Do not use X server (only process files from console)"),
271      NULL},
273     {"with-gui", 'g',
274      POPT_ARG_NONE, NULL, SP_ARG_GUI,
275      N_("Try to use X server (even if $DISPLAY is not set)"),
276      NULL},
278     {"file", 'f',
279      POPT_ARG_STRING, NULL, SP_ARG_FILE,
280      N_("Open specified document(s) (option string may be excluded)"),
281      N_("FILENAME")},
283     {"print", 'p',
284      POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
285      N_("Print document(s) to specified output file (use '| program' for pipe)"),
286      N_("FILENAME")},
288     {"export-png", 'e',
289      POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
290      N_("Export document to a PNG file"),
291      N_("FILENAME")},
293     {"export-dpi", 'd',
294      POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
295      N_("Resolution for exporting to bitmap and for rasterization of filters in PS/EPS/PDF (default 90)"),
296      N_("DPI")},
298     {"export-area", 'a',
299      POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
300      N_("Exported area in SVG user units (default is the page; 0,0 is lower-left corner)"),
301      N_("x0:y0:x1:y1")},
303     {"export-area-drawing", 'D',
304      POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
305      N_("Exported area is the entire drawing (not page)"),
306      NULL},
308     {"export-area-page", 'C',
309      POPT_ARG_NONE, &sp_export_area_page, SP_ARG_EXPORT_AREA_PAGE,
310      N_("Exported area is the entire page"),
311      NULL},
313     {"export-area-snap", 0,
314      POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
315      N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
316      NULL},
318     {"export-width", 'w',
319      POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
320      N_("The width of exported bitmap in pixels (overrides export-dpi)"),
321      N_("WIDTH")},
323     {"export-height", 'h',
324      POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
325      N_("The height of exported bitmap in pixels (overrides export-dpi)"),
326      N_("HEIGHT")},
328     {"export-id", 'i',
329      POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
330      N_("The ID of the object to export"),
331      N_("ID")},
333     {"export-id-only", 'j',
334      POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
335      // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
336      //  See "man inkscape" for details.
337      N_("Export just the object with export-id, hide all others (only with export-id)"),
338      NULL},
340     {"export-use-hints", 't',
341      POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
342      N_("Use stored filename and DPI hints when exporting (only with export-id)"),
343      NULL},
345     {"export-background", 'b',
346      POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
347      N_("Background color of exported bitmap (any SVG-supported color string)"),
348      N_("COLOR")},
350     {"export-background-opacity", 'y',
351      POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
352      N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
353      N_("VALUE")},
355     {"export-plain-svg", 'l',
356      POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
357      N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
358      N_("FILENAME")},
360     {"export-ps", 'P',
361      POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
362      N_("Export document to a PS file"),
363      N_("FILENAME")},
365     {"export-eps", 'E',
366      POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
367      N_("Export document to an EPS file"),
368      N_("FILENAME")},
370     {"export-pdf", 'A',
371      POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
372      N_("Export document to a PDF file"),
373      N_("FILENAME")},
375 #ifdef WIN32
376     {"export-emf", 'M',
377      POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
378      N_("Export document to an Enhanced Metafile (EMF) File"),
379      N_("FILENAME")},
380 #endif //WIN32
382     {"export-text-to-path", 'T',
383      POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
384      N_("Convert text object to paths on export (PS, EPS, PDF)"),
385      NULL},
387     {"export-ignore-filters", 0,
388      POPT_ARG_NONE, &sp_export_ignore_filters, SP_ARG_EXPORT_IGNORE_FILTERS,
389      N_("Render filtered objects without filters, instead of rasterizing (PS, EPS, PDF)"),
390      NULL},
392     {"query-x", 'X',
393      POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
394      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
395      N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
396      NULL},
398     {"query-y", 'Y',
399      POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
400      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
401      N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
402      NULL},
404     {"query-width", 'W',
405      POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
406      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
407      N_("Query the width of the drawing or, if specified, of the object with --query-id"),
408      NULL},
410     {"query-height", 'H',
411      POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
412      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
413      N_("Query the height of the drawing or, if specified, of the object with --query-id"),
414      NULL},
416     {"query-all", 'S',
417      POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
418      N_("List id,x,y,w,h for all objects"),
419      NULL},
421     {"query-id", 'I',
422      POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
423      N_("The ID of the object whose dimensions are queried"),
424      N_("ID")},
426     {"extension-directory", 'x',
427      POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
428      // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
429      N_("Print out the extension directory and exit"),
430      NULL},
432     {"vacuum-defs", 0,
433      POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
434      N_("Remove unused definitions from the defs section(s) of the document"),
435      NULL},
437     {"verb-list", 0,
438      POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
439      N_("List the IDs of all the verbs in Inkscape"),
440      NULL},
442     {"verb", 0,
443      POPT_ARG_STRING, NULL, SP_ARG_VERB,
444      N_("Verb to call when Inkscape opens."),
445      N_("VERB-ID")},
447     {"select", 0,
448      POPT_ARG_STRING, NULL, SP_ARG_SELECT,
449      N_("Object ID to select when Inkscape opens."),
450      N_("OBJECT-ID")},
452     {"shell", 0,
453      POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
454      N_("Start Inkscape in interactive shell mode."),
455      NULL},
457     POPT_AUTOHELP POPT_TABLEEND
458 };
460 static bool needToRecodeParams = true;
461 gchar * blankParam = g_strdup("");
465 #ifdef WIN32
467 /**
468  * Return the directory of the .exe that is currently running
469  */
470 static Glib::ustring _win32_getExePath()
472     char exeName[MAX_PATH+1];
473     // TODO these should use xxxW() calls explicitly and convert UTF-16 <--> UTF-8
474     GetModuleFileName(NULL, exeName, MAX_PATH);
475     char *slashPos = strrchr(exeName, '\\');
476     if (slashPos) {
477         *slashPos = '\0';
478     }
479     Glib::ustring s = exeName;
480     return s;
483 /**
484  * Set up the PATH and PYTHONPATH environment variables on
485  * win32
486  */
487 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
489     // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
491     char *oldenv = getenv("PATH");
492     Glib::ustring tmp = "PATH=";
493     tmp += exePath;
494     tmp += ";";
495     tmp += exePath;
496     tmp += "\\python;";
497     tmp += exePath;
498     tmp += "\\python\\Scripts;";  // for uniconv.cmd
499     tmp += exePath;
500     tmp += "\\perl";
501     if(oldenv != NULL) {
502         tmp += ";";
503         tmp += oldenv;
504     }
505     _putenv(tmp.c_str());
507     oldenv = getenv("PYTHONPATH");
508     tmp = "PYTHONPATH=";
509     tmp += exePath;
510     tmp += "\\python;";
511     tmp += exePath;
512     tmp += "\\python\\Lib;";
513     tmp += exePath;
514     tmp += "\\python\\DLLs";
515     if(oldenv != NULL) {
516         tmp += ";";
517         tmp += oldenv;
518     }
519     _putenv(tmp.c_str());
521     return 0;
523 #endif
525 /**
526  * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
527  * can find inkex.py et al. (Bug #197475)
528  */
529 static int set_extensions_env()
531     char *oldenv = getenv("PYTHONPATH");
532     Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
533     if (oldenv != NULL) {
534         tmp += G_SEARCHPATH_SEPARATOR;
535         tmp += oldenv;
536     }
537     g_setenv("PYTHONPATH", tmp.c_str(), TRUE);
539     return 0;
543 /**
544  * This is the classic main() entry point of the program, though on some
545  * architectures it might be called by something else.
546  */
547 int
548 main(int argc, char **argv)
550 #ifdef HAVE_FPSETMASK
551     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
552        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
553        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
554     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
555 #endif
557 #ifdef WIN32
558     /*
559       Set the current directory to the directory of the
560       executable.  This seems redundant, but is needed for
561       when inkscape.exe is executed from another directory.
562       We use relative paths on win32.
563       HKCR\svgfile\shell\open\command is a good example
564     */
565     Glib::ustring homedir = _win32_getExePath();
566     // TODO these should use xxxW() calls explicitly and convert UTF-16 <--> UTF-8
567     SetCurrentDirectory(homedir.c_str());
568     _win32_set_inkscape_env(homedir);
569     // Don't touch the registry (works fine without it) for Inkscape Portable
570     gchar const *val = g_getenv("INKSCAPE_PORTABLE_PROFILE_DIR");
571     if (!val) {
572         RegistryTool rt;
573         rt.setPathInfo();
574     }
575 #endif
577     // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
578     Gtk::Main::init_gtkmm_internals();
580     // Bug #197475
581     set_extensions_env();
583    /**
584     * Call bindtextdomain() for various machines's paths
585     */
586 #ifdef ENABLE_NLS
587 #ifdef WIN32
588     Glib::ustring localePath = homedir;
589     localePath += "\\";
590     localePath += PACKAGE_LOCALE_DIR;
591     bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
592 #else
593 #ifdef ENABLE_BINRELOC
594     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
595 #else
596     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
597 #endif
598 #endif
599     // Allow the user to override the locale directory by setting
600     // the environment variable INKSCAPE_LOCALEDIR.
601     char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
602     if (inkscape_localedir != NULL) {
603         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
604     }
605 #endif
607     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
609 #ifdef ENABLE_NLS
610     textdomain(GETTEXT_PACKAGE);
611 #endif
613     LIBXML_TEST_VERSION
615     Inkscape::GC::init();
617     Inkscape::Debug::Logger::init();
619     gboolean use_gui;
621 #ifndef WIN32
622     // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
623     use_gui = (getenv("DISPLAY") != NULL);
624 #else
625     use_gui = TRUE;
626 #endif
627     /* Test whether with/without GUI is forced */
628     for (int i = 1; i < argc; i++) {
629         if (!strcmp(argv[i], "-z")
630             || !strcmp(argv[i], "--without-gui")
631             || !strcmp(argv[i], "-p")
632             || !strncmp(argv[i], "--print", 7)
633             || !strcmp(argv[i], "-e")
634             || !strncmp(argv[i], "--export-png", 12)
635             || !strcmp(argv[i], "-l")
636             || !strncmp(argv[i], "--export-plain-svg", 18)
637             || !strcmp(argv[i], "-i")
638             || !strncmp(argv[i], "--export-area-drawing", 21)
639             || !strcmp(argv[i], "-D")
640             || !strncmp(argv[i], "--export-area-page", 18)
641             || !strcmp(argv[i], "-C")
642             || !strncmp(argv[i], "--export-id", 11)
643             || !strcmp(argv[i], "-P")
644             || !strncmp(argv[i], "--export-ps", 11)
645             || !strcmp(argv[i], "-E")
646             || !strncmp(argv[i], "--export-eps", 12)
647             || !strcmp(argv[i], "-A")
648             || !strncmp(argv[i], "--export-pdf", 12)
649 #ifdef WIN32
650             || !strcmp(argv[i], "-M")
651             || !strncmp(argv[i], "--export-emf", 12)
652 #endif //WIN32
653             || !strcmp(argv[i], "-W")
654             || !strncmp(argv[i], "--query-width", 13)
655             || !strcmp(argv[i], "-H")
656             || !strncmp(argv[i], "--query-height", 14)
657             || !strcmp(argv[i], "-S")
658             || !strncmp(argv[i], "--query-all", 11)
659             || !strcmp(argv[i], "-X")
660             || !strncmp(argv[i], "--query-x", 9)
661             || !strcmp(argv[i], "-Y")
662             || !strncmp(argv[i], "--query-y", 9)
663             || !strcmp(argv[i], "--vacuum-defs")
664             || !strcmp(argv[i], "--shell")
665            )
666         {
667             /* main_console handles any exports -- not the gui */
668             use_gui = FALSE;
669             break;
670         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
671             use_gui = TRUE;
672             break;
673         }
674     }
676 #ifdef WIN32
677 #ifndef REPLACEARGS_ANSI
678     if ( PrintWin32::is_os_wide() )
679 #endif // REPLACEARGS_ANSI
680     {
681         // If the call fails, we'll need to convert charsets
682         needToRecodeParams = !replaceArgs( argc, argv );
683     }
684 #endif // WIN32
686     /// \todo  Should this be a static object (see inkscape.cpp)?
687     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
689     return app.run();
695 void fixupSingleFilename( gchar **orig, gchar **spare )
697     if ( orig && *orig && **orig ) {
698         GError *error = NULL;
699         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
700         if ( newFileName )
701         {
702             *orig = newFileName;
703             if ( spare ) {
704                 *spare = newFileName;
705             }
706 //             g_message("Set a replacement fixup");
707         }
708     }
713 GSList *fixupFilenameEncoding( GSList* fl )
715     GSList *newFl = NULL;
716     while ( fl ) {
717         gchar *fn = static_cast<gchar*>(fl->data);
718         fl = g_slist_remove( fl, fl->data );
719         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
720         if ( newFileName ) {
722             if ( 0 )
723             {
724                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
725                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
726                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
727                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
728                 gtk_dialog_run (GTK_DIALOG (w));
729                 gtk_widget_destroy (w);
730                 g_free(safeNewFn);
731                 g_free(safeFn);
732             }
734             g_free( fn );
735             fn = newFileName;
736             newFileName = 0;
737         }
738         else
739             if ( 0 )
740         {
741             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
742             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
743             gtk_dialog_run (GTK_DIALOG (w));
744             gtk_widget_destroy (w);
745             g_free(safeFn);
746         }
747         newFl = g_slist_append( newFl, fn );
748     }
749     return newFl;
752 int sp_common_main( int argc, char const **argv, GSList **flDest )
754     /// \todo fixme: Move these to some centralized location (Lauris)
755     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
756     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
759     // temporarily switch gettext encoding to locale, so that help messages can be output properly
760     gchar const *charset;
761     g_get_charset(&charset);
763     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
765     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
766     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
767     g_return_val_if_fail(ctx != NULL, 1);
769     /* Collect own arguments */
770     GSList *fl = sp_process_args(ctx);
771     poptFreeContext(ctx);
773     // now switch gettext back to UTF-8 (for GUI)
774     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
776     // Now let's see if the file list still holds up
777     if ( needToRecodeParams )
778     {
779         fl = fixupFilenameEncoding( fl );
780     }
782     // Check the globals for filename-fixup
783     if ( needToRecodeParams )
784     {
785         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
786         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
787         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
788     }
789     else
790     {
791         if ( sp_export_png )
792             sp_export_png_utf8 = g_strdup( sp_export_png );
793         if ( sp_export_svg )
794             sp_export_svg_utf8 = g_strdup( sp_export_svg );
795         if ( sp_global_printer )
796             sp_global_printer_utf8 = g_strdup( sp_global_printer );
797     }
799     // Return the list if wanted, else free it up.
800     if ( flDest ) {
801         *flDest = fl;
802         fl = 0;
803     } else {
804         while ( fl ) {
805             g_free( fl->data );
806             fl = g_slist_remove( fl, fl->data );
807         }
808     }
809     return 0;
812 static void
813 snooper(GdkEvent *event, gpointer /*data*/) {
814     if (inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
815     {
816         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
817         switch (event->type) {
818             case GDK_MOTION_NOTIFY:
819                 if(event->motion.state & mapping) {
820                     event->motion.state|=GDK_MOD1_MASK;
821                 }
822                 break;
823             case GDK_BUTTON_PRESS:
824                 if(event->button.state & mapping) {
825                     event->button.state|=GDK_MOD1_MASK;
826                 }
827                 break;
828              case GDK_KEY_PRESS:
829                  if(event->key.state & mapping) {
830                      event->key.state|=GDK_MOD1_MASK;
831                  }
832                  break;
833         default:
834             break;
835         }
836     }
838     if (inkscape_trackalt()) {
839         // MacOS X with X11 has some problem with the default
840         // xmodmapping.  A ~/.xmodmap solution does not work reliably due
841         // to the way we package our executable in a .app that can launch
842         // X11 or use an already-running X11.  The same problem has been
843         // reported on Linux but there is no .app/X11 to get in the way
844         // of ~/.xmodmap fixes.  So we make this a preference.
845         //
846         // For some reason, Gdk senses changes in Alt (Mod1) state for
847         // many message types, but not for keystrokes!  So this ugly hack
848         // tracks what the state of Alt-pressing is, and ensures
849         // GDK_MOD1_MASK is in the event->key.state as appropriate.
850         //
851         static gboolean altL_pressed = FALSE;
852         static gboolean altR_pressed = FALSE;
853         static gboolean alt_pressed = FALSE;
854         guint get_group0_keyval(GdkEventKey* event);
855         guint keyval = 0;
856         switch (event->type) {
857         case GDK_MOTION_NOTIFY:
858             alt_pressed = TRUE && (event->motion.state & GDK_MOD1_MASK);
859             break;
860         case GDK_BUTTON_PRESS:
861             alt_pressed = TRUE && (event->button.state & GDK_MOD1_MASK);
862             break;
863         case GDK_KEY_PRESS:
864             keyval = get_group0_keyval(&event->key);
865             if (keyval == GDK_Alt_L) altL_pressed = TRUE;
866             if (keyval == GDK_Alt_R) altR_pressed = TRUE;
867             alt_pressed = alt_pressed || altL_pressed || altR_pressed;
868             alt_pressed = alt_pressed || (event->button.state & GDK_MOD1_MASK);
869             if (alt_pressed)
870                 event->key.state |= GDK_MOD1_MASK;
871             else
872                 event->key.state &= ~GDK_MOD1_MASK;
873             break;
874         case GDK_KEY_RELEASE:
875             keyval = get_group0_keyval(&event->key);
876             if (keyval == GDK_Alt_L) altL_pressed = FALSE;
877             if (keyval == GDK_Alt_R) altR_pressed = FALSE;
878             if (!altL_pressed && !altR_pressed)
879                 alt_pressed = FALSE;
880             break;
881         default:
882             break;
883         }
884         //printf("alt_pressed: %s\n", alt_pressed? "+" : "-");
885     }
887     gtk_main_do_event (event);
890 static std::vector<Glib::ustring> getDirectorySet(const gchar* userDir, const gchar* const * systemDirs) {
891     std::vector<Glib::ustring> listing;
892     listing.push_back(userDir);
893     for ( const char* const* cur = systemDirs; *cur; cur++ )
894     {
895         listing.push_back(*cur);
896     }
897     return listing;
900 int
901 sp_main_gui(int argc, char const **argv)
903     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
905     GSList *fl = NULL;
906     int retVal = sp_common_main( argc, argv, &fl );
907     g_return_val_if_fail(retVal == 0, 1);
909     // Add possible icon entry directories
910     std::vector<Glib::ustring> dataDirs = getDirectorySet( g_get_user_data_dir(),
911                                                            g_get_system_data_dirs() );
912     for (std::vector<Glib::ustring>::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
913     {
914         std::vector<Glib::ustring> listing;
915         listing.push_back(*it);
916         listing.push_back("inkscape");
917         listing.push_back("icons");
918         Glib::ustring dir = Glib::build_filename(listing);
919         gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir.c_str());
920     }
922     // Add our icon directory to the search path for icon theme lookups.
923     gchar *usericondir = profile_path("icons");
924     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), usericondir);
925     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
926     g_free(usericondir);
928     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
929     Inkscape::Debug::log_display_config();
931     // Set default window icon. Obeys the theme.
932     gtk_window_set_default_icon_name("inkscape");
933     // Do things that were previously in inkscape_gtk_stock_init().
934     sp_icon_get_phys_size(GTK_ICON_SIZE_MENU);
935     Inkscape::UI::Widget::Panel::prep();
937     gboolean create_new = TRUE;
939     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
940     inkscape_application_init(argv[0], true);
942     while (fl) {
943         if (sp_file_open((gchar *)fl->data,NULL)) {
944             create_new=FALSE;
945         }
946         fl = g_slist_remove(fl, fl->data);
947     }
948     if (create_new) {
949         sp_file_new_default();
950     }
952     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
953     main_instance.run();
955 #ifdef WIN32
956     //We might not need anything here
957     //sp_win32_finish(); <-- this is a NOP func
958 #endif
960     return 0;
963 /**
964  * Process file list
965  */
966 void sp_process_file_list(GSList *fl)
968     while (fl) {
969         const gchar *filename = (gchar *)fl->data;
971         SPDocument *doc = NULL;
972         try {
973             doc = Inkscape::Extension::open(NULL, filename);
974         } catch (Inkscape::Extension::Input::no_extension_found &e) {
975             doc = NULL;
976         } catch (Inkscape::Extension::Input::open_failed &e) {
977             doc = NULL;
978         }
980         if (doc == NULL) {
981             try {
982                 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
983             } catch (Inkscape::Extension::Input::no_extension_found &e) {
984                 doc = NULL;
985             } catch (Inkscape::Extension::Input::open_failed &e) {
986                 doc = NULL;
987             }
988         }
989         if (doc == NULL) {
990             g_warning("Specified document %s cannot be opened (does not exist or not a valid SVG file)", filename);
991         } else {
992             if (sp_vacuum_defs) {
993                 vacuum_document(doc);
994             }
995             if (sp_vacuum_defs && !sp_export_svg) {
996                 // save under the name given in the command line
997                 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
998             }
999             if (sp_global_printer) {
1000                 sp_print_document_to_file(doc, sp_global_printer);
1001             }
1002             if (sp_export_png || (sp_export_id && sp_export_use_hints)) {
1003                 sp_do_export_png(doc);
1004             }
1005             if (sp_export_svg) {
1006                 Inkscape::XML::Document *rdoc;
1007                 Inkscape::XML::Node *repr;
1008                 rdoc = sp_repr_document_new("svg:svg");
1009                 repr = rdoc->root();
1010                 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
1011                 sp_repr_save_rebased_file(repr->document(), sp_export_svg, SP_SVG_NS_URI,
1012                                           doc->base, sp_export_svg);
1013             }
1014             if (sp_export_ps) {
1015                 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
1016             }
1017             if (sp_export_eps) {
1018                 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
1019             }
1020             if (sp_export_pdf) {
1021                 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
1022             }
1023 #ifdef WIN32
1024             if (sp_export_emf) {
1025                 do_export_emf(doc, sp_export_emf, "image/x-emf");
1026             }
1027 #endif //WIN32
1028             if (sp_query_all) {
1029                 do_query_all (doc);
1030             } else if (sp_query_width || sp_query_height) {
1031                 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
1032             } else if (sp_query_x || sp_query_y) {
1033                 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
1034             }
1036             delete doc;
1037         }
1038         fl = g_slist_remove(fl, fl->data);
1039     }
1042 /**
1043  * Run the application as an interactive shell, parsing command lines from stdin
1044  * Returns -1 on error.
1045  */
1046 int sp_main_shell(char const* command_name)
1048     int retval = 0;
1050     const unsigned int buffer_size = 4096;
1051     gchar *command_line = g_strnfill(buffer_size, 0);
1052     g_strlcpy(command_line, command_name, buffer_size);
1053     gsize offset = g_strlcat(command_line, " ", buffer_size);
1054     gsize sizeLeft = buffer_size - offset;
1055     gchar *useme = command_line + offset;
1057     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
1058     fflush(stdout);
1059     char* linedata = 0;
1060     do {
1061         fprintf(stdout, ">");
1062         fflush(stdout);
1063         if ((linedata = fgets(useme, sizeLeft, stdin))) {
1064             size_t len = strlen(useme);
1065             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
1066                 fprintf(stdout, "ERROR: Command line too long\n");
1067                 // Consume rest of line
1068                 retval = -1; // If the while loop completes, this remains -1
1069                 while (fgets(useme, sizeLeft, stdin) && retval) {
1070                     len = strlen(command_line);
1071                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
1072                         retval = 0;
1073                     }
1074                 }
1075             } else {
1076                 useme[--len] = '\0';  // Strip newline
1077                 if (useme[len - 1] == '\r') {
1078                     useme[--len] = '\0';
1079                 }
1080                 if ( strcmp(useme, "quit") == 0 ) {
1081                     // Time to quit
1082                     fflush(stdout);
1083                     linedata = 0; // mark for exit
1084                 } else if ( len < 1 ) {
1085                     // blank string. Do nothing.
1086                 } else {
1087                     GError* parseError = 0;
1088                     gchar** argv = 0;
1089                     gint argc = 0;
1090                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
1091                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
1092                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
1093                         if ( ctx ) {
1094                             GSList *fl = sp_process_args(ctx);
1095                             sp_process_file_list(fl);
1096                             poptFreeContext(ctx);
1097                         } else {
1098                             retval = 1; // not sure why. But this was the previous return value
1099                         }
1100                         resetCommandlineGlobals();
1101                         g_strfreev(argv);
1102                     } else {
1103                         g_warning("Cannot parse commandline: %s", useme);
1104                     }
1105                 }
1106             }
1107         } // if (linedata...
1108     } while (linedata && (retval == 0));
1110     g_free(command_line);
1111     return retval;
1114 int sp_main_console(int argc, char const **argv)
1116     /* We are started in text mode */
1118     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1119      * in a non-Gtk environment.  Used in libnrtype's
1120      * FontInstance.cpp and FontFactory.cpp.
1121      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1122      */
1123     g_type_init();
1124     char **argv2 = const_cast<char **>(argv);
1125     gtk_init_check( &argc, &argv2 );
1126     //setlocale(LC_ALL, "");
1128     GSList *fl = NULL;
1129     int retVal = sp_common_main( argc, argv, &fl );
1130     g_return_val_if_fail(retVal == 0, 1);
1132     if (fl == NULL && !sp_shell) {
1133         g_print("Nothing to do!\n");
1134         exit(0);
1135     }
1137     inkscape_application_init(argv[0], false);
1139     if (sp_shell) {
1140         sp_main_shell(argv[0]); // Run as interactive shell
1141         exit(0);
1142     } else {
1143         sp_process_file_list(fl); // Normal command line invokation
1144     }
1146     return 0;
1149 static void
1150 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1152     SPObject *o = NULL;
1154     if (id) {
1155         o = doc->getObjectById(id);
1156         if (o) {
1157             if (!SP_IS_ITEM (o)) {
1158                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1159                 return;
1160             }
1161         } else {
1162             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1163             return;
1164         }
1165     } else {
1166         o = SP_DOCUMENT_ROOT(doc);
1167     }
1169     if (o) {
1170         sp_document_ensure_up_to_date (doc);
1171         SPItem *item = ((SPItem *) o);
1173         // "true" SVG bbox for scripting
1174         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1175         if (area) {
1176             Inkscape::SVGOStringStream os;
1177             if (extent) {
1178                 os << area->dimensions()[axis];
1179             } else {
1180                 os << area->min()[axis];
1181             }
1182             g_print ("%s", os.str().c_str());
1183         } else {
1184             g_print("0");
1185         }
1186     }
1189 static void
1190 do_query_all (SPDocument *doc)
1192     SPObject *o = NULL;
1194     o = SP_DOCUMENT_ROOT(doc);
1196     if (o) {
1197         sp_document_ensure_up_to_date (doc);
1198         do_query_all_recurse(o);
1199     }
1202 static void
1203 do_query_all_recurse (SPObject *o)
1205     SPItem *item = ((SPItem *) o);
1206     if (o->id && SP_IS_ITEM(item)) {
1207         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1208         if (area) {
1209             Inkscape::SVGOStringStream os;
1210             os << o->id;
1211             os << "," << area->min()[Geom::X];
1212             os << "," << area->min()[Geom::Y];
1213             os << "," << area->dimensions()[Geom::X];
1214             os << "," << area->dimensions()[Geom::Y];
1215             g_print ("%s\n", os.str().c_str());
1216         }
1217     }
1219     SPObject *child = o->children;
1220     while (child) {
1221         do_query_all_recurse (child);
1222         child = child->next;
1223     }
1227 static void
1228 sp_do_export_png(SPDocument *doc)
1230     const gchar *filename = NULL;
1231     bool filename_from_hint = false;
1232     gdouble dpi = 0.0;
1234     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1235         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1236     }
1238     GSList *items = NULL;
1240     Geom::Rect area;
1241     if (sp_export_id || sp_export_area_drawing) {
1243         SPObject *o = NULL;
1244         SPObject *o_area = NULL;
1245         if (sp_export_id && sp_export_area_drawing) {
1246             o = doc->getObjectById(sp_export_id);
1247             o_area = SP_DOCUMENT_ROOT (doc);
1248         } else if (sp_export_id) {
1249             o = doc->getObjectById(sp_export_id);
1250             o_area = o;
1251         } else if (sp_export_area_drawing) {
1252             o = SP_DOCUMENT_ROOT (doc);
1253             o_area = o;
1254         }
1256         if (o) {
1257             if (!SP_IS_ITEM (o)) {
1258                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1259                 return;
1260             }
1262             items = g_slist_prepend (items, SP_ITEM(o));
1264             if (sp_export_id_only) {
1265                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1266             }
1268             if (sp_export_use_hints) {
1270                 // retrieve export filename hint
1271                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1272                 if (fn_hint) {
1273                     if (sp_export_png) {
1274                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1275                         filename = sp_export_png;
1276                     } else {
1277                         filename = fn_hint;
1278                         filename_from_hint = true;
1279                     }
1280                 } else {
1281                     g_warning ("Export filename hint not found for the object.");
1282                     filename = sp_export_png;
1283                 }
1285                 // retrieve export dpi hints
1286                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1287                 if (dpi_hint) {
1288                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1289                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1290                     } else {
1291                         dpi = atof(dpi_hint);
1292                     }
1293                 } else {
1294                     g_warning ("Export DPI hint not found for the object.");
1295                 }
1297             }
1299             // write object bbox to area
1300             sp_document_ensure_up_to_date (doc);
1301             Geom::OptRect areaMaybe;
1302             sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1303             if (areaMaybe) {
1304                 area = *areaMaybe;
1305             } else {
1306                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1307                 return;
1308             }
1309         } else {
1310             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1311             return;
1312         }
1313     }
1315     if (sp_export_area) {
1316         /* Try to parse area (given in SVG pixels) */
1317         gdouble x0,y0,x1,y1;
1318         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1319             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1320             return;
1321         }
1322         area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1323     } else if (sp_export_area_page || !(sp_export_id || sp_export_area_drawing)) {
1324         /* Export the whole page: note: Inkscape uses 'page' in all menus and dialogs, not 'canvas' */
1325         sp_document_ensure_up_to_date (doc);
1326         Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1327         area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1328     }
1330     // set filename and dpi from options, if not yet set from the hints
1331     if (!filename) {
1332         if (!sp_export_png) {
1333             g_warning ("No export filename given and no filename hint. Nothing exported.");
1334             return;
1335         }
1336         filename = sp_export_png;
1337     }
1339     if (sp_export_dpi && dpi == 0.0) {
1340         dpi = atof(sp_export_dpi);
1341         if ((dpi < 0.1) || (dpi > 10000.0)) {
1342             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1343             return;
1344         }
1345         g_print("DPI: %g\n", dpi);
1346     }
1348     if (sp_export_area_snap) {
1349         round_rectangle_outwards(area);
1350     }
1352     // default dpi
1353     if (dpi == 0.0) {
1354         dpi = PX_PER_IN;
1355     }
1357     unsigned long int width = 0;
1358     unsigned long int height = 0;
1360     if (sp_export_width) {
1361         errno=0;
1362         width = strtoul(sp_export_width, NULL, 0);
1363         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1364             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1365             return;
1366         }
1367         dpi = (gdouble) width * PX_PER_IN / area.width();
1368     }
1370     if (sp_export_height) {
1371         errno=0;
1372         height = strtoul(sp_export_height, NULL, 0);
1373         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1374             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1375             return;
1376         }
1377         dpi = (gdouble) height * PX_PER_IN / area.height();
1378     }
1380     if (!sp_export_width) {
1381         width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1382     }
1384     if (!sp_export_height) {
1385         height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1386     }
1388     guint32 bgcolor = 0x00000000;
1389     if (sp_export_background) {
1390         // override the page color
1391         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1392         bgcolor |= 0xff; // default is no opacity
1393     } else {
1394         // read from namedview
1395         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1396         if (nv && nv->attribute("pagecolor"))
1397             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1398         if (nv && nv->attribute("inkscape:pageopacity"))
1399             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1400     }
1402     if (sp_export_background_opacity) {
1403         // override opacity
1404         gfloat value;
1405         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1406             if (value > 1.0) {
1407                 value = CLAMP (value, 1.0f, 255.0f);
1408                 bgcolor &= (guint32) 0xffffff00;
1409                 bgcolor |= (guint32) floor(value);
1410             } else {
1411                 value = CLAMP (value, 0.0f, 1.0f);
1412                 bgcolor &= (guint32) 0xffffff00;
1413                 bgcolor |= SP_COLOR_F_TO_U(value);
1414             }
1415         }
1416     }
1418     gchar *path = 0;
1419     if (filename_from_hint) {
1420         //Make relative paths go from the document location, if possible:
1421         if (!g_path_is_absolute(filename) && doc->uri) {
1422             gchar *dirname = g_path_get_dirname(doc->uri);
1423             if (dirname) {
1424                 path = g_build_filename(dirname, filename, NULL);
1425                 g_free(dirname);
1426             }
1427         }
1428         if (!path) {
1429             path = g_strdup(filename);
1430         }
1431     } else {
1432         path = g_strdup(filename);
1433     }
1435     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1437     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);
1439     g_print("Bitmap saved as: %s\n", filename);
1441     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1442         sp_export_png_file(doc, path, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1443     } else {
1444         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1445     }
1447     g_free (path);
1448     g_slist_free (items);
1452 /**
1453  *  Perform a PDF/PS/EPS export
1454  *
1455  *  \param doc Document to export.
1456  *  \param uri URI to export to.
1457  *  \param mime MIME type to export as.
1458  */
1460 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1462     Inkscape::Extension::DB::OutputList o;
1463     Inkscape::Extension::db.get_output_list(o);
1464     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1465     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1466         i++;
1467     }
1469     if (i == o.end())
1470     {
1471         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1472         return;
1473     }
1475     if (sp_export_id) {
1476         SPObject *o = doc->getObjectById(sp_export_id);
1477         if (o == NULL) {
1478             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1479             return;
1480         }
1481         (*i)->set_param_string ("exportId", sp_export_id);
1482     } else {
1483         (*i)->set_param_string ("exportId", "");
1484     }
1486     if (sp_export_area_page && sp_export_area_drawing) {
1487         g_warning ("You cannot use --export-area-page and --export-area-drawing at the same time; only the former will take effect.");
1488         sp_export_area_drawing = false;
1489     }
1491     if (sp_export_area_drawing) {
1492         (*i)->set_param_bool ("areaDrawing", TRUE);
1493     } else {
1494         (*i)->set_param_bool ("areaDrawing", FALSE);
1495     }
1497     if (sp_export_area_page) {
1498         if (sp_export_eps) {
1499             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.");
1500         }
1501         (*i)->set_param_bool ("areaPage", TRUE);
1502     } else {
1503         (*i)->set_param_bool ("areaPage", FALSE);
1504     }
1506     if (!sp_export_area_drawing && !sp_export_area_page && !sp_export_id) {
1507         // neither is set, set page as default for ps/pdf and drawing for eps
1508         if (sp_export_eps) {
1509             try {
1510                (*i)->set_param_bool("areaDrawing", TRUE);
1511             } catch (...) {}
1512         }
1513     }
1515     if (sp_export_text_to_path) {
1516         (*i)->set_param_bool("textToPath", TRUE);
1517     } else {
1518         (*i)->set_param_bool("textToPath", FALSE);
1519     }
1521     if (sp_export_ignore_filters) {
1522         (*i)->set_param_bool("blurToBitmap", FALSE);
1523     } else {
1524         (*i)->set_param_bool("blurToBitmap", TRUE);
1526         gdouble dpi = 90.0;
1527         if (sp_export_dpi) {
1528             dpi = atof(sp_export_dpi);
1529             if ((dpi < 1) || (dpi > 10000.0)) {
1530                 g_warning("DPI value %s out of range [1 - 10000]. Using 90 dpi instead.", sp_export_dpi);
1531                 dpi = 90;
1532             }
1533         }
1535         (*i)->set_param_int("resolution", (int) dpi);
1536     }
1538     (*i)->save(doc, uri);
1541 #ifdef WIN32
1542 /**
1543  *  Export a document to EMF
1544  *
1545  *  \param doc Document to export.
1546  *  \param uri URI to export to.
1547  *  \param mime MIME type to export as (should be "image/x-emf")
1548  */
1550 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1552     Inkscape::Extension::DB::OutputList o;
1553     Inkscape::Extension::db.get_output_list(o);
1554     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1555     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1556         i++;
1557     }
1559     if (i == o.end())
1560     {
1561         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1562         return;
1563     }
1565     (*i)->save(doc, uri);
1567 #endif //WIN32
1569 #ifdef WIN32
1570 bool replaceArgs( int& argc, char**& argv )
1572     bool worked = false;
1574 #ifdef REPLACEARGS_DEBUG
1575     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1576 #endif // REPLACEARGS_DEBUG
1578     wchar_t* line = GetCommandLineW();
1579     if ( line )
1580     {
1581 #ifdef REPLACEARGS_DEBUG
1582         {
1583             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1584             if ( utf8Line )
1585             {
1586                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1587                 {
1588                     char tmp[strlen(safe) + 32];
1589                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1590                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1591                 }
1592             }
1593         }
1594 #endif // REPLACEARGS_DEBUG
1596         int numArgs = 0;
1597         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1599 #ifdef REPLACEARGS_ANSI
1600 // test code for trying things on Win95/98/ME
1601         if ( !parsed )
1602         {
1603 #ifdef REPLACEARGS_DEBUG
1604             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1605 #endif // REPLACEARGS_DEBUG
1606             int lineLen = wcslen(line) + 1;
1607             wchar_t* lineDup = new wchar_t[lineLen];
1608             wcsncpy( lineDup, line, lineLen );
1610             int pos = 0;
1611             bool inQuotes = false;
1612             bool inWhitespace = true;
1613             std::vector<int> places;
1614             while ( lineDup[pos] )
1615             {
1616                 if ( inQuotes )
1617                 {
1618                     if ( lineDup[pos] == L'"' )
1619                     {
1620                         inQuotes = false;
1621                     }
1622                 }
1623                 else if ( lineDup[pos] == L'"' )
1624                 {
1625                     inQuotes = true;
1626                     inWhitespace = false;
1627                     places.push_back(pos);
1628                 }
1629                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1630                 {
1631                     if ( !inWhitespace )
1632                     {
1633                         inWhitespace = true;
1634                         lineDup[pos] = 0;
1635                     }
1636                 }
1637                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1638                 {
1639                     inWhitespace = false;
1640                     places.push_back(pos);
1641                 }
1642                 else
1643                 {
1644                     // consume
1645                 }
1646                 pos++;
1647             }
1648 #ifdef REPLACEARGS_DEBUG
1649             {
1650                 char tmp[256];
1651                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1652                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1653             }
1654 #endif // REPLACEARGS_DEBUG
1656             wchar_t** block = new wchar_t*[places.size()];
1657             int i = 0;
1658             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1659             {
1660                 block[i++] = &lineDup[*it];
1661             }
1662             parsed = block;
1663             numArgs = places.size();
1664         }
1665 #endif // REPLACEARGS_ANSI
1667         if ( parsed )
1668         {
1669             std::vector<wchar_t*>expandedArgs;
1670             if ( numArgs > 0 )
1671             {
1672                 expandedArgs.push_back( parsed[0] );
1673             }
1675             for ( int i1 = 1; i1 < numArgs; i1++ )
1676             {
1677                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1678                 wildcarded &= parsed[i1][0] != L'"';
1679                 wildcarded &= parsed[i1][0] != L'-';
1680                 if ( wildcarded )
1681                 {
1682 #ifdef REPLACEARGS_ANSI
1683                     WIN32_FIND_DATAA data;
1684 #else
1685                     WIN32_FIND_DATAW data;
1686 #endif // REPLACEARGS_ANSI
1688                     memset((void *)&data, 0, sizeof(data));
1690                     int baseLen = wcslen(parsed[i1]) + 2;
1691                     wchar_t* base = new wchar_t[baseLen];
1692                     wcsncpy( base, parsed[i1], baseLen );
1693                     wchar_t* last = wcsrchr( base, L'\\' );
1694                     if ( last )
1695                     {
1696                         last[1] = 0;
1697                     }
1698                     else
1699                     {
1700                         base[0] = 0;
1701                     }
1702                     baseLen = wcslen( base );
1704 #ifdef REPLACEARGS_ANSI
1705                     char target[MAX_PATH];
1706                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1707                     {
1708                         HANDLE hf = FindFirstFileA( target, &data );
1709 #else
1710                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1711 #endif // REPLACEARGS_ANSI
1712                         if ( hf != INVALID_HANDLE_VALUE )
1713                         {
1714                             BOOL found = TRUE;
1715                             do
1716                             {
1717 #ifdef REPLACEARGS_ANSI
1718                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1719                                 if ( howMany > 0 )
1720                                 {
1721                                     howMany += baseLen;
1722                                     wchar_t* tmp = new wchar_t[howMany + 1];
1723                                     wcsncpy( tmp, base, howMany + 1 );
1724                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1725                                     expandedArgs.push_back( tmp );
1726                                     found = FindNextFileA( hf, &data );
1727                                 }
1728 #else
1729                                 int howMany = wcslen(data.cFileName) + baseLen;
1730                                 wchar_t* tmp = new wchar_t[howMany + 1];
1731                                 wcsncpy( tmp, base, howMany + 1 );
1732                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1733                                 expandedArgs.push_back( tmp );
1734                                 found = FindNextFileW( hf, &data );
1735 #endif // REPLACEARGS_ANSI
1736                             } while ( found );
1738                             FindClose( hf );
1739                         }
1740                         else
1741                         {
1742                             expandedArgs.push_back( parsed[i1] );
1743                         }
1744 #ifdef REPLACEARGS_ANSI
1745                     }
1746 #endif // REPLACEARGS_ANSI
1748                     delete[] base;
1749                 }
1750                 else
1751                 {
1752                     expandedArgs.push_back( parsed[i1] );
1753                 }
1754             }
1756             {
1757                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1758                 int iz = 0;
1759                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1760                 {
1761                     block[iz++] = *it;
1762                 }
1763                 parsed = block;
1764                 numArgs = expandedArgs.size();
1765             }
1767             std::vector<gchar*> newArgs;
1768             for ( int i = 0; i < numArgs; i++ )
1769             {
1770                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1771                 if ( replacement )
1772                 {
1773 #ifdef REPLACEARGS_DEBUG
1774                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1776                     if ( safe2 )
1777                     {
1778                         {
1779                             char tmp[1024];
1780                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1781                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1782                         }
1783                         g_free( safe2 );
1784                     }
1785 #endif // REPLACEARGS_DEBUG
1787                     newArgs.push_back( replacement );
1788                 }
1789                 else
1790                 {
1791                     newArgs.push_back( blankParam );
1792                 }
1793             }
1795             // Now push our munged params to be the new argv and argc
1796             {
1797                 char** block = new char*[newArgs.size()];
1798                 int iz = 0;
1799                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1800                 {
1801                     block[iz++] = *it;
1802                 }
1803                 argv = block;
1804                 argc = newArgs.size();
1805                 worked = true;
1806             }
1807         }
1808 #ifdef REPLACEARGS_DEBUG
1809         else
1810         {
1811             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1812         }
1813 #endif // REPLACEARGS_DEBUG
1814     }
1815 #ifdef REPLACEARGS_DEBUG
1816     else
1817     {
1818         {
1819             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1820         }
1822         char* line2 = GetCommandLineA();
1823         if ( line2 )
1824         {
1825             gchar *safe = Inkscape::IO::sanitizeString(line2);
1826             {
1827                 {
1828                     char tmp[strlen(safe) + 32];
1829                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1830                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1831                 }
1832             }
1833         }
1834         else
1835         {
1836             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1837         }
1838     }
1839 #endif // REPLACEARGS_DEBUG
1841     return worked;
1843 #endif // WIN32
1845 static GSList *
1846 sp_process_args(poptContext ctx)
1848     GSList *fl = NULL;
1850     gint a;
1851     while ((a = poptGetNextOpt(ctx)) != -1) {
1852         switch (a) {
1853             case SP_ARG_FILE: {
1854                 gchar const *fn = poptGetOptArg(ctx);
1855                 if (fn != NULL) {
1856                     fl = g_slist_append(fl, g_strdup(fn));
1857                 }
1858                 break;
1859             }
1860             case SP_ARG_VERSION: {
1861                 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1862                 exit(0);
1863                 break;
1864             }
1865             case SP_ARG_EXTENSIONDIR: {
1866                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1867                 exit(0);
1868                 break;
1869             }
1870             case SP_ARG_VERB_LIST: {
1871                 // This really shouldn't go here, we should init the app.
1872                 // But, since we're just exiting in this path, there is
1873                 // no harm, and this is really a better place to put
1874                 // everything else.
1875                 Inkscape::Extension::init();
1876                 Inkscape::Verb::list();
1877                 exit(0);
1878                 break;
1879             }
1880             case SP_ARG_VERB:
1881             case SP_ARG_SELECT: {
1882                 gchar const *arg = poptGetOptArg(ctx);
1883                 if (arg != NULL) {
1884                     // printf("Adding in: %s\n", arg);
1885                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1886                 }
1887                 break;
1888             }
1889             case POPT_ERROR_BADOPT: {
1890                 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1891                 exit(1);
1892                 break;
1893             }
1894             default: {
1895                 break;
1896             }
1897         }
1898     }
1900     gchar const ** const args = poptGetArgs(ctx);
1901     if (args != NULL) {
1902         for (unsigned i = 0; args[i] != NULL; i++) {
1903             fl = g_slist_append(fl, g_strdup(args[i]));
1904         }
1905     }
1907     return fl;
1911 /*
1912   Local Variables:
1913   mode:c++
1914   c-file-style:"stroustrup"
1915   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1916   indent-tabs-mode:nil
1917   fill-column:99
1918   End:
1919 */
1920 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :