Code

Merge from trunk
[inkscape.git] / src / main.cpp
1 #define __MAIN_C__
3 /** \file
4  * Inkscape - an ambitious vector drawing program
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Frank Felfe <innerspace@iname.com>
9  *   Davide Puricelli <evo@debian.org>
10  *   Mitsuru Oka <oka326@parkcity.ne.jp>
11  *   Masatake YAMATO  <jet@gyve.org>
12  *   F.J.Franklin <F.J.Franklin@sheffield.ac.uk>
13  *   Michael Meeks <michael@helixcode.com>
14  *   Chema Celorio <chema@celorio.com>
15  *   Pawel Palucha
16  *   Bryce Harrington <bryce@bryceharrington.com>
17  * ... and various people who have worked with various projects
18  *
19  * Copyright (C) 1999-2004 authors
20  * Copyright (C) 2001-2002 Ximian, Inc.
21  *
22  * Released under GNU GPL, read the file 'COPYING' for more information
23  */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 #include "path-prefix.h"
31 #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"
86 #include <extension/extension.h>
87 #include <extension/system.h>
88 #include <extension/db.h>
89 #include <extension/output.h>
91 #ifdef WIN32
92 //#define REPLACEARGS_ANSI
93 //#define REPLACEARGS_DEBUG
95 #include "registrytool.h"
97 #include "extension/internal/win32.h"
98 using Inkscape::Extension::Internal::PrintWin32;
100 #endif // WIN32
102 #include "extension/init.h"
104 #include <glibmm/i18n.h>
105 #include <gtkmm/main.h>
107 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
108 #define bind_textdomain_codeset(p,c)
109 #endif
111 #include "application/application.h"
113 #include "main-cmdlineact.h"
115 #include <png.h>
116 #include <errno.h>
118 enum {
119     SP_ARG_NONE,
120     SP_ARG_NOGUI,
121     SP_ARG_GUI,
122     SP_ARG_FILE,
123     SP_ARG_PRINT,
124     SP_ARG_EXPORT_PNG,
125     SP_ARG_EXPORT_DPI,
126     SP_ARG_EXPORT_AREA,
127     SP_ARG_EXPORT_AREA_DRAWING,
128     SP_ARG_EXPORT_AREA_CANVAS,
129     SP_ARG_EXPORT_AREA_SNAP,
130     SP_ARG_EXPORT_WIDTH,
131     SP_ARG_EXPORT_HEIGHT,
132     SP_ARG_EXPORT_ID,
133     SP_ARG_EXPORT_ID_ONLY,
134     SP_ARG_EXPORT_USE_HINTS,
135     SP_ARG_EXPORT_BACKGROUND,
136     SP_ARG_EXPORT_BACKGROUND_OPACITY,
137     SP_ARG_EXPORT_SVG,
138     SP_ARG_EXPORT_PS,
139     SP_ARG_EXPORT_EPS,
140     SP_ARG_EXPORT_PDF,
141 #ifdef WIN32
142     SP_ARG_EXPORT_EMF,
143 #endif //WIN32
144     SP_ARG_EXPORT_TEXT_TO_PATH,
145     SP_ARG_EXTENSIONDIR,
146     SP_ARG_QUERY_X,
147     SP_ARG_QUERY_Y,
148     SP_ARG_QUERY_WIDTH,
149     SP_ARG_QUERY_HEIGHT,
150     SP_ARG_QUERY_ALL,
151     SP_ARG_QUERY_ID,
152     SP_ARG_SHELL,
153     SP_ARG_VERSION,
154     SP_ARG_VACUUM_DEFS,
155     SP_ARG_VERB_LIST,
156     SP_ARG_VERB,
157     SP_ARG_SELECT,
158     SP_ARG_LAST
159 };
161 int sp_main_gui(int argc, char const **argv);
162 int sp_main_console(int argc, char const **argv);
163 static void sp_do_export_png(SPDocument *doc);
164 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime);
165 #ifdef WIN32
166 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
167 #endif //WIN32
168 static void do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id);
169 static void do_query_all (SPDocument *doc);
170 static void do_query_all_recurse (SPObject *o);
172 static gchar *sp_global_printer = NULL;
173 static gchar *sp_export_png = NULL;
174 static gchar *sp_export_dpi = NULL;
175 static gchar *sp_export_area = NULL;
176 static gboolean sp_export_area_drawing = FALSE;
177 static gboolean sp_export_area_canvas = FALSE;
178 static gchar *sp_export_width = NULL;
179 static gchar *sp_export_height = NULL;
180 static gchar *sp_export_id = NULL;
181 static gchar *sp_export_background = NULL;
182 static gchar *sp_export_background_opacity = NULL;
183 static gboolean sp_export_area_snap = FALSE;
184 static gboolean sp_export_use_hints = FALSE;
185 static gboolean sp_export_id_only = FALSE;
186 static gchar *sp_export_svg = NULL;
187 static gchar *sp_export_ps = NULL;
188 static gchar *sp_export_eps = NULL;
189 static gchar *sp_export_pdf = NULL;
190 #ifdef WIN32
191 static gchar *sp_export_emf = NULL;
192 #endif //WIN32
193 static gboolean sp_export_text_to_path = FALSE;
194 static gboolean sp_export_font = FALSE;
195 static gboolean sp_query_x = FALSE;
196 static gboolean sp_query_y = FALSE;
197 static gboolean sp_query_width = FALSE;
198 static gboolean sp_query_height = FALSE;
199 static gboolean sp_query_all = FALSE;
200 static gchar *sp_query_id = NULL;
201 static int sp_new_gui = FALSE;
202 static gboolean sp_shell = FALSE;
203 static gboolean sp_vacuum_defs = FALSE;
205 static gchar *sp_export_png_utf8 = NULL;
206 static gchar *sp_export_svg_utf8 = NULL;
207 static gchar *sp_global_printer_utf8 = NULL;
210 /**
211  *  Reset variables to default values.
212  */
213 static void resetCommandlineGlobals() {
214         sp_global_printer = NULL;
215         sp_export_png = NULL;
216         sp_export_dpi = NULL;
217         sp_export_area = NULL;
218         sp_export_area_drawing = FALSE;
219         sp_export_area_canvas = FALSE;
220         sp_export_width = NULL;
221         sp_export_height = NULL;
222         sp_export_id = NULL;
223         sp_export_background = NULL;
224         sp_export_background_opacity = NULL;
225         sp_export_area_snap = FALSE;
226         sp_export_use_hints = FALSE;
227         sp_export_id_only = FALSE;
228         sp_export_svg = NULL;
229         sp_export_ps = NULL;
230         sp_export_eps = NULL;
231         sp_export_pdf = NULL;
232 #ifdef WIN32
233         sp_export_emf = NULL;
234 #endif //WIN32
235         sp_export_text_to_path = FALSE;
236         sp_export_font = FALSE;
237         sp_query_x = FALSE;
238         sp_query_y = FALSE;
239         sp_query_width = FALSE;
240         sp_query_height = FALSE;
241         sp_query_all = FALSE;
242         sp_query_id = NULL;
243         sp_vacuum_defs = FALSE;
245         sp_export_png_utf8 = NULL;
246         sp_export_svg_utf8 = NULL;
247         sp_global_printer_utf8 = NULL;
250 #ifdef WIN32
251 static bool replaceArgs( int& argc, char**& argv );
252 #endif
253 static GSList *sp_process_args(poptContext ctx);
254 struct poptOption options[] = {
255     {"version", 'V',
256      POPT_ARG_NONE, NULL, SP_ARG_VERSION,
257      N_("Print the Inkscape version number"),
258      NULL},
260     {"without-gui", 'z',
261      POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
262      N_("Do not use X server (only process files from console)"),
263      NULL},
265     {"with-gui", 'g',
266      POPT_ARG_NONE, NULL, SP_ARG_GUI,
267      N_("Try to use X server (even if $DISPLAY is not set)"),
268      NULL},
270     {"file", 'f',
271      POPT_ARG_STRING, NULL, SP_ARG_FILE,
272      N_("Open specified document(s) (option string may be excluded)"),
273      N_("FILENAME")},
275     {"print", 'p',
276      POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
277      N_("Print document(s) to specified output file (use '| program' for pipe)"),
278      N_("FILENAME")},
280     {"export-png", 'e',
281      POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
282      N_("Export document to a PNG file"),
283      N_("FILENAME")},
285     {"export-dpi", 'd',
286      POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
287      N_("The resolution used for exporting SVG into bitmap (default 90)"),
288      N_("DPI")},
290     {"export-area", 'a',
291      POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
292      N_("Exported area in SVG user units (default is the canvas; 0,0 is lower-left corner)"),
293      N_("x0:y0:x1:y1")},
295     {"export-area-drawing", 'D',
296      POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
297      N_("Exported area is the entire drawing (not canvas)"),
298      NULL},
300     {"export-area-canvas", 'C',
301      POPT_ARG_NONE, &sp_export_area_canvas, SP_ARG_EXPORT_AREA_CANVAS,
302      N_("Exported area is the entire canvas"),
303      NULL},
305     {"export-area-snap", 0,
306      POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
307      N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
308      NULL},
310     {"export-width", 'w',
311      POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
312      N_("The width of exported bitmap in pixels (overrides export-dpi)"),
313      N_("WIDTH")},
315     {"export-height", 'h',
316      POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
317      N_("The height of exported bitmap in pixels (overrides export-dpi)"),
318      N_("HEIGHT")},
320     {"export-id", 'i',
321      POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
322      N_("The ID of the object to export"),
323      N_("ID")},
325     {"export-id-only", 'j',
326      POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
327      // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
328      //  See "man inkscape" for details.
329      N_("Export just the object with export-id, hide all others (only with export-id)"),
330      NULL},
332     {"export-use-hints", 't',
333      POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
334      N_("Use stored filename and DPI hints when exporting (only with export-id)"),
335      NULL},
337     {"export-background", 'b',
338      POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
339      N_("Background color of exported bitmap (any SVG-supported color string)"),
340      N_("COLOR")},
342     {"export-background-opacity", 'y',
343      POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
344      N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
345      N_("VALUE")},
347     {"export-plain-svg", 'l',
348      POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
349      N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
350      N_("FILENAME")},
352     {"export-ps", 'P',
353      POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
354      N_("Export document to a PS file"),
355      N_("FILENAME")},
357     {"export-eps", 'E',
358      POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
359      N_("Export document to an EPS file"),
360      N_("FILENAME")},
362     {"export-pdf", 'A',
363      POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
364      N_("Export document to a PDF file"),
365      N_("FILENAME")},
367 #ifdef WIN32
368     {"export-emf", 'M',
369      POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
370      N_("Export document to an Enhanced Metafile (EMF) File"),
371      N_("FILENAME")},
372 #endif //WIN32
374     {"export-text-to-path", 'T',
375      POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
376      N_("Convert text object to paths on export (EPS)"),
377      NULL},
379     {"query-x", 'X',
380      POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
381      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
382      N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
383      NULL},
385     {"query-y", 'Y',
386      POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
387      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
388      N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
389      NULL},
391     {"query-width", 'W',
392      POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
393      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
394      N_("Query the width of the drawing or, if specified, of the object with --query-id"),
395      NULL},
397     {"query-height", 'H',
398      POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
399      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
400      N_("Query the height of the drawing or, if specified, of the object with --query-id"),
401      NULL},
403     {"query-all", 'S',
404      POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
405      N_("List id,x,y,w,h for all objects"),
406      NULL},
408     {"query-id", 'I',
409      POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
410      N_("The ID of the object whose dimensions are queried"),
411      N_("ID")},
413     {"extension-directory", 'x',
414      POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
415      // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
416      N_("Print out the extension directory and exit"),
417      NULL},
419     {"vacuum-defs", 0,
420      POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
421      N_("Remove unused definitions from the defs section(s) of the document"),
422      NULL},
424     {"verb-list", 0,
425      POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
426      N_("List the IDs of all the verbs in Inkscape"),
427      NULL},
429     {"verb", 0,
430      POPT_ARG_STRING, NULL, SP_ARG_VERB,
431      N_("Verb to call when Inkscape opens."),
432      N_("VERB-ID")},
434     {"select", 0,
435      POPT_ARG_STRING, NULL, SP_ARG_SELECT,
436      N_("Object ID to select when Inkscape opens."),
437      N_("OBJECT-ID")},
439     {"shell", 0,
440      POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
441      N_("Start Inkscape in interative shell mode."),
442      NULL},
444     POPT_AUTOHELP POPT_TABLEEND
445 };
447 static bool needToRecodeParams = true;
448 gchar * blankParam = g_strdup("");
452 #ifdef WIN32
454 /**
455  * Return the directory of the .exe that is currently running
456  */
457 static Glib::ustring _win32_getExePath()
459     char exeName[MAX_PATH+1];
460     GetModuleFileName(NULL, exeName, MAX_PATH);
461     char *slashPos = strrchr(exeName, '\\');
462     if (slashPos)
463         *slashPos = '\0';
464     Glib::ustring s = exeName;
465     return s;
468 /**
469  * Set up the PATH and PYTHONPATH environment variables on
470  * win32
471  */
472 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
475     char *oldenv = getenv("PATH");
476     Glib::ustring tmp = "PATH=";
477     tmp += exePath;
478     tmp += ";";
479     tmp += exePath;
480     tmp += "\\python;";
481     tmp += exePath;
482     tmp += "\\python\\Scripts;";  // for uniconv.cmd
483     tmp += exePath;
484     tmp += "\\perl";
485     if(oldenv != NULL) {
486         tmp += ";";
487         tmp += oldenv;
488     }
489     _putenv(tmp.c_str());
491     oldenv = getenv("PYTHONPATH");
492     tmp = "PYTHONPATH=";
493     tmp += exePath;
494     tmp += "\\python;";
495     tmp += exePath;
496     tmp += "\\python\\Lib;";
497     tmp += exePath;
498     tmp += "\\python\\DLLs";
499     if(oldenv != NULL) {
500         tmp += ";";
501         tmp += oldenv;
502     }
503     _putenv(tmp.c_str());
505     return 0;
507 #endif
509 /**
510  * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
511  * can find inkex.py et al. (Bug #197475)
512  */
513 static int set_extensions_env()
515     char *oldenv = getenv("PYTHONPATH");
516     Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
517     if (oldenv != NULL) {
518 #ifdef WIN32
519         tmp += ";";
520 #else
521         tmp += ":";
522 #endif
523         tmp += oldenv;
524     }
525 #ifdef WIN32
526     /// \todo this does not work on windows, cannot find the setenv method or an equivalent.
527     //setenv("PYTHONPATH", tmp.c_str(), 1);
528 #else
529     setenv("PYTHONPATH", tmp.c_str(), 1);
530 #endif
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     // Bug #197475
566     set_extensions_env();
568    /**
569     * Call bindtextdomain() for various machines's paths
570     */
571 #ifdef ENABLE_NLS
572 #ifdef WIN32
573     Glib::ustring localePath = homedir;
574     localePath += "\\";
575     localePath += PACKAGE_LOCALE_DIR;
576     bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
577 #else
578 #ifdef ENABLE_BINRELOC
579     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
580 #else
581     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
582 #endif
583 #endif
584     // Allow the user to override the locale directory by setting
585     // the environment variable INKSCAPE_LOCALEDIR.
586     char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
587     if (inkscape_localedir != NULL) {
588         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
589     }
590 #endif
592     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
594 #ifdef ENABLE_NLS
595     textdomain(GETTEXT_PACKAGE);
596 #endif
598     LIBXML_TEST_VERSION
600     Inkscape::GC::init();
602     Inkscape::Debug::Logger::init();
604     gboolean use_gui;
606 #ifndef WIN32
607     use_gui = (getenv("DISPLAY") != NULL);
608 #else
609     use_gui = TRUE;
610 #endif
611     /* Test whether with/without GUI is forced */
612     for (int i = 1; i < argc; i++) {
613         if (!strcmp(argv[i], "-z")
614             || !strcmp(argv[i], "--without-gui")
615             || !strcmp(argv[i], "-p")
616             || !strncmp(argv[i], "--print", 7)
617             || !strcmp(argv[i], "-e")
618             || !strncmp(argv[i], "--export-png", 12)
619             || !strcmp(argv[i], "-l")
620             || !strncmp(argv[i], "--export-plain-svg", 12)
621             || !strcmp(argv[i], "-i")
622             || !strncmp(argv[i], "--export-area-drawing", 21)
623             || !strcmp(argv[i], "-D")
624             || !strncmp(argv[i], "--export-area-canvas", 20)
625             || !strcmp(argv[i], "-C")
626             || !strncmp(argv[i], "--export-id", 12)
627             || !strcmp(argv[i], "-P")
628             || !strncmp(argv[i], "--export-ps", 11)
629             || !strcmp(argv[i], "-E")
630             || !strncmp(argv[i], "--export-eps", 12)
631             || !strcmp(argv[i], "-A")
632             || !strncmp(argv[i], "--export-pdf", 12)
633 #ifdef WIN32
634             || !strcmp(argv[i], "-M")
635             || !strncmp(argv[i], "--export-emf", 12)
636 #endif //WIN32
637             || !strcmp(argv[i], "-W")
638             || !strncmp(argv[i], "--query-width", 13)
639             || !strcmp(argv[i], "-H")
640             || !strncmp(argv[i], "--query-height", 14)
641             || !strcmp(argv[i], "-S")
642             || !strncmp(argv[i], "--query-all", 11)
643             || !strcmp(argv[i], "-X")
644             || !strncmp(argv[i], "--query-x", 13)
645             || !strcmp(argv[i], "-Y")
646             || !strncmp(argv[i], "--query-y", 14)
647             || !strcmp(argv[i], "--vacuum-defs")
648             || !strncmp(argv[i], "--shell", 7)
649            )
650         {
651             /* main_console handles any exports -- not the gui */
652             use_gui = FALSE;
653             break;
654         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
655             use_gui = TRUE;
656             break;
657         }
658     }
660 #ifdef WIN32
661 #ifndef REPLACEARGS_ANSI
662     if ( PrintWin32::is_os_wide() )
663 #endif // REPLACEARGS_ANSI
664     {
665         // If the call fails, we'll need to convert charsets
666         needToRecodeParams = !replaceArgs( argc, argv );
667     }
668 #endif // WIN32
670     /// \todo  Should this be a static object (see inkscape.cpp)?
671     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
673     return app.run();
679 void fixupSingleFilename( gchar **orig, gchar **spare )
681     if ( orig && *orig && **orig ) {
682         GError *error = NULL;
683         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
684         if ( newFileName )
685         {
686             *orig = newFileName;
687             if ( spare ) {
688                 *spare = newFileName;
689             }
690 //             g_message("Set a replacement fixup");
691         }
692     }
697 GSList *fixupFilenameEncoding( GSList* fl )
699     GSList *newFl = NULL;
700     while ( fl ) {
701         gchar *fn = static_cast<gchar*>(fl->data);
702         fl = g_slist_remove( fl, fl->data );
703         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
704         if ( newFileName ) {
706             if ( 0 )
707             {
708                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
709                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
710                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
711                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
712                 gtk_dialog_run (GTK_DIALOG (w));
713                 gtk_widget_destroy (w);
714                 g_free(safeNewFn);
715                 g_free(safeFn);
716             }
718             g_free( fn );
719             fn = newFileName;
720             newFileName = 0;
721         }
722         else
723             if ( 0 )
724         {
725             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
726             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
727             gtk_dialog_run (GTK_DIALOG (w));
728             gtk_widget_destroy (w);
729             g_free(safeFn);
730         }
731         newFl = g_slist_append( newFl, fn );
732     }
733     return newFl;
736 int sp_common_main( int argc, char const **argv, GSList **flDest )
738     /// \todo fixme: Move these to some centralized location (Lauris)
739     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
740     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
743     // temporarily switch gettext encoding to locale, so that help messages can be output properly
744     gchar const *charset;
745     g_get_charset(&charset);
747     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
749     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
750     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
751     g_return_val_if_fail(ctx != NULL, 1);
753     /* Collect own arguments */
754     GSList *fl = sp_process_args(ctx);
755     poptFreeContext(ctx);
757     // now switch gettext back to UTF-8 (for GUI)
758     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
760     // Now let's see if the file list still holds up
761     if ( needToRecodeParams )
762     {
763         fl = fixupFilenameEncoding( fl );
764     }
766     // Check the globals for filename-fixup
767     if ( needToRecodeParams )
768     {
769         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
770         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
771         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
772     }
773     else
774     {
775         if ( sp_export_png )
776             sp_export_png_utf8 = g_strdup( sp_export_png );
777         if ( sp_export_svg )
778             sp_export_svg_utf8 = g_strdup( sp_export_svg );
779         if ( sp_global_printer )
780             sp_global_printer_utf8 = g_strdup( sp_global_printer );
781     }
783     // Return the list if wanted, else free it up.
784     if ( flDest ) {
785         *flDest = fl;
786         fl = 0;
787     } else {
788         while ( fl ) {
789             g_free( fl->data );
790             fl = g_slist_remove( fl, fl->data );
791         }
792     }
793     return 0;
796 static void
797 snooper(GdkEvent *event, gpointer /*data*/) {
798     if(inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
799     {
800         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
801         switch (event->type) {
802             case GDK_MOTION_NOTIFY:
803                 if(event->motion.state & mapping) {
804                     event->motion.state|=GDK_MOD1_MASK;
805                 }
806                 break;
807             case GDK_BUTTON_PRESS:
808                 if(event->button.state & mapping) {
809                     event->button.state|=GDK_MOD1_MASK;
810                 }
811                 break;
812              case GDK_KEY_PRESS:
813                  if(event->key.state & mapping) {
814                      event->key.state|=GDK_MOD1_MASK;
815                  }
816                  break;
817         default:
818             break;
819         }
820     }
821     gtk_main_do_event (event);
824 int
825 sp_main_gui(int argc, char const **argv)
827     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
829     GSList *fl = NULL;
830     int retVal = sp_common_main( argc, argv, &fl );
831     g_return_val_if_fail(retVal == 0, 1);
833     inkscape_gtk_stock_init();
835     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
837     Inkscape::Debug::log_display_config();
839     /* Set default icon */
840     gchar *filename = (gchar *) g_build_filename (INKSCAPE_APPICONDIR, "inkscape.png", NULL);
841     if (Inkscape::IO::file_test(filename, (GFileTest)(G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))) {
842         gtk_window_set_default_icon_from_file(filename, NULL);
843     }
844     g_free (filename);
845     filename = 0;
847     gboolean create_new = TRUE;
849     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
850     inkscape_application_init(argv[0], true);
852     while (fl) {
853         if (sp_file_open((gchar *)fl->data,NULL)) {
854             create_new=FALSE;
855         }
856         fl = g_slist_remove(fl, fl->data);
857     }
858     if (create_new) {
859         sp_file_new_default();
860     }
862     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
863     main_instance.run();
865 #ifdef WIN32
866     //We might not need anything here
867     //sp_win32_finish(); <-- this is a NOP func
868 #endif
870     return 0;
873 /**
874  * Process file list
875  */
876 void sp_process_file_list(GSList *fl)
878     while (fl) {
879         const gchar *filename = (gchar *)fl->data;
880         SPDocument *doc = Inkscape::Extension::open(NULL, filename);
881         if (doc == NULL) {
882             doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
883         }
884         if (doc == NULL) {
885             g_warning("Specified document %s cannot be opened (is it a valid SVG file?)", filename);
886         } else {
887             if (sp_vacuum_defs) {
888                 vacuum_document(doc);
889             }
890             if (sp_vacuum_defs && !sp_export_svg) {
891                 // save under the name given in the command line
892                 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
893             }
894             if (sp_global_printer) {
895                 sp_print_document_to_file(doc, sp_global_printer);
896             }
897             if (sp_export_png) {
898                 sp_do_export_png(doc);
899             }
900             if (sp_export_svg) {
901                 Inkscape::XML::Document *rdoc;
902                 Inkscape::XML::Node *repr;
903                 rdoc = sp_repr_document_new("svg:svg");
904                 repr = rdoc->root();
905                 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
906                 sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
907             }
908             if (sp_export_ps) {
909                 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
910             }
911             if (sp_export_eps) {
912                 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
913             }
914             if (sp_export_pdf) {
915                 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
916             }
917 #ifdef WIN32
918             if (sp_export_emf) {
919                 do_export_emf(doc, sp_export_emf, "image/x-emf");
920             }
921 #endif //WIN32
922             if (sp_query_all) {
923                 do_query_all (doc);
924             } else if (sp_query_width || sp_query_height) {
925                 do_query_dimension (doc, true, sp_query_width? NR::X : NR::Y, sp_query_id);
926             } else if (sp_query_x || sp_query_y) {
927                 do_query_dimension (doc, false, sp_query_x? NR::X : NR::Y, sp_query_id);
928             }
930             delete doc;
931         }
932         fl = g_slist_remove(fl, fl->data);
933     }
936 /**
937  * Run the application as an interactive shell, parsing command lines from stdin
938  * Returns -1 on error.
939  */
940 int sp_main_shell(char const* command_name)
942     int retval = 0;
944     const unsigned int buffer_size = 4096;
945     gchar *command_line = g_strnfill(buffer_size, 0);
946     g_strlcpy(command_line, command_name, buffer_size);
947     gsize offset = g_strlcat(command_line, " ", buffer_size);
948     gsize sizeLeft = buffer_size - offset;
949     gchar *useme = command_line + offset;
951     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", INKSCAPE_VERSION);
952     fflush(stdout);
953     char* linedata = 0;
954     do {
955         fprintf(stdout, ">");
956         fflush(stdout);
957         if ((linedata = fgets(useme, sizeLeft, stdin))) {
958             size_t len = strlen(useme);
959             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
960                 fprintf(stdout, "ERROR: Command line too long\n");
961                 // Consume rest of line
962                 retval = -1; // If the while loop completes, this remains -1
963                 while (fgets(useme, sizeLeft, stdin) && retval) {
964                     len = strlen(command_line);
965                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
966                         retval = 0;
967                     }
968                 }
969             } else {
970                 useme[--len] = '\0';  // Strip newline
971                 if (useme[len - 1] == '\r') {
972                     useme[--len] = '\0';
973                 }
974                 if ( strcmp(useme, "quit") == 0 ) {
975                     // Time to quit
976                     fflush(stdout);
977                     linedata = 0; // mark for exit
978                 } else if ( len < 1 ) {
979                     // blank string. Do nothing.
980                 } else {
981                     GError* parseError = 0;
982                     gchar** argv = 0;
983                     gint argc = 0;
984                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
985                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
986                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
987                         if ( ctx ) {
988                             GSList *fl = sp_process_args(ctx);
989                             sp_process_file_list(fl);
990                             poptFreeContext(ctx);
991                         } else {
992                             retval = 1; // not sure why. But this was the previous return value
993                         }
994                         resetCommandlineGlobals();
995                         g_strfreev(argv);
996                     } else {
997                         g_warning("Cannot parse commandline: %s", useme);
998                     }
999                 }
1000             }
1001         } // if (linedata...
1002     } while (linedata && (retval == 0));
1004     g_free(command_line);
1005     return retval;
1008 int sp_main_console(int argc, char const **argv)
1010     /* We are started in text mode */
1012     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1013      * in a non-Gtk environment.  Used in libnrtype's
1014      * FontInstance.cpp and FontFactory.cpp.
1015      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1016      */
1017     g_type_init();
1018     char **argv2 = const_cast<char **>(argv);
1019     gtk_init_check( &argc, &argv2 );
1020     //setlocale(LC_ALL, "");
1022     GSList *fl = NULL;
1023     int retVal = sp_common_main( argc, argv, &fl );
1024     g_return_val_if_fail(retVal == 0, 1);
1026     if (fl == NULL && !sp_shell) {
1027         g_print("Nothing to do!\n");
1028         exit(0);
1029     }
1031     inkscape_application_init(argv[0], false);
1033     if (sp_shell) {
1034         sp_main_shell(argv[0]); // Run as interactive shell
1035         exit(0);
1036     } else {
1037         sp_process_file_list(fl); // Normal command line invokation
1038     }
1040     return 0;
1043 static void
1044 do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id)
1046     SPObject *o = NULL;
1048     if (id) {
1049         o = doc->getObjectById(id);
1050         if (o) {
1051             if (!SP_IS_ITEM (o)) {
1052                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1053                 return;
1054             }
1055         } else {
1056             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1057             return;
1058         }
1059     } else {
1060         o = SP_DOCUMENT_ROOT(doc);
1061     }
1063     if (o) {
1064         sp_document_ensure_up_to_date (doc);
1065         SPItem *item = ((SPItem *) o);
1067         // "true" SVG bbox for scripting
1068         boost::optional<Geom::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
1069         if (area) {
1070             Inkscape::SVGOStringStream os;
1071             if (extent) {
1072                 os << area->dimensions()[axis];
1073             } else {
1074                 os << area->min()[axis];
1075             }
1076             g_print ("%s", os.str().c_str());
1077         } else {
1078             g_print("0");
1079         }
1080     }
1083 static void
1084 do_query_all (SPDocument *doc)
1086     SPObject *o = NULL;
1088     o = SP_DOCUMENT_ROOT(doc);
1090     if (o) {
1091         sp_document_ensure_up_to_date (doc);
1092         do_query_all_recurse(o);
1093     }
1096 static void
1097 do_query_all_recurse (SPObject *o)
1099     SPItem *item = ((SPItem *) o);
1100     if (o->id && SP_IS_ITEM(item)) {
1101         boost::optional<Geom::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
1102         if (area) {
1103             Inkscape::SVGOStringStream os;
1104             os << o->id;
1105             os << "," << area->min()[Geom::X];
1106             os << "," << area->min()[Geom::Y];
1107             os << "," << area->dimensions()[Geom::X];
1108             os << "," << area->dimensions()[Geom::Y];
1109             g_print ("%s\n", os.str().c_str());
1110         }
1111     }
1113     SPObject *child = o->children;
1114     while (child) {
1115         do_query_all_recurse (child);
1116         child = child->next;
1117     }
1121 static void
1122 sp_do_export_png(SPDocument *doc)
1124     const gchar *filename = NULL;
1125     gdouble dpi = 0.0;
1127     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1128         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1129     }
1131     GSList *items = NULL;
1133     NRRect area;
1134     if (sp_export_id || sp_export_area_drawing) {
1136         SPObject *o = NULL;
1137         SPObject *o_area = NULL;
1138         if (sp_export_id && sp_export_area_drawing) {
1139             o = doc->getObjectById(sp_export_id);
1140             o_area = SP_DOCUMENT_ROOT (doc);
1141         } else if (sp_export_id) {
1142             o = doc->getObjectById(sp_export_id);
1143             o_area = o;
1144         } else if (sp_export_area_drawing) {
1145             o = SP_DOCUMENT_ROOT (doc);
1146             o_area = o;
1147         }
1149         if (o) {
1150             if (!SP_IS_ITEM (o)) {
1151                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1152                 return;
1153             }
1155             items = g_slist_prepend (items, SP_ITEM(o));
1157             if (sp_export_id_only) {
1158                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1159             }
1161             if (sp_export_use_hints) {
1163                 // retrieve export filename hint
1164                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1165                 if (fn_hint) {
1166                     if (sp_export_png) {
1167                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1168                         filename = sp_export_png;
1169                     } else {
1170                         filename = fn_hint;
1171                     }
1172                 } else {
1173                     g_warning ("Export filename hint not found for the object.");
1174                     filename = sp_export_png;
1175                 }
1177                 // retrieve export dpi hints
1178                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1179                 if (dpi_hint) {
1180                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1181                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1182                     } else {
1183                         dpi = atof(dpi_hint);
1184                     }
1185                 } else {
1186                     g_warning ("Export DPI hint not found for the object.");
1187                 }
1189             }
1191             // write object bbox to area
1192             sp_document_ensure_up_to_date (doc);
1193             boost::optional<Geom::Rect> areaMaybe;
1194             sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2r_affine((SPItem *) o_area), TRUE);
1195             if (areaMaybe) {
1196                 area = NRRect(areaMaybe);
1197             } else {
1198                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1199                 return;
1200             }
1201         } else {
1202             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1203             return;
1204         }
1205     }
1207     if (sp_export_area) {
1208         /* Try to parse area (given in SVG pixels) */
1209         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &area.x0, &area.y0, &area.x1, &area.y1) == 4) {
1210             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1211             return;
1212         }
1213         if ((area.x0 >= area.x1) || (area.y0 >= area.y1)) {
1214             g_warning("Export area '%s' has negative width or height. Nothing exported.", sp_export_area);
1215             return;
1216         }
1217     } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1218         /* Export the whole canvas */
1219         sp_document_ensure_up_to_date (doc);
1220         area.x0 = SP_ROOT(doc->root)->x.computed;
1221         area.y0 = SP_ROOT(doc->root)->y.computed;
1222         area.x1 = area.x0 + sp_document_width (doc);
1223         area.y1 = area.y0 + sp_document_height (doc);
1224     }
1226     // set filename and dpi from options, if not yet set from the hints
1227     if (!filename) {
1228         if (!sp_export_png) {
1229             g_warning ("No export filename given and no filename hint. Nothing exported.");
1230             return;
1231         }
1232         filename = sp_export_png;
1233     }
1235     if (sp_export_dpi && dpi == 0.0) {
1236         dpi = atof(sp_export_dpi);
1237         if ((dpi < 0.1) || (dpi > 10000.0)) {
1238             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1239             return;
1240         }
1241         g_print("DPI: %g\n", dpi);
1242     }
1244     if (sp_export_area_snap) {
1245         area.x0 = std::floor (area.x0);
1246         area.y0 = std::floor (area.y0);
1247         area.x1 = std::ceil (area.x1);
1248         area.y1 = std::ceil (area.y1);
1249     }
1251     // default dpi
1252     if (dpi == 0.0) {
1253         dpi = PX_PER_IN;
1254     }
1256     unsigned long int width = 0;
1257     unsigned long int height = 0;
1259     if (sp_export_width) {
1260         errno=0;
1261         width = strtoul(sp_export_width, NULL, 0);
1262         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1263             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1264             return;
1265         }
1266         dpi = (gdouble) width * PX_PER_IN / (area.x1 - area.x0);
1267     }
1269     if (sp_export_height) {
1270         errno=0;
1271         height = strtoul(sp_export_height, NULL, 0);
1272         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1273             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1274             return;
1275         }
1276         dpi = (gdouble) height * PX_PER_IN / (area.y1 - area.y0);
1277     }
1279     if (!sp_export_width) {
1280         width = (unsigned long int) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1281     }
1283     if (!sp_export_height) {
1284         height = (unsigned long int) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1285     }
1287     guint32 bgcolor = 0x00000000;
1288     if (sp_export_background) {
1289         // override the page color
1290         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1291         bgcolor |= 0xff; // default is no opacity
1292     } else {
1293         // read from namedview
1294         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1295         if (nv && nv->attribute("pagecolor"))
1296             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1297         if (nv && nv->attribute("inkscape:pageopacity"))
1298             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1299     }
1301     if (sp_export_background_opacity) {
1302         // override opacity
1303         gfloat value;
1304         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1305             if (value > 1.0) {
1306                 value = CLAMP (value, 1.0f, 255.0f);
1307                 bgcolor &= (guint32) 0xffffff00;
1308                 bgcolor |= (guint32) floor(value);
1309             } else {
1310                 value = CLAMP (value, 0.0f, 1.0f);
1311                 bgcolor &= (guint32) 0xffffff00;
1312                 bgcolor |= SP_COLOR_F_TO_U(value);
1313             }
1314         }
1315     }
1317     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1319     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);
1321     g_print("Bitmap saved as: %s\n", filename);
1323     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1324         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);
1325     } else {
1326         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1327     }
1329     g_slist_free (items);
1333 /**
1334  *  Perform a PDF/PS/EPS export
1335  *
1336  *  \param doc Document to export.
1337  *  \param uri URI to export to.
1338  *  \param mime MIME type to export as.
1339  */
1341 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1343     Inkscape::Extension::DB::OutputList o;
1344     Inkscape::Extension::db.get_output_list(o);
1345     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1346     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1347         i++;
1348     }
1350     if (i == o.end())
1351     {
1352         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1353         return;
1354     }
1356     if (sp_export_id) {
1357         SPObject *o = doc->getObjectById(sp_export_id);
1358         if (o == NULL) {
1359             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1360             return;
1361         }
1362         (*i)->set_param_string ("exportId", sp_export_id);
1363     } else {
1364         (*i)->set_param_string ("exportId", "");
1365     }
1367     if (sp_export_area_canvas && sp_export_area_drawing) {
1368         g_warning ("You cannot use --export-area-canvas and --export-area-drawing at the same time; only the former will take effect.");
1369         sp_export_area_drawing = false;
1370     }
1372     if (sp_export_area_drawing) {
1373         (*i)->set_param_bool ("areaDrawing", TRUE);
1374     } else {
1375         (*i)->set_param_bool ("areaDrawing", FALSE);
1376     }
1378     if (sp_export_area_canvas) {
1379         if (sp_export_eps) {
1380             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.");
1381         } 
1382         (*i)->set_param_bool ("areaCanvas", TRUE);
1383     } else {
1384         (*i)->set_param_bool ("areaCanvas", FALSE);
1385     }
1387     if (!sp_export_area_drawing && !sp_export_area_canvas && !sp_export_id) { 
1388         // neither is set, set canvas as default for ps/pdf and drawing for eps
1389         if (sp_export_eps) {
1390             try {
1391                (*i)->set_param_bool("areaDrawing", TRUE);
1392             } catch (...) {}
1393         } 
1394     }
1396     if (sp_export_text_to_path) {
1397         (*i)->set_param_bool("textToPath", TRUE);
1398     } else {
1399         (*i)->set_param_bool("textToPath", FALSE);
1400     }
1402     (*i)->save(doc, uri);
1405 #ifdef WIN32
1406 /**
1407  *  Export a document to EMF
1408  *
1409  *  \param doc Document to export.
1410  *  \param uri URI to export to.
1411  *  \param mime MIME type to export as (should be "image/x-emf")
1412  */
1414 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1416     Inkscape::Extension::DB::OutputList o;
1417     Inkscape::Extension::db.get_output_list(o);
1418     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1419     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1420         i++;
1421     }
1423     if (i == o.end())
1424     {
1425         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1426         return;
1427     }
1429     (*i)->save(doc, uri);
1431 #endif //WIN32
1433 #ifdef WIN32
1434 bool replaceArgs( int& argc, char**& argv )
1436     bool worked = false;
1438 #ifdef REPLACEARGS_DEBUG
1439     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1440 #endif // REPLACEARGS_DEBUG
1442     wchar_t* line = GetCommandLineW();
1443     if ( line )
1444     {
1445 #ifdef REPLACEARGS_DEBUG
1446         {
1447             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1448             if ( utf8Line )
1449             {
1450                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1451                 {
1452                     char tmp[strlen(safe) + 32];
1453                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1454                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1455                 }
1456             }
1457         }
1458 #endif // REPLACEARGS_DEBUG
1460         int numArgs = 0;
1461         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1463 #ifdef REPLACEARGS_ANSI
1464 // test code for trying things on Win95/98/ME
1465         if ( !parsed )
1466         {
1467 #ifdef REPLACEARGS_DEBUG
1468             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1469 #endif // REPLACEARGS_DEBUG
1470             int lineLen = wcslen(line) + 1;
1471             wchar_t* lineDup = new wchar_t[lineLen];
1472             wcsncpy( lineDup, line, lineLen );
1474             int pos = 0;
1475             bool inQuotes = false;
1476             bool inWhitespace = true;
1477             std::vector<int> places;
1478             while ( lineDup[pos] )
1479             {
1480                 if ( inQuotes )
1481                 {
1482                     if ( lineDup[pos] == L'"' )
1483                     {
1484                         inQuotes = false;
1485                     }
1486                 }
1487                 else if ( lineDup[pos] == L'"' )
1488                 {
1489                     inQuotes = true;
1490                     inWhitespace = false;
1491                     places.push_back(pos);
1492                 }
1493                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1494                 {
1495                     if ( !inWhitespace )
1496                     {
1497                         inWhitespace = true;
1498                         lineDup[pos] = 0;
1499                     }
1500                 }
1501                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1502                 {
1503                     inWhitespace = false;
1504                     places.push_back(pos);
1505                 }
1506                 else
1507                 {
1508                     // consume
1509                 }
1510                 pos++;
1511             }
1512 #ifdef REPLACEARGS_DEBUG
1513             {
1514                 char tmp[256];
1515                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1516                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1517             }
1518 #endif // REPLACEARGS_DEBUG
1520             wchar_t** block = new wchar_t*[places.size()];
1521             int i = 0;
1522             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1523             {
1524                 block[i++] = &lineDup[*it];
1525             }
1526             parsed = block;
1527             numArgs = places.size();
1528         }
1529 #endif // REPLACEARGS_ANSI
1531         if ( parsed )
1532         {
1533             std::vector<wchar_t*>expandedArgs;
1534             if ( numArgs > 0 )
1535             {
1536                 expandedArgs.push_back( parsed[0] );
1537             }
1539             for ( int i1 = 1; i1 < numArgs; i1++ )
1540             {
1541                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1542                 wildcarded &= parsed[i1][0] != L'"';
1543                 wildcarded &= parsed[i1][0] != L'-';
1544                 if ( wildcarded )
1545                 {
1546 #ifdef REPLACEARGS_ANSI
1547                     WIN32_FIND_DATAA data;
1548 #else
1549                     WIN32_FIND_DATAW data;
1550 #endif // REPLACEARGS_ANSI
1552                     memset((void *)&data, 0, sizeof(data));
1554                     int baseLen = wcslen(parsed[i1]) + 2;
1555                     wchar_t* base = new wchar_t[baseLen];
1556                     wcsncpy( base, parsed[i1], baseLen );
1557                     wchar_t* last = wcsrchr( base, L'\\' );
1558                     if ( last )
1559                     {
1560                         last[1] = 0;
1561                     }
1562                     else
1563                     {
1564                         base[0] = 0;
1565                     }
1566                     baseLen = wcslen( base );
1568 #ifdef REPLACEARGS_ANSI
1569                     char target[MAX_PATH];
1570                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1571                     {
1572                         HANDLE hf = FindFirstFileA( target, &data );
1573 #else
1574                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1575 #endif // REPLACEARGS_ANSI
1576                         if ( hf != INVALID_HANDLE_VALUE )
1577                         {
1578                             BOOL found = TRUE;
1579                             do
1580                             {
1581 #ifdef REPLACEARGS_ANSI
1582                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1583                                 if ( howMany > 0 )
1584                                 {
1585                                     howMany += baseLen;
1586                                     wchar_t* tmp = new wchar_t[howMany + 1];
1587                                     wcsncpy( tmp, base, howMany + 1 );
1588                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1589                                     expandedArgs.push_back( tmp );
1590                                     found = FindNextFileA( hf, &data );
1591                                 }
1592 #else
1593                                 int howMany = wcslen(data.cFileName) + baseLen;
1594                                 wchar_t* tmp = new wchar_t[howMany + 1];
1595                                 wcsncpy( tmp, base, howMany + 1 );
1596                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1597                                 expandedArgs.push_back( tmp );
1598                                 found = FindNextFileW( hf, &data );
1599 #endif // REPLACEARGS_ANSI
1600                             } while ( found );
1602                             FindClose( hf );
1603                         }
1604                         else
1605                         {
1606                             expandedArgs.push_back( parsed[i1] );
1607                         }
1608 #ifdef REPLACEARGS_ANSI
1609                     }
1610 #endif // REPLACEARGS_ANSI
1612                     delete[] base;
1613                 }
1614                 else
1615                 {
1616                     expandedArgs.push_back( parsed[i1] );
1617                 }
1618             }
1620             {
1621                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1622                 int iz = 0;
1623                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1624                 {
1625                     block[iz++] = *it;
1626                 }
1627                 parsed = block;
1628                 numArgs = expandedArgs.size();
1629             }
1631             std::vector<gchar*> newArgs;
1632             for ( int i = 0; i < numArgs; i++ )
1633             {
1634                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1635                 if ( replacement )
1636                 {
1637 #ifdef REPLACEARGS_DEBUG
1638                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1640                     if ( safe2 )
1641                     {
1642                         {
1643                             char tmp[1024];
1644                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1645                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1646                         }
1647                         g_free( safe2 );
1648                     }
1649 #endif // REPLACEARGS_DEBUG
1651                     newArgs.push_back( replacement );
1652                 }
1653                 else
1654                 {
1655                     newArgs.push_back( blankParam );
1656                 }
1657             }
1659             // Now push our munged params to be the new argv and argc
1660             {
1661                 char** block = new char*[newArgs.size()];
1662                 int iz = 0;
1663                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1664                 {
1665                     block[iz++] = *it;
1666                 }
1667                 argv = block;
1668                 argc = newArgs.size();
1669                 worked = true;
1670             }
1671         }
1672 #ifdef REPLACEARGS_DEBUG
1673         else
1674         {
1675             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1676         }
1677 #endif // REPLACEARGS_DEBUG
1678     }
1679 #ifdef REPLACEARGS_DEBUG
1680     else
1681     {
1682         {
1683             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1684         }
1686         char* line2 = GetCommandLineA();
1687         if ( line2 )
1688         {
1689             gchar *safe = Inkscape::IO::sanitizeString(line2);
1690             {
1691                 {
1692                     char tmp[strlen(safe) + 32];
1693                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1694                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1695                 }
1696             }
1697         }
1698         else
1699         {
1700             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1701         }
1702     }
1703 #endif // REPLACEARGS_DEBUG
1705     return worked;
1707 #endif // WIN32
1709 static GSList *
1710 sp_process_args(poptContext ctx)
1712     GSList *fl = NULL;
1714     gint a;
1715     while ((a = poptGetNextOpt(ctx)) != -1) {
1716         switch (a) {
1717             case SP_ARG_FILE: {
1718                 gchar const *fn = poptGetOptArg(ctx);
1719                 if (fn != NULL) {
1720                     fl = g_slist_append(fl, g_strdup(fn));
1721                 }
1722                 break;
1723             }
1724             case SP_ARG_VERSION: {
1725                 printf("Inkscape %s (%s)\n", INKSCAPE_VERSION, __DATE__);
1726                 exit(0);
1727                 break;
1728             }
1729             case SP_ARG_EXTENSIONDIR: {
1730                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1731                 exit(0);
1732                 break;
1733             }
1734             case SP_ARG_VERB_LIST: {
1735                 // This really shouldn't go here, we should init the app.
1736                 // But, since we're just exiting in this path, there is
1737                 // no harm, and this is really a better place to put
1738                 // everything else.
1739                 Inkscape::Extension::init();
1740                 Inkscape::Verb::list();
1741                 exit(0);
1742                 break;
1743             }
1744             case SP_ARG_VERB:
1745             case SP_ARG_SELECT: {
1746                 gchar const *arg = poptGetOptArg(ctx);
1747                 if (arg != NULL) {
1748                     // printf("Adding in: %s\n", arg);
1749                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1750                 }
1751                 break;
1752             }
1753             case POPT_ERROR_BADOPT: {
1754                 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1755                 exit(1);
1756                 break;
1757             }
1758             default: {
1759                 break;
1760             }
1761         }
1762     }
1764     gchar const ** const args = poptGetArgs(ctx);
1765     if (args != NULL) {
1766         for (unsigned i = 0; args[i] != NULL; i++) {
1767             fl = g_slist_append(fl, g_strdup(args[i]));
1768         }
1769     }
1771     return fl;
1775 /*
1776   Local Variables:
1777   mode:c++
1778   c-file-style:"stroustrup"
1779   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1780   indent-tabs-mode:nil
1781   fill-column:99
1782   End:
1783 */
1784 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :