Code

Improved version reporting. Add SVN revision and custom status to
[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 #include <gtk/gtkmessagedialog.h>
33 #ifdef HAVE_IEEEFP_H
34 #include <ieeefp.h>
35 #endif
36 #include <cstring>
37 #include <string>
38 #include <locale.h>
39 #include <stdlib.h>
41 #include <popt.h>
42 #ifndef POPT_TABLEEND
43 #define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
44 #endif /* Not def: POPT_TABLEEND */
46 #include <libxml/tree.h>
47 #include <glib-object.h>
48 #include <gtk/gtkmain.h>
49 #include <gtk/gtksignal.h>
50 #include <gtk/gtkwindow.h>
51 #include <gtk/gtkbox.h>
53 #include "gc-core.h"
55 #include "macros.h"
56 #include "file.h"
57 #include "document.h"
58 #include "sp-object.h"
59 #include "interface.h"
60 #include "print.h"
61 #include "color.h"
62 #include "sp-item.h"
63 #include "sp-root.h"
64 #include "unit-constants.h"
66 #include "svg/svg.h"
67 #include "svg/svg-color.h"
68 #include "svg/stringstream.h"
70 #include "inkscape-private.h"
71 #include "inkscape-stock.h"
72 #include "inkscape-version.h"
74 #include "sp-namedview.h"
75 #include "sp-guide.h"
76 #include "sp-object-repr.h"
77 #include "xml/repr.h"
79 #include "io/sys.h"
81 #include "debug/logger.h"
82 #include "debug/log-display-config.h"
84 #include "helper/png-write.h"
85 #include "helper/geom.h"
87 #include <extension/extension.h>
88 #include <extension/system.h>
89 #include <extension/db.h>
90 #include <extension/output.h>
92 #ifdef WIN32
93 //#define REPLACEARGS_ANSI
94 //#define REPLACEARGS_DEBUG
96 #include "registrytool.h"
98 #include "extension/internal/win32.h"
99 using Inkscape::Extension::Internal::PrintWin32;
101 #endif // WIN32
103 #include "extension/init.h"
105 #include <glibmm/i18n.h>
106 #include <gtkmm/main.h>
108 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
109 #define bind_textdomain_codeset(p,c)
110 #endif
112 #include "application/application.h"
114 #include "main-cmdlineact.h"
116 #include <png.h>
117 #include <errno.h>
119 enum {
120     SP_ARG_NONE,
121     SP_ARG_NOGUI,
122     SP_ARG_GUI,
123     SP_ARG_FILE,
124     SP_ARG_PRINT,
125     SP_ARG_EXPORT_PNG,
126     SP_ARG_EXPORT_DPI,
127     SP_ARG_EXPORT_AREA,
128     SP_ARG_EXPORT_AREA_DRAWING,
129     SP_ARG_EXPORT_AREA_CANVAS,
130     SP_ARG_EXPORT_AREA_SNAP,
131     SP_ARG_EXPORT_WIDTH,
132     SP_ARG_EXPORT_HEIGHT,
133     SP_ARG_EXPORT_ID,
134     SP_ARG_EXPORT_ID_ONLY,
135     SP_ARG_EXPORT_USE_HINTS,
136     SP_ARG_EXPORT_BACKGROUND,
137     SP_ARG_EXPORT_BACKGROUND_OPACITY,
138     SP_ARG_EXPORT_SVG,
139     SP_ARG_EXPORT_PS,
140     SP_ARG_EXPORT_EPS,
141     SP_ARG_EXPORT_PDF,
142 #ifdef WIN32
143     SP_ARG_EXPORT_EMF,
144 #endif //WIN32
145     SP_ARG_EXPORT_TEXT_TO_PATH,
146     SP_ARG_EXPORT_IGNORE_FILTERS,
147     SP_ARG_EXTENSIONDIR,
148     SP_ARG_QUERY_X,
149     SP_ARG_QUERY_Y,
150     SP_ARG_QUERY_WIDTH,
151     SP_ARG_QUERY_HEIGHT,
152     SP_ARG_QUERY_ALL,
153     SP_ARG_QUERY_ID,
154     SP_ARG_SHELL,
155     SP_ARG_VERSION,
156     SP_ARG_VACUUM_DEFS,
157     SP_ARG_VERB_LIST,
158     SP_ARG_VERB,
159     SP_ARG_SELECT,
160     SP_ARG_LAST
161 };
163 int sp_main_gui(int argc, char const **argv);
164 int sp_main_console(int argc, char const **argv);
165 static void sp_do_export_png(SPDocument *doc);
166 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime);
167 #ifdef WIN32
168 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
169 #endif //WIN32
170 static void do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id);
171 static void do_query_all (SPDocument *doc);
172 static void do_query_all_recurse (SPObject *o);
174 static gchar *sp_global_printer = NULL;
175 static gchar *sp_export_png = NULL;
176 static gchar *sp_export_dpi = NULL;
177 static gchar *sp_export_area = NULL;
178 static gboolean sp_export_area_drawing = FALSE;
179 static gboolean sp_export_area_canvas = FALSE;
180 static gchar *sp_export_width = NULL;
181 static gchar *sp_export_height = NULL;
182 static gchar *sp_export_id = NULL;
183 static gchar *sp_export_background = NULL;
184 static gchar *sp_export_background_opacity = NULL;
185 static gboolean sp_export_area_snap = FALSE;
186 static gboolean sp_export_use_hints = FALSE;
187 static gboolean sp_export_id_only = FALSE;
188 static gchar *sp_export_svg = NULL;
189 static gchar *sp_export_ps = NULL;
190 static gchar *sp_export_eps = NULL;
191 static gchar *sp_export_pdf = NULL;
192 #ifdef WIN32
193 static gchar *sp_export_emf = NULL;
194 #endif //WIN32
195 static gboolean sp_export_text_to_path = FALSE;
196 static gboolean sp_export_ignore_filters = FALSE;
197 static gboolean sp_export_font = FALSE;
198 static gboolean sp_query_x = FALSE;
199 static gboolean sp_query_y = FALSE;
200 static gboolean sp_query_width = FALSE;
201 static gboolean sp_query_height = FALSE;
202 static gboolean sp_query_all = FALSE;
203 static gchar *sp_query_id = NULL;
204 static int sp_new_gui = FALSE;
205 static gboolean sp_shell = FALSE;
206 static gboolean sp_vacuum_defs = FALSE;
208 static gchar *sp_export_png_utf8 = NULL;
209 static gchar *sp_export_svg_utf8 = NULL;
210 static gchar *sp_global_printer_utf8 = NULL;
213 /**
214  *  Reset variables to default values.
215  */
216 static void resetCommandlineGlobals() {
217         sp_global_printer = NULL;
218         sp_export_png = NULL;
219         sp_export_dpi = NULL;
220         sp_export_area = NULL;
221         sp_export_area_drawing = FALSE;
222         sp_export_area_canvas = FALSE;
223         sp_export_width = NULL;
224         sp_export_height = NULL;
225         sp_export_id = NULL;
226         sp_export_background = NULL;
227         sp_export_background_opacity = NULL;
228         sp_export_area_snap = FALSE;
229         sp_export_use_hints = FALSE;
230         sp_export_id_only = FALSE;
231         sp_export_svg = NULL;
232         sp_export_ps = NULL;
233         sp_export_eps = NULL;
234         sp_export_pdf = NULL;
235 #ifdef WIN32
236         sp_export_emf = NULL;
237 #endif //WIN32
238         sp_export_text_to_path = FALSE;
239         sp_export_ignore_filters = FALSE;
240         sp_export_font = FALSE;
241         sp_query_x = FALSE;
242         sp_query_y = FALSE;
243         sp_query_width = FALSE;
244         sp_query_height = FALSE;
245         sp_query_all = FALSE;
246         sp_query_id = NULL;
247         sp_vacuum_defs = FALSE;
249         sp_export_png_utf8 = NULL;
250         sp_export_svg_utf8 = NULL;
251         sp_global_printer_utf8 = NULL;
254 #ifdef WIN32
255 static bool replaceArgs( int& argc, char**& argv );
256 #endif
257 static GSList *sp_process_args(poptContext ctx);
258 struct poptOption options[] = {
259     {"version", 'V',
260      POPT_ARG_NONE, NULL, SP_ARG_VERSION,
261      N_("Print the Inkscape version number"),
262      NULL},
264     {"without-gui", 'z',
265      POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
266      N_("Do not use X server (only process files from console)"),
267      NULL},
269     {"with-gui", 'g',
270      POPT_ARG_NONE, NULL, SP_ARG_GUI,
271      N_("Try to use X server (even if $DISPLAY is not set)"),
272      NULL},
274     {"file", 'f',
275      POPT_ARG_STRING, NULL, SP_ARG_FILE,
276      N_("Open specified document(s) (option string may be excluded)"),
277      N_("FILENAME")},
279     {"print", 'p',
280      POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
281      N_("Print document(s) to specified output file (use '| program' for pipe)"),
282      N_("FILENAME")},
284     {"export-png", 'e',
285      POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
286      N_("Export document to a PNG file"),
287      N_("FILENAME")},
289     {"export-dpi", 'd',
290      POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
291      N_("The resolution used for exporting SVG into bitmap (default 90)"),
292      N_("DPI")},
294     {"export-area", 'a',
295      POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
296      N_("Exported area in SVG user units (default is the canvas; 0,0 is lower-left corner)"),
297      N_("x0:y0:x1:y1")},
299     {"export-area-drawing", 'D',
300      POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
301      N_("Exported area is the entire drawing (not canvas)"),
302      NULL},
304     {"export-area-canvas", 'C',
305      POPT_ARG_NONE, &sp_export_area_canvas, SP_ARG_EXPORT_AREA_CANVAS,
306      N_("Exported area is the entire canvas"),
307      NULL},
309     {"export-area-snap", 0,
310      POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
311      N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
312      NULL},
314     {"export-width", 'w',
315      POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
316      N_("The width of exported bitmap in pixels (overrides export-dpi)"),
317      N_("WIDTH")},
319     {"export-height", 'h',
320      POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
321      N_("The height of exported bitmap in pixels (overrides export-dpi)"),
322      N_("HEIGHT")},
324     {"export-id", 'i',
325      POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
326      N_("The ID of the object to export"),
327      N_("ID")},
329     {"export-id-only", 'j',
330      POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
331      // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
332      //  See "man inkscape" for details.
333      N_("Export just the object with export-id, hide all others (only with export-id)"),
334      NULL},
336     {"export-use-hints", 't',
337      POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
338      N_("Use stored filename and DPI hints when exporting (only with export-id)"),
339      NULL},
341     {"export-background", 'b',
342      POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
343      N_("Background color of exported bitmap (any SVG-supported color string)"),
344      N_("COLOR")},
346     {"export-background-opacity", 'y',
347      POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
348      N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
349      N_("VALUE")},
351     {"export-plain-svg", 'l',
352      POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
353      N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
354      N_("FILENAME")},
356     {"export-ps", 'P',
357      POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
358      N_("Export document to a PS file"),
359      N_("FILENAME")},
361     {"export-eps", 'E',
362      POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
363      N_("Export document to an EPS file"),
364      N_("FILENAME")},
366     {"export-pdf", 'A',
367      POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
368      N_("Export document to a PDF file"),
369      N_("FILENAME")},
371 #ifdef WIN32
372     {"export-emf", 'M',
373      POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
374      N_("Export document to an Enhanced Metafile (EMF) File"),
375      N_("FILENAME")},
376 #endif //WIN32
378     {"export-text-to-path", 'T',
379      POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
380      N_("Convert text object to paths on export (PS, EPS, PDF)"),
381      NULL},
383     {"export-ignore-filters", 0,
384      POPT_ARG_NONE, &sp_export_ignore_filters, SP_ARG_EXPORT_IGNORE_FILTERS,
385      N_("Render filtered objects without filters, instead of rasterizing (PS, EPS, PDF)"),
386      NULL},
388     {"query-x", 'X',
389      POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
390      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
391      N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
392      NULL},
394     {"query-y", 'Y',
395      POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
396      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
397      N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
398      NULL},
400     {"query-width", 'W',
401      POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
402      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
403      N_("Query the width of the drawing or, if specified, of the object with --query-id"),
404      NULL},
406     {"query-height", 'H',
407      POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
408      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
409      N_("Query the height of the drawing or, if specified, of the object with --query-id"),
410      NULL},
412     {"query-all", 'S',
413      POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
414      N_("List id,x,y,w,h for all objects"),
415      NULL},
417     {"query-id", 'I',
418      POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
419      N_("The ID of the object whose dimensions are queried"),
420      N_("ID")},
422     {"extension-directory", 'x',
423      POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
424      // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
425      N_("Print out the extension directory and exit"),
426      NULL},
428     {"vacuum-defs", 0,
429      POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
430      N_("Remove unused definitions from the defs section(s) of the document"),
431      NULL},
433     {"verb-list", 0,
434      POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
435      N_("List the IDs of all the verbs in Inkscape"),
436      NULL},
438     {"verb", 0,
439      POPT_ARG_STRING, NULL, SP_ARG_VERB,
440      N_("Verb to call when Inkscape opens."),
441      N_("VERB-ID")},
443     {"select", 0,
444      POPT_ARG_STRING, NULL, SP_ARG_SELECT,
445      N_("Object ID to select when Inkscape opens."),
446      N_("OBJECT-ID")},
448     {"shell", 0,
449      POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
450      N_("Start Inkscape in interative shell mode."),
451      NULL},
453     POPT_AUTOHELP POPT_TABLEEND
454 };
456 static bool needToRecodeParams = true;
457 gchar * blankParam = g_strdup("");
461 #ifdef WIN32
463 /**
464  * Return the directory of the .exe that is currently running
465  */
466 static Glib::ustring _win32_getExePath()
468     char exeName[MAX_PATH+1];
469     GetModuleFileName(NULL, exeName, MAX_PATH);
470     char *slashPos = strrchr(exeName, '\\');
471     if (slashPos)
472         *slashPos = '\0';
473     Glib::ustring s = exeName;
474     return s;
477 /**
478  * Set up the PATH and PYTHONPATH environment variables on
479  * win32
480  */
481 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
484     char *oldenv = getenv("PATH");
485     Glib::ustring tmp = "PATH=";
486     tmp += exePath;
487     tmp += ";";
488     tmp += exePath;
489     tmp += "\\python;";
490     tmp += exePath;
491     tmp += "\\python\\Scripts;";  // for uniconv.cmd
492     tmp += exePath;
493     tmp += "\\perl";
494     if(oldenv != NULL) {
495         tmp += ";";
496         tmp += oldenv;
497     }
498     _putenv(tmp.c_str());
500     oldenv = getenv("PYTHONPATH");
501     tmp = "PYTHONPATH=";
502     tmp += exePath;
503     tmp += "\\python;";
504     tmp += exePath;
505     tmp += "\\python\\Lib;";
506     tmp += exePath;
507     tmp += "\\python\\DLLs";
508     if(oldenv != NULL) {
509         tmp += ";";
510         tmp += oldenv;
511     }
512     _putenv(tmp.c_str());
514     return 0;
516 #endif
518 /**
519  * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
520  * can find inkex.py et al. (Bug #197475)
521  */
522 static int set_extensions_env()
524     char *oldenv = getenv("PYTHONPATH");
525     Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
526     if (oldenv != NULL) {
527         tmp += G_SEARCHPATH_SEPARATOR;
528         tmp += oldenv;
529     }
530     g_setenv("PYTHONPATH", tmp.c_str(), TRUE);
531     
532     return 0;
536 /**
537  * This is the classic main() entry point of the program, though on some
538  * architectures it might be called by something else.
539  */
540 int
541 main(int argc, char **argv)
543 #ifdef HAVE_FPSETMASK
544     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
545        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
546        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
547     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
548 #endif
550 #ifdef WIN32
551     /*
552       Set the current directory to the directory of the
553       executable.  This seems redundant, but is needed for
554       when inkscape.exe is executed from another directory.
555       We use relative paths on win32.
556       HKCR\svgfile\shell\open\command is a good example
557     */
558     Glib::ustring homedir = _win32_getExePath();
559     SetCurrentDirectory(homedir.c_str());
560     _win32_set_inkscape_env(homedir);
561     RegistryTool rt;
562     rt.setPathInfo();
563 #endif
565     // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
566     Gtk::Main::init_gtkmm_internals();
568     // Bug #197475
569     set_extensions_env();
571    /**
572     * Call bindtextdomain() for various machines's paths
573     */
574 #ifdef ENABLE_NLS
575 #ifdef WIN32
576     Glib::ustring localePath = homedir;
577     localePath += "\\";
578     localePath += PACKAGE_LOCALE_DIR;
579     bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
580 #else
581 #ifdef ENABLE_BINRELOC
582     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
583 #else
584     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
585 #endif
586 #endif
587     // Allow the user to override the locale directory by setting
588     // the environment variable INKSCAPE_LOCALEDIR.
589     char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
590     if (inkscape_localedir != NULL) {
591         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
592     }
593 #endif
595     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
597 #ifdef ENABLE_NLS
598     textdomain(GETTEXT_PACKAGE);
599 #endif
601     LIBXML_TEST_VERSION
603     Inkscape::GC::init();
605     Inkscape::Debug::Logger::init();
607     gboolean use_gui;
609 #ifndef WIN32
610     use_gui = (getenv("DISPLAY") != NULL);
611 #else
612     use_gui = TRUE;
613 #endif
614     /* Test whether with/without GUI is forced */
615     for (int i = 1; i < argc; i++) {
616         if (!strcmp(argv[i], "-z")
617             || !strcmp(argv[i], "--without-gui")
618             || !strcmp(argv[i], "-p")
619             || !strncmp(argv[i], "--print", 7)
620             || !strcmp(argv[i], "-e")
621             || !strncmp(argv[i], "--export-png", 12)
622             || !strcmp(argv[i], "-l")
623             || !strncmp(argv[i], "--export-plain-svg", 12)
624             || !strcmp(argv[i], "-i")
625             || !strncmp(argv[i], "--export-area-drawing", 21)
626             || !strcmp(argv[i], "-D")
627             || !strncmp(argv[i], "--export-area-canvas", 20)
628             || !strcmp(argv[i], "-C")
629             || !strncmp(argv[i], "--export-id", 12)
630             || !strcmp(argv[i], "-P")
631             || !strncmp(argv[i], "--export-ps", 11)
632             || !strcmp(argv[i], "-E")
633             || !strncmp(argv[i], "--export-eps", 12)
634             || !strcmp(argv[i], "-A")
635             || !strncmp(argv[i], "--export-pdf", 12)
636 #ifdef WIN32
637             || !strcmp(argv[i], "-M")
638             || !strncmp(argv[i], "--export-emf", 12)
639 #endif //WIN32
640             || !strcmp(argv[i], "-W")
641             || !strncmp(argv[i], "--query-width", 13)
642             || !strcmp(argv[i], "-H")
643             || !strncmp(argv[i], "--query-height", 14)
644             || !strcmp(argv[i], "-S")
645             || !strncmp(argv[i], "--query-all", 11)
646             || !strcmp(argv[i], "-X")
647             || !strncmp(argv[i], "--query-x", 13)
648             || !strcmp(argv[i], "-Y")
649             || !strncmp(argv[i], "--query-y", 14)
650             || !strcmp(argv[i], "--vacuum-defs")
651             || !strncmp(argv[i], "--shell", 7)
652            )
653         {
654             /* main_console handles any exports -- not the gui */
655             use_gui = FALSE;
656             break;
657         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
658             use_gui = TRUE;
659             break;
660         }
661     }
663 #ifdef WIN32
664 #ifndef REPLACEARGS_ANSI
665     if ( PrintWin32::is_os_wide() )
666 #endif // REPLACEARGS_ANSI
667     {
668         // If the call fails, we'll need to convert charsets
669         needToRecodeParams = !replaceArgs( argc, argv );
670     }
671 #endif // WIN32
673     /// \todo  Should this be a static object (see inkscape.cpp)?
674     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
676     return app.run();
682 void fixupSingleFilename( gchar **orig, gchar **spare )
684     if ( orig && *orig && **orig ) {
685         GError *error = NULL;
686         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
687         if ( newFileName )
688         {
689             *orig = newFileName;
690             if ( spare ) {
691                 *spare = newFileName;
692             }
693 //             g_message("Set a replacement fixup");
694         }
695     }
700 GSList *fixupFilenameEncoding( GSList* fl )
702     GSList *newFl = NULL;
703     while ( fl ) {
704         gchar *fn = static_cast<gchar*>(fl->data);
705         fl = g_slist_remove( fl, fl->data );
706         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
707         if ( newFileName ) {
709             if ( 0 )
710             {
711                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
712                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
713                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
714                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
715                 gtk_dialog_run (GTK_DIALOG (w));
716                 gtk_widget_destroy (w);
717                 g_free(safeNewFn);
718                 g_free(safeFn);
719             }
721             g_free( fn );
722             fn = newFileName;
723             newFileName = 0;
724         }
725         else
726             if ( 0 )
727         {
728             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
729             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
730             gtk_dialog_run (GTK_DIALOG (w));
731             gtk_widget_destroy (w);
732             g_free(safeFn);
733         }
734         newFl = g_slist_append( newFl, fn );
735     }
736     return newFl;
739 int sp_common_main( int argc, char const **argv, GSList **flDest )
741     /// \todo fixme: Move these to some centralized location (Lauris)
742     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
743     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
746     // temporarily switch gettext encoding to locale, so that help messages can be output properly
747     gchar const *charset;
748     g_get_charset(&charset);
750     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
752     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
753     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
754     g_return_val_if_fail(ctx != NULL, 1);
756     /* Collect own arguments */
757     GSList *fl = sp_process_args(ctx);
758     poptFreeContext(ctx);
760     // now switch gettext back to UTF-8 (for GUI)
761     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
763     // Now let's see if the file list still holds up
764     if ( needToRecodeParams )
765     {
766         fl = fixupFilenameEncoding( fl );
767     }
769     // Check the globals for filename-fixup
770     if ( needToRecodeParams )
771     {
772         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
773         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
774         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
775     }
776     else
777     {
778         if ( sp_export_png )
779             sp_export_png_utf8 = g_strdup( sp_export_png );
780         if ( sp_export_svg )
781             sp_export_svg_utf8 = g_strdup( sp_export_svg );
782         if ( sp_global_printer )
783             sp_global_printer_utf8 = g_strdup( sp_global_printer );
784     }
786     // Return the list if wanted, else free it up.
787     if ( flDest ) {
788         *flDest = fl;
789         fl = 0;
790     } else {
791         while ( fl ) {
792             g_free( fl->data );
793             fl = g_slist_remove( fl, fl->data );
794         }
795     }
796     return 0;
799 static void
800 snooper(GdkEvent *event, gpointer /*data*/) {
801     if(inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
802     {
803         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
804         switch (event->type) {
805             case GDK_MOTION_NOTIFY:
806                 if(event->motion.state & mapping) {
807                     event->motion.state|=GDK_MOD1_MASK;
808                 }
809                 break;
810             case GDK_BUTTON_PRESS:
811                 if(event->button.state & mapping) {
812                     event->button.state|=GDK_MOD1_MASK;
813                 }
814                 break;
815              case GDK_KEY_PRESS:
816                  if(event->key.state & mapping) {
817                      event->key.state|=GDK_MOD1_MASK;
818                  }
819                  break;
820         default:
821             break;
822         }
823     }
824     gtk_main_do_event (event);
827 int
828 sp_main_gui(int argc, char const **argv)
830     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
832     GSList *fl = NULL;
833     int retVal = sp_common_main( argc, argv, &fl );
834     g_return_val_if_fail(retVal == 0, 1);
836     inkscape_gtk_stock_init();
838     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
840     Inkscape::Debug::log_display_config();
842     /* Set default icon */
843     gchar *filename = (gchar *) g_build_filename (INKSCAPE_APPICONDIR, "inkscape.png", NULL);
844     if (Inkscape::IO::file_test(filename, (GFileTest)(G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))) {
845         gtk_window_set_default_icon_from_file(filename, NULL);
846     }
847     g_free (filename);
848     filename = 0;
850     gboolean create_new = TRUE;
852     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
853     inkscape_application_init(argv[0], true);
855     while (fl) {
856         if (sp_file_open((gchar *)fl->data,NULL)) {
857             create_new=FALSE;
858         }
859         fl = g_slist_remove(fl, fl->data);
860     }
861     if (create_new) {
862         sp_file_new_default();
863     }
865     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
866     main_instance.run();
868 #ifdef WIN32
869     //We might not need anything here
870     //sp_win32_finish(); <-- this is a NOP func
871 #endif
873     return 0;
876 /**
877  * Process file list
878  */
879 void sp_process_file_list(GSList *fl)
881     while (fl) {
882         const gchar *filename = (gchar *)fl->data;
883         SPDocument *doc = Inkscape::Extension::open(NULL, filename);
884         if (doc == NULL) {
885             doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
886         }
887         if (doc == NULL) {
888             g_warning("Specified document %s cannot be opened (is it a valid SVG file?)", filename);
889         } else {
890             if (sp_vacuum_defs) {
891                 vacuum_document(doc);
892             }
893             if (sp_vacuum_defs && !sp_export_svg) {
894                 // save under the name given in the command line
895                 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
896             }
897             if (sp_global_printer) {
898                 sp_print_document_to_file(doc, sp_global_printer);
899             }
900             if (sp_export_png) {
901                 sp_do_export_png(doc);
902             }
903             if (sp_export_svg) {
904                 Inkscape::XML::Document *rdoc;
905                 Inkscape::XML::Node *repr;
906                 rdoc = sp_repr_document_new("svg:svg");
907                 repr = rdoc->root();
908                 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
909                 sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
910             }
911             if (sp_export_ps) {
912                 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
913             }
914             if (sp_export_eps) {
915                 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
916             }
917             if (sp_export_pdf) {
918                 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
919             }
920 #ifdef WIN32
921             if (sp_export_emf) {
922                 do_export_emf(doc, sp_export_emf, "image/x-emf");
923             }
924 #endif //WIN32
925             if (sp_query_all) {
926                 do_query_all (doc);
927             } else if (sp_query_width || sp_query_height) {
928                 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
929             } else if (sp_query_x || sp_query_y) {
930                 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
931             }
933             delete doc;
934         }
935         fl = g_slist_remove(fl, fl->data);
936     }
939 /**
940  * Run the application as an interactive shell, parsing command lines from stdin
941  * Returns -1 on error.
942  */
943 int sp_main_shell(char const* command_name)
945     int retval = 0;
947     const unsigned int buffer_size = 4096;
948     gchar *command_line = g_strnfill(buffer_size, 0);
949     g_strlcpy(command_line, command_name, buffer_size);
950     gsize offset = g_strlcat(command_line, " ", buffer_size);
951     gsize sizeLeft = buffer_size - offset;
952     gchar *useme = command_line + offset;
954     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
955     fflush(stdout);
956     char* linedata = 0;
957     do {
958         fprintf(stdout, ">");
959         fflush(stdout);
960         if ((linedata = fgets(useme, sizeLeft, stdin))) {
961             size_t len = strlen(useme);
962             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
963                 fprintf(stdout, "ERROR: Command line too long\n");
964                 // Consume rest of line
965                 retval = -1; // If the while loop completes, this remains -1
966                 while (fgets(useme, sizeLeft, stdin) && retval) {
967                     len = strlen(command_line);
968                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
969                         retval = 0;
970                     }
971                 }
972             } else {
973                 useme[--len] = '\0';  // Strip newline
974                 if (useme[len - 1] == '\r') {
975                     useme[--len] = '\0';
976                 }
977                 if ( strcmp(useme, "quit") == 0 ) {
978                     // Time to quit
979                     fflush(stdout);
980                     linedata = 0; // mark for exit
981                 } else if ( len < 1 ) {
982                     // blank string. Do nothing.
983                 } else {
984                     GError* parseError = 0;
985                     gchar** argv = 0;
986                     gint argc = 0;
987                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
988                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
989                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
990                         if ( ctx ) {
991                             GSList *fl = sp_process_args(ctx);
992                             sp_process_file_list(fl);
993                             poptFreeContext(ctx);
994                         } else {
995                             retval = 1; // not sure why. But this was the previous return value
996                         }
997                         resetCommandlineGlobals();
998                         g_strfreev(argv);
999                     } else {
1000                         g_warning("Cannot parse commandline: %s", useme);
1001                     }
1002                 }
1003             }
1004         } // if (linedata...
1005     } while (linedata && (retval == 0));
1007     g_free(command_line);
1008     return retval;
1011 int sp_main_console(int argc, char const **argv)
1013     /* We are started in text mode */
1015     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1016      * in a non-Gtk environment.  Used in libnrtype's
1017      * FontInstance.cpp and FontFactory.cpp.
1018      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1019      */
1020     g_type_init();
1021     char **argv2 = const_cast<char **>(argv);
1022     gtk_init_check( &argc, &argv2 );
1023     //setlocale(LC_ALL, "");
1025     GSList *fl = NULL;
1026     int retVal = sp_common_main( argc, argv, &fl );
1027     g_return_val_if_fail(retVal == 0, 1);
1029     if (fl == NULL && !sp_shell) {
1030         g_print("Nothing to do!\n");
1031         exit(0);
1032     }
1034     inkscape_application_init(argv[0], false);
1036     if (sp_shell) {
1037         sp_main_shell(argv[0]); // Run as interactive shell
1038         exit(0);
1039     } else {
1040         sp_process_file_list(fl); // Normal command line invokation
1041     }
1043     return 0;
1046 static void
1047 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1049     SPObject *o = NULL;
1051     if (id) {
1052         o = doc->getObjectById(id);
1053         if (o) {
1054             if (!SP_IS_ITEM (o)) {
1055                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1056                 return;
1057             }
1058         } else {
1059             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1060             return;
1061         }
1062     } else {
1063         o = SP_DOCUMENT_ROOT(doc);
1064     }
1066     if (o) {
1067         sp_document_ensure_up_to_date (doc);
1068         SPItem *item = ((SPItem *) o);
1070         // "true" SVG bbox for scripting
1071         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1072         if (area) {
1073             Inkscape::SVGOStringStream os;
1074             if (extent) {
1075                 os << area->dimensions()[axis];
1076             } else {
1077                 os << area->min()[axis];
1078             }
1079             g_print ("%s", os.str().c_str());
1080         } else {
1081             g_print("0");
1082         }
1083     }
1086 static void
1087 do_query_all (SPDocument *doc)
1089     SPObject *o = NULL;
1091     o = SP_DOCUMENT_ROOT(doc);
1093     if (o) {
1094         sp_document_ensure_up_to_date (doc);
1095         do_query_all_recurse(o);
1096     }
1099 static void
1100 do_query_all_recurse (SPObject *o)
1102     SPItem *item = ((SPItem *) o);
1103     if (o->id && SP_IS_ITEM(item)) {
1104         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1105         if (area) {
1106             Inkscape::SVGOStringStream os;
1107             os << o->id;
1108             os << "," << area->min()[Geom::X];
1109             os << "," << area->min()[Geom::Y];
1110             os << "," << area->dimensions()[Geom::X];
1111             os << "," << area->dimensions()[Geom::Y];
1112             g_print ("%s\n", os.str().c_str());
1113         }
1114     }
1116     SPObject *child = o->children;
1117     while (child) {
1118         do_query_all_recurse (child);
1119         child = child->next;
1120     }
1124 static void
1125 sp_do_export_png(SPDocument *doc)
1127     const gchar *filename = NULL;
1128     gdouble dpi = 0.0;
1130     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1131         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1132     }
1134     GSList *items = NULL;
1136     Geom::Rect area;
1137     if (sp_export_id || sp_export_area_drawing) {
1139         SPObject *o = NULL;
1140         SPObject *o_area = NULL;
1141         if (sp_export_id && sp_export_area_drawing) {
1142             o = doc->getObjectById(sp_export_id);
1143             o_area = SP_DOCUMENT_ROOT (doc);
1144         } else if (sp_export_id) {
1145             o = doc->getObjectById(sp_export_id);
1146             o_area = o;
1147         } else if (sp_export_area_drawing) {
1148             o = SP_DOCUMENT_ROOT (doc);
1149             o_area = o;
1150         }
1152         if (o) {
1153             if (!SP_IS_ITEM (o)) {
1154                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1155                 return;
1156             }
1158             items = g_slist_prepend (items, SP_ITEM(o));
1160             if (sp_export_id_only) {
1161                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1162             }
1164             if (sp_export_use_hints) {
1166                 // retrieve export filename hint
1167                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1168                 if (fn_hint) {
1169                     if (sp_export_png) {
1170                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1171                         filename = sp_export_png;
1172                     } else {
1173                         filename = fn_hint;
1174                     }
1175                 } else {
1176                     g_warning ("Export filename hint not found for the object.");
1177                     filename = sp_export_png;
1178                 }
1180                 // retrieve export dpi hints
1181                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1182                 if (dpi_hint) {
1183                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1184                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1185                     } else {
1186                         dpi = atof(dpi_hint);
1187                     }
1188                 } else {
1189                     g_warning ("Export DPI hint not found for the object.");
1190                 }
1192             }
1194             // write object bbox to area
1195             sp_document_ensure_up_to_date (doc);
1196             Geom::OptRect areaMaybe;
1197             sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1198             if (areaMaybe) {
1199                 area = *areaMaybe;
1200             } else {
1201                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1202                 return;
1203             }
1204         } else {
1205             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1206             return;
1207         }
1208     }
1210     if (sp_export_area) {
1211         /* Try to parse area (given in SVG pixels) */
1212         gdouble x0,y0,x1,y1;
1213         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1214             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1215             return;
1216         }
1217         area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1218     } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1219         /* Export the whole canvas */
1220         sp_document_ensure_up_to_date (doc);
1221         Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1222         area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1223     }
1225     // set filename and dpi from options, if not yet set from the hints
1226     if (!filename) {
1227         if (!sp_export_png) {
1228             g_warning ("No export filename given and no filename hint. Nothing exported.");
1229             return;
1230         }
1231         filename = sp_export_png;
1232     }
1234     if (sp_export_dpi && dpi == 0.0) {
1235         dpi = atof(sp_export_dpi);
1236         if ((dpi < 0.1) || (dpi > 10000.0)) {
1237             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1238             return;
1239         }
1240         g_print("DPI: %g\n", dpi);
1241     }
1243     if (sp_export_area_snap) {
1244         round_rectangle_outwards(area);
1245     }
1247     // default dpi
1248     if (dpi == 0.0) {
1249         dpi = PX_PER_IN;
1250     }
1252     unsigned long int width = 0;
1253     unsigned long int height = 0;
1255     if (sp_export_width) {
1256         errno=0;
1257         width = strtoul(sp_export_width, NULL, 0);
1258         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1259             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1260             return;
1261         }
1262         dpi = (gdouble) width * PX_PER_IN / area.width();
1263     }
1265     if (sp_export_height) {
1266         errno=0;
1267         height = strtoul(sp_export_height, NULL, 0);
1268         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1269             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1270             return;
1271         }
1272         dpi = (gdouble) height * PX_PER_IN / area.height();
1273     }
1275     if (!sp_export_width) {
1276         width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1277     }
1279     if (!sp_export_height) {
1280         height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1281     }
1283     guint32 bgcolor = 0x00000000;
1284     if (sp_export_background) {
1285         // override the page color
1286         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1287         bgcolor |= 0xff; // default is no opacity
1288     } else {
1289         // read from namedview
1290         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1291         if (nv && nv->attribute("pagecolor"))
1292             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1293         if (nv && nv->attribute("inkscape:pageopacity"))
1294             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1295     }
1297     if (sp_export_background_opacity) {
1298         // override opacity
1299         gfloat value;
1300         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1301             if (value > 1.0) {
1302                 value = CLAMP (value, 1.0f, 255.0f);
1303                 bgcolor &= (guint32) 0xffffff00;
1304                 bgcolor |= (guint32) floor(value);
1305             } else {
1306                 value = CLAMP (value, 0.0f, 1.0f);
1307                 bgcolor &= (guint32) 0xffffff00;
1308                 bgcolor |= SP_COLOR_F_TO_U(value);
1309             }
1310         }
1311     }
1313     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1315     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     g_print("Bitmap saved as: %s\n", filename);
1319     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1320         sp_export_png_file(doc, filename, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1321     } else {
1322         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1323     }
1325     g_slist_free (items);
1329 /**
1330  *  Perform a PDF/PS/EPS export
1331  *
1332  *  \param doc Document to export.
1333  *  \param uri URI to export to.
1334  *  \param mime MIME type to export as.
1335  */
1337 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1339     Inkscape::Extension::DB::OutputList o;
1340     Inkscape::Extension::db.get_output_list(o);
1341     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1342     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1343         i++;
1344     }
1346     if (i == o.end())
1347     {
1348         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1349         return;
1350     }
1352     if (sp_export_id) {
1353         SPObject *o = doc->getObjectById(sp_export_id);
1354         if (o == NULL) {
1355             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1356             return;
1357         }
1358         (*i)->set_param_string ("exportId", sp_export_id);
1359     } else {
1360         (*i)->set_param_string ("exportId", "");
1361     }
1363     if (sp_export_area_canvas && sp_export_area_drawing) {
1364         g_warning ("You cannot use --export-area-canvas and --export-area-drawing at the same time; only the former will take effect.");
1365         sp_export_area_drawing = false;
1366     }
1368     if (sp_export_area_drawing) {
1369         (*i)->set_param_bool ("areaDrawing", TRUE);
1370     } else {
1371         (*i)->set_param_bool ("areaDrawing", FALSE);
1372     }
1374     if (sp_export_area_canvas) {
1375         if (sp_export_eps) {
1376             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.");
1377         } 
1378         (*i)->set_param_bool ("areaCanvas", TRUE);
1379     } else {
1380         (*i)->set_param_bool ("areaCanvas", FALSE);
1381     }
1383     if (!sp_export_area_drawing && !sp_export_area_canvas && !sp_export_id) { 
1384         // neither is set, set canvas as default for ps/pdf and drawing for eps
1385         if (sp_export_eps) {
1386             try {
1387                (*i)->set_param_bool("areaDrawing", TRUE);
1388             } catch (...) {}
1389         } 
1390     }
1392     if (sp_export_text_to_path) {
1393         (*i)->set_param_bool("textToPath", TRUE);
1394     } else {
1395         (*i)->set_param_bool("textToPath", FALSE);
1396     }
1398     if (sp_export_ignore_filters) {
1399         (*i)->set_param_bool("blurToBitmap", FALSE);
1400     } else {
1401         (*i)->set_param_bool("blurToBitmap", TRUE);
1402     }
1404     (*i)->save(doc, uri);
1407 #ifdef WIN32
1408 /**
1409  *  Export a document to EMF
1410  *
1411  *  \param doc Document to export.
1412  *  \param uri URI to export to.
1413  *  \param mime MIME type to export as (should be "image/x-emf")
1414  */
1416 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1418     Inkscape::Extension::DB::OutputList o;
1419     Inkscape::Extension::db.get_output_list(o);
1420     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1421     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1422         i++;
1423     }
1425     if (i == o.end())
1426     {
1427         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1428         return;
1429     }
1431     (*i)->save(doc, uri);
1433 #endif //WIN32
1435 #ifdef WIN32
1436 bool replaceArgs( int& argc, char**& argv )
1438     bool worked = false;
1440 #ifdef REPLACEARGS_DEBUG
1441     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1442 #endif // REPLACEARGS_DEBUG
1444     wchar_t* line = GetCommandLineW();
1445     if ( line )
1446     {
1447 #ifdef REPLACEARGS_DEBUG
1448         {
1449             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1450             if ( utf8Line )
1451             {
1452                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1453                 {
1454                     char tmp[strlen(safe) + 32];
1455                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1456                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1457                 }
1458             }
1459         }
1460 #endif // REPLACEARGS_DEBUG
1462         int numArgs = 0;
1463         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1465 #ifdef REPLACEARGS_ANSI
1466 // test code for trying things on Win95/98/ME
1467         if ( !parsed )
1468         {
1469 #ifdef REPLACEARGS_DEBUG
1470             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1471 #endif // REPLACEARGS_DEBUG
1472             int lineLen = wcslen(line) + 1;
1473             wchar_t* lineDup = new wchar_t[lineLen];
1474             wcsncpy( lineDup, line, lineLen );
1476             int pos = 0;
1477             bool inQuotes = false;
1478             bool inWhitespace = true;
1479             std::vector<int> places;
1480             while ( lineDup[pos] )
1481             {
1482                 if ( inQuotes )
1483                 {
1484                     if ( lineDup[pos] == L'"' )
1485                     {
1486                         inQuotes = false;
1487                     }
1488                 }
1489                 else if ( lineDup[pos] == L'"' )
1490                 {
1491                     inQuotes = true;
1492                     inWhitespace = false;
1493                     places.push_back(pos);
1494                 }
1495                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1496                 {
1497                     if ( !inWhitespace )
1498                     {
1499                         inWhitespace = true;
1500                         lineDup[pos] = 0;
1501                     }
1502                 }
1503                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1504                 {
1505                     inWhitespace = false;
1506                     places.push_back(pos);
1507                 }
1508                 else
1509                 {
1510                     // consume
1511                 }
1512                 pos++;
1513             }
1514 #ifdef REPLACEARGS_DEBUG
1515             {
1516                 char tmp[256];
1517                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1518                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1519             }
1520 #endif // REPLACEARGS_DEBUG
1522             wchar_t** block = new wchar_t*[places.size()];
1523             int i = 0;
1524             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1525             {
1526                 block[i++] = &lineDup[*it];
1527             }
1528             parsed = block;
1529             numArgs = places.size();
1530         }
1531 #endif // REPLACEARGS_ANSI
1533         if ( parsed )
1534         {
1535             std::vector<wchar_t*>expandedArgs;
1536             if ( numArgs > 0 )
1537             {
1538                 expandedArgs.push_back( parsed[0] );
1539             }
1541             for ( int i1 = 1; i1 < numArgs; i1++ )
1542             {
1543                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1544                 wildcarded &= parsed[i1][0] != L'"';
1545                 wildcarded &= parsed[i1][0] != L'-';
1546                 if ( wildcarded )
1547                 {
1548 #ifdef REPLACEARGS_ANSI
1549                     WIN32_FIND_DATAA data;
1550 #else
1551                     WIN32_FIND_DATAW data;
1552 #endif // REPLACEARGS_ANSI
1554                     memset((void *)&data, 0, sizeof(data));
1556                     int baseLen = wcslen(parsed[i1]) + 2;
1557                     wchar_t* base = new wchar_t[baseLen];
1558                     wcsncpy( base, parsed[i1], baseLen );
1559                     wchar_t* last = wcsrchr( base, L'\\' );
1560                     if ( last )
1561                     {
1562                         last[1] = 0;
1563                     }
1564                     else
1565                     {
1566                         base[0] = 0;
1567                     }
1568                     baseLen = wcslen( base );
1570 #ifdef REPLACEARGS_ANSI
1571                     char target[MAX_PATH];
1572                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1573                     {
1574                         HANDLE hf = FindFirstFileA( target, &data );
1575 #else
1576                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1577 #endif // REPLACEARGS_ANSI
1578                         if ( hf != INVALID_HANDLE_VALUE )
1579                         {
1580                             BOOL found = TRUE;
1581                             do
1582                             {
1583 #ifdef REPLACEARGS_ANSI
1584                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1585                                 if ( howMany > 0 )
1586                                 {
1587                                     howMany += baseLen;
1588                                     wchar_t* tmp = new wchar_t[howMany + 1];
1589                                     wcsncpy( tmp, base, howMany + 1 );
1590                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1591                                     expandedArgs.push_back( tmp );
1592                                     found = FindNextFileA( hf, &data );
1593                                 }
1594 #else
1595                                 int howMany = wcslen(data.cFileName) + baseLen;
1596                                 wchar_t* tmp = new wchar_t[howMany + 1];
1597                                 wcsncpy( tmp, base, howMany + 1 );
1598                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1599                                 expandedArgs.push_back( tmp );
1600                                 found = FindNextFileW( hf, &data );
1601 #endif // REPLACEARGS_ANSI
1602                             } while ( found );
1604                             FindClose( hf );
1605                         }
1606                         else
1607                         {
1608                             expandedArgs.push_back( parsed[i1] );
1609                         }
1610 #ifdef REPLACEARGS_ANSI
1611                     }
1612 #endif // REPLACEARGS_ANSI
1614                     delete[] base;
1615                 }
1616                 else
1617                 {
1618                     expandedArgs.push_back( parsed[i1] );
1619                 }
1620             }
1622             {
1623                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1624                 int iz = 0;
1625                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1626                 {
1627                     block[iz++] = *it;
1628                 }
1629                 parsed = block;
1630                 numArgs = expandedArgs.size();
1631             }
1633             std::vector<gchar*> newArgs;
1634             for ( int i = 0; i < numArgs; i++ )
1635             {
1636                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1637                 if ( replacement )
1638                 {
1639 #ifdef REPLACEARGS_DEBUG
1640                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1642                     if ( safe2 )
1643                     {
1644                         {
1645                             char tmp[1024];
1646                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1647                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1648                         }
1649                         g_free( safe2 );
1650                     }
1651 #endif // REPLACEARGS_DEBUG
1653                     newArgs.push_back( replacement );
1654                 }
1655                 else
1656                 {
1657                     newArgs.push_back( blankParam );
1658                 }
1659             }
1661             // Now push our munged params to be the new argv and argc
1662             {
1663                 char** block = new char*[newArgs.size()];
1664                 int iz = 0;
1665                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1666                 {
1667                     block[iz++] = *it;
1668                 }
1669                 argv = block;
1670                 argc = newArgs.size();
1671                 worked = true;
1672             }
1673         }
1674 #ifdef REPLACEARGS_DEBUG
1675         else
1676         {
1677             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1678         }
1679 #endif // REPLACEARGS_DEBUG
1680     }
1681 #ifdef REPLACEARGS_DEBUG
1682     else
1683     {
1684         {
1685             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1686         }
1688         char* line2 = GetCommandLineA();
1689         if ( line2 )
1690         {
1691             gchar *safe = Inkscape::IO::sanitizeString(line2);
1692             {
1693                 {
1694                     char tmp[strlen(safe) + 32];
1695                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1696                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1697                 }
1698             }
1699         }
1700         else
1701         {
1702             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1703         }
1704     }
1705 #endif // REPLACEARGS_DEBUG
1707     return worked;
1709 #endif // WIN32
1711 static GSList *
1712 sp_process_args(poptContext ctx)
1714     GSList *fl = NULL;
1716     gint a;
1717     while ((a = poptGetNextOpt(ctx)) != -1) {
1718         switch (a) {
1719             case SP_ARG_FILE: {
1720                 gchar const *fn = poptGetOptArg(ctx);
1721                 if (fn != NULL) {
1722                     fl = g_slist_append(fl, g_strdup(fn));
1723                 }
1724                 break;
1725             }
1726             case SP_ARG_VERSION: {
1727                 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1728                 exit(0);
1729                 break;
1730             }
1731             case SP_ARG_EXTENSIONDIR: {
1732                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1733                 exit(0);
1734                 break;
1735             }
1736             case SP_ARG_VERB_LIST: {
1737                 // This really shouldn't go here, we should init the app.
1738                 // But, since we're just exiting in this path, there is
1739                 // no harm, and this is really a better place to put
1740                 // everything else.
1741                 Inkscape::Extension::init();
1742                 Inkscape::Verb::list();
1743                 exit(0);
1744                 break;
1745             }
1746             case SP_ARG_VERB:
1747             case SP_ARG_SELECT: {
1748                 gchar const *arg = poptGetOptArg(ctx);
1749                 if (arg != NULL) {
1750                     // printf("Adding in: %s\n", arg);
1751                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1752                 }
1753                 break;
1754             }
1755             case POPT_ERROR_BADOPT: {
1756                 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1757                 exit(1);
1758                 break;
1759             }
1760             default: {
1761                 break;
1762             }
1763         }
1764     }
1766     gchar const ** const args = poptGetArgs(ctx);
1767     if (args != NULL) {
1768         for (unsigned i = 0; args[i] != NULL; i++) {
1769             fl = g_slist_append(fl, g_strdup(args[i]));
1770         }
1771     }
1773     return fl;
1777 /*
1778   Local Variables:
1779   mode:c++
1780   c-file-style:"stroustrup"
1781   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1782   indent-tabs-mode:nil
1783   fill-column:99
1784   End:
1785 */
1786 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :