Code

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