Code

5e1e6f64b357ce62f7ce185c6798f7aceefe6919
[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         tmp += G_SEARCHPATH_SEPARATOR;
519         tmp += oldenv;
520     }
521     g_setenv("PYTHONPATH", tmp.c_str(), TRUE);
522     
523     return 0;
527 /**
528  * This is the classic main() entry point of the program, though on some
529  * architectures it might be called by something else.
530  */
531 int
532 main(int argc, char **argv)
534 #ifdef HAVE_FPSETMASK
535     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
536        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
537        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
538     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
539 #endif
541 #ifdef WIN32
542     /*
543       Set the current directory to the directory of the
544       executable.  This seems redundant, but is needed for
545       when inkscape.exe is executed from another directory.
546       We use relative paths on win32.
547       HKCR\svgfile\shell\open\command is a good example
548     */
549     Glib::ustring homedir = _win32_getExePath();
550     SetCurrentDirectory(homedir.c_str());
551     _win32_set_inkscape_env(homedir);
552     RegistryTool rt;
553     rt.setPathInfo();
554 #endif
556     // Bug #197475
557     set_extensions_env();
559    /**
560     * Call bindtextdomain() for various machines's paths
561     */
562 #ifdef ENABLE_NLS
563 #ifdef WIN32
564     Glib::ustring localePath = homedir;
565     localePath += "\\";
566     localePath += PACKAGE_LOCALE_DIR;
567     bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
568 #else
569 #ifdef ENABLE_BINRELOC
570     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
571 #else
572     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
573 #endif
574 #endif
575     // Allow the user to override the locale directory by setting
576     // the environment variable INKSCAPE_LOCALEDIR.
577     char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
578     if (inkscape_localedir != NULL) {
579         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
580     }
581 #endif
583     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
585 #ifdef ENABLE_NLS
586     textdomain(GETTEXT_PACKAGE);
587 #endif
589     LIBXML_TEST_VERSION
591     Inkscape::GC::init();
593     Inkscape::Debug::Logger::init();
595     gboolean use_gui;
597 #ifndef WIN32
598     use_gui = (getenv("DISPLAY") != NULL);
599 #else
600     use_gui = TRUE;
601 #endif
602     /* Test whether with/without GUI is forced */
603     for (int i = 1; i < argc; i++) {
604         if (!strcmp(argv[i], "-z")
605             || !strcmp(argv[i], "--without-gui")
606             || !strcmp(argv[i], "-p")
607             || !strncmp(argv[i], "--print", 7)
608             || !strcmp(argv[i], "-e")
609             || !strncmp(argv[i], "--export-png", 12)
610             || !strcmp(argv[i], "-l")
611             || !strncmp(argv[i], "--export-plain-svg", 12)
612             || !strcmp(argv[i], "-i")
613             || !strncmp(argv[i], "--export-area-drawing", 21)
614             || !strcmp(argv[i], "-D")
615             || !strncmp(argv[i], "--export-area-canvas", 20)
616             || !strcmp(argv[i], "-C")
617             || !strncmp(argv[i], "--export-id", 12)
618             || !strcmp(argv[i], "-P")
619             || !strncmp(argv[i], "--export-ps", 11)
620             || !strcmp(argv[i], "-E")
621             || !strncmp(argv[i], "--export-eps", 12)
622             || !strcmp(argv[i], "-A")
623             || !strncmp(argv[i], "--export-pdf", 12)
624 #ifdef WIN32
625             || !strcmp(argv[i], "-M")
626             || !strncmp(argv[i], "--export-emf", 12)
627 #endif //WIN32
628             || !strcmp(argv[i], "-W")
629             || !strncmp(argv[i], "--query-width", 13)
630             || !strcmp(argv[i], "-H")
631             || !strncmp(argv[i], "--query-height", 14)
632             || !strcmp(argv[i], "-S")
633             || !strncmp(argv[i], "--query-all", 11)
634             || !strcmp(argv[i], "-X")
635             || !strncmp(argv[i], "--query-x", 13)
636             || !strcmp(argv[i], "-Y")
637             || !strncmp(argv[i], "--query-y", 14)
638             || !strcmp(argv[i], "--vacuum-defs")
639             || !strncmp(argv[i], "--shell", 7)
640            )
641         {
642             /* main_console handles any exports -- not the gui */
643             use_gui = FALSE;
644             break;
645         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
646             use_gui = TRUE;
647             break;
648         }
649     }
651 #ifdef WIN32
652 #ifndef REPLACEARGS_ANSI
653     if ( PrintWin32::is_os_wide() )
654 #endif // REPLACEARGS_ANSI
655     {
656         // If the call fails, we'll need to convert charsets
657         needToRecodeParams = !replaceArgs( argc, argv );
658     }
659 #endif // WIN32
661     /// \todo  Should this be a static object (see inkscape.cpp)?
662     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
664     return app.run();
670 void fixupSingleFilename( gchar **orig, gchar **spare )
672     if ( orig && *orig && **orig ) {
673         GError *error = NULL;
674         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
675         if ( newFileName )
676         {
677             *orig = newFileName;
678             if ( spare ) {
679                 *spare = newFileName;
680             }
681 //             g_message("Set a replacement fixup");
682         }
683     }
688 GSList *fixupFilenameEncoding( GSList* fl )
690     GSList *newFl = NULL;
691     while ( fl ) {
692         gchar *fn = static_cast<gchar*>(fl->data);
693         fl = g_slist_remove( fl, fl->data );
694         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
695         if ( newFileName ) {
697             if ( 0 )
698             {
699                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
700                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
701                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
702                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
703                 gtk_dialog_run (GTK_DIALOG (w));
704                 gtk_widget_destroy (w);
705                 g_free(safeNewFn);
706                 g_free(safeFn);
707             }
709             g_free( fn );
710             fn = newFileName;
711             newFileName = 0;
712         }
713         else
714             if ( 0 )
715         {
716             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
717             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
718             gtk_dialog_run (GTK_DIALOG (w));
719             gtk_widget_destroy (w);
720             g_free(safeFn);
721         }
722         newFl = g_slist_append( newFl, fn );
723     }
724     return newFl;
727 int sp_common_main( int argc, char const **argv, GSList **flDest )
729     /// \todo fixme: Move these to some centralized location (Lauris)
730     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
731     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
734     // temporarily switch gettext encoding to locale, so that help messages can be output properly
735     gchar const *charset;
736     g_get_charset(&charset);
738     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
740     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
741     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
742     g_return_val_if_fail(ctx != NULL, 1);
744     /* Collect own arguments */
745     GSList *fl = sp_process_args(ctx);
746     poptFreeContext(ctx);
748     // now switch gettext back to UTF-8 (for GUI)
749     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
751     // Now let's see if the file list still holds up
752     if ( needToRecodeParams )
753     {
754         fl = fixupFilenameEncoding( fl );
755     }
757     // Check the globals for filename-fixup
758     if ( needToRecodeParams )
759     {
760         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
761         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
762         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
763     }
764     else
765     {
766         if ( sp_export_png )
767             sp_export_png_utf8 = g_strdup( sp_export_png );
768         if ( sp_export_svg )
769             sp_export_svg_utf8 = g_strdup( sp_export_svg );
770         if ( sp_global_printer )
771             sp_global_printer_utf8 = g_strdup( sp_global_printer );
772     }
774     // Return the list if wanted, else free it up.
775     if ( flDest ) {
776         *flDest = fl;
777         fl = 0;
778     } else {
779         while ( fl ) {
780             g_free( fl->data );
781             fl = g_slist_remove( fl, fl->data );
782         }
783     }
784     return 0;
787 static void
788 snooper(GdkEvent *event, gpointer /*data*/) {
789     if(inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
790     {
791         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
792         switch (event->type) {
793             case GDK_MOTION_NOTIFY:
794                 if(event->motion.state & mapping) {
795                     event->motion.state|=GDK_MOD1_MASK;
796                 }
797                 break;
798             case GDK_BUTTON_PRESS:
799                 if(event->button.state & mapping) {
800                     event->button.state|=GDK_MOD1_MASK;
801                 }
802                 break;
803              case GDK_KEY_PRESS:
804                  if(event->key.state & mapping) {
805                      event->key.state|=GDK_MOD1_MASK;
806                  }
807                  break;
808         default:
809             break;
810         }
811     }
812     gtk_main_do_event (event);
815 int
816 sp_main_gui(int argc, char const **argv)
818     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
820     GSList *fl = NULL;
821     int retVal = sp_common_main( argc, argv, &fl );
822     g_return_val_if_fail(retVal == 0, 1);
824     inkscape_gtk_stock_init();
826     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
828     Inkscape::Debug::log_display_config();
830     /* Set default icon */
831     gchar *filename = (gchar *) g_build_filename (INKSCAPE_APPICONDIR, "inkscape.png", NULL);
832     if (Inkscape::IO::file_test(filename, (GFileTest)(G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))) {
833         gtk_window_set_default_icon_from_file(filename, NULL);
834     }
835     g_free (filename);
836     filename = 0;
838     gboolean create_new = TRUE;
840     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
841     inkscape_application_init(argv[0], true);
843     while (fl) {
844         if (sp_file_open((gchar *)fl->data,NULL)) {
845             create_new=FALSE;
846         }
847         fl = g_slist_remove(fl, fl->data);
848     }
849     if (create_new) {
850         sp_file_new_default();
851     }
853     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
854     main_instance.run();
856 #ifdef WIN32
857     //We might not need anything here
858     //sp_win32_finish(); <-- this is a NOP func
859 #endif
861     return 0;
864 /**
865  * Process file list
866  */
867 void sp_process_file_list(GSList *fl)
869     while (fl) {
870         const gchar *filename = (gchar *)fl->data;
871         SPDocument *doc = Inkscape::Extension::open(NULL, filename);
872         if (doc == NULL) {
873             doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
874         }
875         if (doc == NULL) {
876             g_warning("Specified document %s cannot be opened (is it a valid SVG file?)", filename);
877         } else {
878             if (sp_vacuum_defs) {
879                 vacuum_document(doc);
880             }
881             if (sp_vacuum_defs && !sp_export_svg) {
882                 // save under the name given in the command line
883                 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
884             }
885             if (sp_global_printer) {
886                 sp_print_document_to_file(doc, sp_global_printer);
887             }
888             if (sp_export_png) {
889                 sp_do_export_png(doc);
890             }
891             if (sp_export_svg) {
892                 Inkscape::XML::Document *rdoc;
893                 Inkscape::XML::Node *repr;
894                 rdoc = sp_repr_document_new("svg:svg");
895                 repr = rdoc->root();
896                 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
897                 sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
898             }
899             if (sp_export_ps) {
900                 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
901             }
902             if (sp_export_eps) {
903                 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
904             }
905             if (sp_export_pdf) {
906                 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
907             }
908 #ifdef WIN32
909             if (sp_export_emf) {
910                 do_export_emf(doc, sp_export_emf, "image/x-emf");
911             }
912 #endif //WIN32
913             if (sp_query_all) {
914                 do_query_all (doc);
915             } else if (sp_query_width || sp_query_height) {
916                 do_query_dimension (doc, true, sp_query_width? NR::X : NR::Y, sp_query_id);
917             } else if (sp_query_x || sp_query_y) {
918                 do_query_dimension (doc, false, sp_query_x? NR::X : NR::Y, sp_query_id);
919             }
921             delete doc;
922         }
923         fl = g_slist_remove(fl, fl->data);
924     }
927 /**
928  * Run the application as an interactive shell, parsing command lines from stdin
929  * Returns -1 on error.
930  */
931 int sp_main_shell(char const* command_name)
933     int retval = 0;
935     const unsigned int buffer_size = 4096;
936     gchar *command_line = g_strnfill(buffer_size, 0);
937     g_strlcpy(command_line, command_name, buffer_size);
938     gsize offset = g_strlcat(command_line, " ", buffer_size);
939     gsize sizeLeft = buffer_size - offset;
940     gchar *useme = command_line + offset;
942     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", INKSCAPE_VERSION);
943     fflush(stdout);
944     char* linedata = 0;
945     do {
946         fprintf(stdout, ">");
947         fflush(stdout);
948         if ((linedata = fgets(useme, sizeLeft, stdin))) {
949             size_t len = strlen(useme);
950             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
951                 fprintf(stdout, "ERROR: Command line too long\n");
952                 // Consume rest of line
953                 retval = -1; // If the while loop completes, this remains -1
954                 while (fgets(useme, sizeLeft, stdin) && retval) {
955                     len = strlen(command_line);
956                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
957                         retval = 0;
958                     }
959                 }
960             } else {
961                 useme[--len] = '\0';  // Strip newline
962                 if (useme[len - 1] == '\r') {
963                     useme[--len] = '\0';
964                 }
965                 if ( strcmp(useme, "quit") == 0 ) {
966                     // Time to quit
967                     fflush(stdout);
968                     linedata = 0; // mark for exit
969                 } else if ( len < 1 ) {
970                     // blank string. Do nothing.
971                 } else {
972                     GError* parseError = 0;
973                     gchar** argv = 0;
974                     gint argc = 0;
975                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
976                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
977                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
978                         if ( ctx ) {
979                             GSList *fl = sp_process_args(ctx);
980                             sp_process_file_list(fl);
981                             poptFreeContext(ctx);
982                         } else {
983                             retval = 1; // not sure why. But this was the previous return value
984                         }
985                         resetCommandlineGlobals();
986                         g_strfreev(argv);
987                     } else {
988                         g_warning("Cannot parse commandline: %s", useme);
989                     }
990                 }
991             }
992         } // if (linedata...
993     } while (linedata && (retval == 0));
995     g_free(command_line);
996     return retval;
999 int sp_main_console(int argc, char const **argv)
1001     /* We are started in text mode */
1003     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1004      * in a non-Gtk environment.  Used in libnrtype's
1005      * FontInstance.cpp and FontFactory.cpp.
1006      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1007      */
1008     g_type_init();
1009     char **argv2 = const_cast<char **>(argv);
1010     gtk_init_check( &argc, &argv2 );
1011     //setlocale(LC_ALL, "");
1013     GSList *fl = NULL;
1014     int retVal = sp_common_main( argc, argv, &fl );
1015     g_return_val_if_fail(retVal == 0, 1);
1017     if (fl == NULL && !sp_shell) {
1018         g_print("Nothing to do!\n");
1019         exit(0);
1020     }
1022     inkscape_application_init(argv[0], false);
1024     if (sp_shell) {
1025         sp_main_shell(argv[0]); // Run as interactive shell
1026         exit(0);
1027     } else {
1028         sp_process_file_list(fl); // Normal command line invokation
1029     }
1031     return 0;
1034 static void
1035 do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id)
1037     SPObject *o = NULL;
1039     if (id) {
1040         o = doc->getObjectById(id);
1041         if (o) {
1042             if (!SP_IS_ITEM (o)) {
1043                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1044                 return;
1045             }
1046         } else {
1047             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1048             return;
1049         }
1050     } else {
1051         o = SP_DOCUMENT_ROOT(doc);
1052     }
1054     if (o) {
1055         sp_document_ensure_up_to_date (doc);
1056         SPItem *item = ((SPItem *) o);
1058         // "true" SVG bbox for scripting
1059         boost::optional<Geom::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
1060         if (area) {
1061             Inkscape::SVGOStringStream os;
1062             if (extent) {
1063                 os << area->dimensions()[axis];
1064             } else {
1065                 os << area->min()[axis];
1066             }
1067             g_print ("%s", os.str().c_str());
1068         } else {
1069             g_print("0");
1070         }
1071     }
1074 static void
1075 do_query_all (SPDocument *doc)
1077     SPObject *o = NULL;
1079     o = SP_DOCUMENT_ROOT(doc);
1081     if (o) {
1082         sp_document_ensure_up_to_date (doc);
1083         do_query_all_recurse(o);
1084     }
1087 static void
1088 do_query_all_recurse (SPObject *o)
1090     SPItem *item = ((SPItem *) o);
1091     if (o->id && SP_IS_ITEM(item)) {
1092         boost::optional<Geom::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
1093         if (area) {
1094             Inkscape::SVGOStringStream os;
1095             os << o->id;
1096             os << "," << area->min()[Geom::X];
1097             os << "," << area->min()[Geom::Y];
1098             os << "," << area->dimensions()[Geom::X];
1099             os << "," << area->dimensions()[Geom::Y];
1100             g_print ("%s\n", os.str().c_str());
1101         }
1102     }
1104     SPObject *child = o->children;
1105     while (child) {
1106         do_query_all_recurse (child);
1107         child = child->next;
1108     }
1112 static void
1113 sp_do_export_png(SPDocument *doc)
1115     const gchar *filename = NULL;
1116     gdouble dpi = 0.0;
1118     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1119         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1120     }
1122     GSList *items = NULL;
1124     NRRect area;
1125     if (sp_export_id || sp_export_area_drawing) {
1127         SPObject *o = NULL;
1128         SPObject *o_area = NULL;
1129         if (sp_export_id && sp_export_area_drawing) {
1130             o = doc->getObjectById(sp_export_id);
1131             o_area = SP_DOCUMENT_ROOT (doc);
1132         } else if (sp_export_id) {
1133             o = doc->getObjectById(sp_export_id);
1134             o_area = o;
1135         } else if (sp_export_area_drawing) {
1136             o = SP_DOCUMENT_ROOT (doc);
1137             o_area = o;
1138         }
1140         if (o) {
1141             if (!SP_IS_ITEM (o)) {
1142                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1143                 return;
1144             }
1146             items = g_slist_prepend (items, SP_ITEM(o));
1148             if (sp_export_id_only) {
1149                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1150             }
1152             if (sp_export_use_hints) {
1154                 // retrieve export filename hint
1155                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1156                 if (fn_hint) {
1157                     if (sp_export_png) {
1158                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1159                         filename = sp_export_png;
1160                     } else {
1161                         filename = fn_hint;
1162                     }
1163                 } else {
1164                     g_warning ("Export filename hint not found for the object.");
1165                     filename = sp_export_png;
1166                 }
1168                 // retrieve export dpi hints
1169                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1170                 if (dpi_hint) {
1171                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1172                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1173                     } else {
1174                         dpi = atof(dpi_hint);
1175                     }
1176                 } else {
1177                     g_warning ("Export DPI hint not found for the object.");
1178                 }
1180             }
1182             // write object bbox to area
1183             sp_document_ensure_up_to_date (doc);
1184             boost::optional<Geom::Rect> areaMaybe;
1185             sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2r_affine((SPItem *) o_area), TRUE);
1186             if (areaMaybe) {
1187                 area = NRRect(areaMaybe);
1188             } else {
1189                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1190                 return;
1191             }
1192         } else {
1193             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1194             return;
1195         }
1196     }
1198     if (sp_export_area) {
1199         /* Try to parse area (given in SVG pixels) */
1200         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &area.x0, &area.y0, &area.x1, &area.y1) == 4) {
1201             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1202             return;
1203         }
1204         if ((area.x0 >= area.x1) || (area.y0 >= area.y1)) {
1205             g_warning("Export area '%s' has negative width or height. Nothing exported.", sp_export_area);
1206             return;
1207         }
1208     } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1209         /* Export the whole canvas */
1210         sp_document_ensure_up_to_date (doc);
1211         area.x0 = SP_ROOT(doc->root)->x.computed;
1212         area.y0 = SP_ROOT(doc->root)->y.computed;
1213         area.x1 = area.x0 + sp_document_width (doc);
1214         area.y1 = area.y0 + sp_document_height (doc);
1215     }
1217     // set filename and dpi from options, if not yet set from the hints
1218     if (!filename) {
1219         if (!sp_export_png) {
1220             g_warning ("No export filename given and no filename hint. Nothing exported.");
1221             return;
1222         }
1223         filename = sp_export_png;
1224     }
1226     if (sp_export_dpi && dpi == 0.0) {
1227         dpi = atof(sp_export_dpi);
1228         if ((dpi < 0.1) || (dpi > 10000.0)) {
1229             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1230             return;
1231         }
1232         g_print("DPI: %g\n", dpi);
1233     }
1235     if (sp_export_area_snap) {
1236         area.x0 = std::floor (area.x0);
1237         area.y0 = std::floor (area.y0);
1238         area.x1 = std::ceil (area.x1);
1239         area.y1 = std::ceil (area.y1);
1240     }
1242     // default dpi
1243     if (dpi == 0.0) {
1244         dpi = PX_PER_IN;
1245     }
1247     unsigned long int width = 0;
1248     unsigned long int height = 0;
1250     if (sp_export_width) {
1251         errno=0;
1252         width = strtoul(sp_export_width, NULL, 0);
1253         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1254             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1255             return;
1256         }
1257         dpi = (gdouble) width * PX_PER_IN / (area.x1 - area.x0);
1258     }
1260     if (sp_export_height) {
1261         errno=0;
1262         height = strtoul(sp_export_height, NULL, 0);
1263         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1264             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1265             return;
1266         }
1267         dpi = (gdouble) height * PX_PER_IN / (area.y1 - area.y0);
1268     }
1270     if (!sp_export_width) {
1271         width = (unsigned long int) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1272     }
1274     if (!sp_export_height) {
1275         height = (unsigned long int) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1276     }
1278     guint32 bgcolor = 0x00000000;
1279     if (sp_export_background) {
1280         // override the page color
1281         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1282         bgcolor |= 0xff; // default is no opacity
1283     } else {
1284         // read from namedview
1285         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1286         if (nv && nv->attribute("pagecolor"))
1287             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1288         if (nv && nv->attribute("inkscape:pageopacity"))
1289             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1290     }
1292     if (sp_export_background_opacity) {
1293         // override opacity
1294         gfloat value;
1295         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1296             if (value > 1.0) {
1297                 value = CLAMP (value, 1.0f, 255.0f);
1298                 bgcolor &= (guint32) 0xffffff00;
1299                 bgcolor |= (guint32) floor(value);
1300             } else {
1301                 value = CLAMP (value, 0.0f, 1.0f);
1302                 bgcolor &= (guint32) 0xffffff00;
1303                 bgcolor |= SP_COLOR_F_TO_U(value);
1304             }
1305         }
1306     }
1308     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1310     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);
1312     g_print("Bitmap saved as: %s\n", filename);
1314     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1315         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);
1316     } else {
1317         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1318     }
1320     g_slist_free (items);
1324 /**
1325  *  Perform a PDF/PS/EPS export
1326  *
1327  *  \param doc Document to export.
1328  *  \param uri URI to export to.
1329  *  \param mime MIME type to export as.
1330  */
1332 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1334     Inkscape::Extension::DB::OutputList o;
1335     Inkscape::Extension::db.get_output_list(o);
1336     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1337     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1338         i++;
1339     }
1341     if (i == o.end())
1342     {
1343         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1344         return;
1345     }
1347     if (sp_export_id) {
1348         SPObject *o = doc->getObjectById(sp_export_id);
1349         if (o == NULL) {
1350             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1351             return;
1352         }
1353         (*i)->set_param_string ("exportId", sp_export_id);
1354     } else {
1355         (*i)->set_param_string ("exportId", "");
1356     }
1358     if (sp_export_area_canvas && sp_export_area_drawing) {
1359         g_warning ("You cannot use --export-area-canvas and --export-area-drawing at the same time; only the former will take effect.");
1360         sp_export_area_drawing = false;
1361     }
1363     if (sp_export_area_drawing) {
1364         (*i)->set_param_bool ("areaDrawing", TRUE);
1365     } else {
1366         (*i)->set_param_bool ("areaDrawing", FALSE);
1367     }
1369     if (sp_export_area_canvas) {
1370         if (sp_export_eps) {
1371             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.");
1372         } 
1373         (*i)->set_param_bool ("areaCanvas", TRUE);
1374     } else {
1375         (*i)->set_param_bool ("areaCanvas", FALSE);
1376     }
1378     if (!sp_export_area_drawing && !sp_export_area_canvas && !sp_export_id) { 
1379         // neither is set, set canvas as default for ps/pdf and drawing for eps
1380         if (sp_export_eps) {
1381             try {
1382                (*i)->set_param_bool("areaDrawing", TRUE);
1383             } catch (...) {}
1384         } 
1385     }
1387     if (sp_export_text_to_path) {
1388         (*i)->set_param_bool("textToPath", TRUE);
1389     } else {
1390         (*i)->set_param_bool("textToPath", FALSE);
1391     }
1393     (*i)->save(doc, uri);
1396 #ifdef WIN32
1397 /**
1398  *  Export a document to EMF
1399  *
1400  *  \param doc Document to export.
1401  *  \param uri URI to export to.
1402  *  \param mime MIME type to export as (should be "image/x-emf")
1403  */
1405 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1407     Inkscape::Extension::DB::OutputList o;
1408     Inkscape::Extension::db.get_output_list(o);
1409     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1410     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1411         i++;
1412     }
1414     if (i == o.end())
1415     {
1416         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1417         return;
1418     }
1420     (*i)->save(doc, uri);
1422 #endif //WIN32
1424 #ifdef WIN32
1425 bool replaceArgs( int& argc, char**& argv )
1427     bool worked = false;
1429 #ifdef REPLACEARGS_DEBUG
1430     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1431 #endif // REPLACEARGS_DEBUG
1433     wchar_t* line = GetCommandLineW();
1434     if ( line )
1435     {
1436 #ifdef REPLACEARGS_DEBUG
1437         {
1438             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1439             if ( utf8Line )
1440             {
1441                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1442                 {
1443                     char tmp[strlen(safe) + 32];
1444                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1445                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1446                 }
1447             }
1448         }
1449 #endif // REPLACEARGS_DEBUG
1451         int numArgs = 0;
1452         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1454 #ifdef REPLACEARGS_ANSI
1455 // test code for trying things on Win95/98/ME
1456         if ( !parsed )
1457         {
1458 #ifdef REPLACEARGS_DEBUG
1459             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1460 #endif // REPLACEARGS_DEBUG
1461             int lineLen = wcslen(line) + 1;
1462             wchar_t* lineDup = new wchar_t[lineLen];
1463             wcsncpy( lineDup, line, lineLen );
1465             int pos = 0;
1466             bool inQuotes = false;
1467             bool inWhitespace = true;
1468             std::vector<int> places;
1469             while ( lineDup[pos] )
1470             {
1471                 if ( inQuotes )
1472                 {
1473                     if ( lineDup[pos] == L'"' )
1474                     {
1475                         inQuotes = false;
1476                     }
1477                 }
1478                 else if ( lineDup[pos] == L'"' )
1479                 {
1480                     inQuotes = true;
1481                     inWhitespace = false;
1482                     places.push_back(pos);
1483                 }
1484                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1485                 {
1486                     if ( !inWhitespace )
1487                     {
1488                         inWhitespace = true;
1489                         lineDup[pos] = 0;
1490                     }
1491                 }
1492                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1493                 {
1494                     inWhitespace = false;
1495                     places.push_back(pos);
1496                 }
1497                 else
1498                 {
1499                     // consume
1500                 }
1501                 pos++;
1502             }
1503 #ifdef REPLACEARGS_DEBUG
1504             {
1505                 char tmp[256];
1506                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1507                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1508             }
1509 #endif // REPLACEARGS_DEBUG
1511             wchar_t** block = new wchar_t*[places.size()];
1512             int i = 0;
1513             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1514             {
1515                 block[i++] = &lineDup[*it];
1516             }
1517             parsed = block;
1518             numArgs = places.size();
1519         }
1520 #endif // REPLACEARGS_ANSI
1522         if ( parsed )
1523         {
1524             std::vector<wchar_t*>expandedArgs;
1525             if ( numArgs > 0 )
1526             {
1527                 expandedArgs.push_back( parsed[0] );
1528             }
1530             for ( int i1 = 1; i1 < numArgs; i1++ )
1531             {
1532                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1533                 wildcarded &= parsed[i1][0] != L'"';
1534                 wildcarded &= parsed[i1][0] != L'-';
1535                 if ( wildcarded )
1536                 {
1537 #ifdef REPLACEARGS_ANSI
1538                     WIN32_FIND_DATAA data;
1539 #else
1540                     WIN32_FIND_DATAW data;
1541 #endif // REPLACEARGS_ANSI
1543                     memset((void *)&data, 0, sizeof(data));
1545                     int baseLen = wcslen(parsed[i1]) + 2;
1546                     wchar_t* base = new wchar_t[baseLen];
1547                     wcsncpy( base, parsed[i1], baseLen );
1548                     wchar_t* last = wcsrchr( base, L'\\' );
1549                     if ( last )
1550                     {
1551                         last[1] = 0;
1552                     }
1553                     else
1554                     {
1555                         base[0] = 0;
1556                     }
1557                     baseLen = wcslen( base );
1559 #ifdef REPLACEARGS_ANSI
1560                     char target[MAX_PATH];
1561                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1562                     {
1563                         HANDLE hf = FindFirstFileA( target, &data );
1564 #else
1565                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1566 #endif // REPLACEARGS_ANSI
1567                         if ( hf != INVALID_HANDLE_VALUE )
1568                         {
1569                             BOOL found = TRUE;
1570                             do
1571                             {
1572 #ifdef REPLACEARGS_ANSI
1573                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1574                                 if ( howMany > 0 )
1575                                 {
1576                                     howMany += baseLen;
1577                                     wchar_t* tmp = new wchar_t[howMany + 1];
1578                                     wcsncpy( tmp, base, howMany + 1 );
1579                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1580                                     expandedArgs.push_back( tmp );
1581                                     found = FindNextFileA( hf, &data );
1582                                 }
1583 #else
1584                                 int howMany = wcslen(data.cFileName) + baseLen;
1585                                 wchar_t* tmp = new wchar_t[howMany + 1];
1586                                 wcsncpy( tmp, base, howMany + 1 );
1587                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1588                                 expandedArgs.push_back( tmp );
1589                                 found = FindNextFileW( hf, &data );
1590 #endif // REPLACEARGS_ANSI
1591                             } while ( found );
1593                             FindClose( hf );
1594                         }
1595                         else
1596                         {
1597                             expandedArgs.push_back( parsed[i1] );
1598                         }
1599 #ifdef REPLACEARGS_ANSI
1600                     }
1601 #endif // REPLACEARGS_ANSI
1603                     delete[] base;
1604                 }
1605                 else
1606                 {
1607                     expandedArgs.push_back( parsed[i1] );
1608                 }
1609             }
1611             {
1612                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1613                 int iz = 0;
1614                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1615                 {
1616                     block[iz++] = *it;
1617                 }
1618                 parsed = block;
1619                 numArgs = expandedArgs.size();
1620             }
1622             std::vector<gchar*> newArgs;
1623             for ( int i = 0; i < numArgs; i++ )
1624             {
1625                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1626                 if ( replacement )
1627                 {
1628 #ifdef REPLACEARGS_DEBUG
1629                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1631                     if ( safe2 )
1632                     {
1633                         {
1634                             char tmp[1024];
1635                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1636                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1637                         }
1638                         g_free( safe2 );
1639                     }
1640 #endif // REPLACEARGS_DEBUG
1642                     newArgs.push_back( replacement );
1643                 }
1644                 else
1645                 {
1646                     newArgs.push_back( blankParam );
1647                 }
1648             }
1650             // Now push our munged params to be the new argv and argc
1651             {
1652                 char** block = new char*[newArgs.size()];
1653                 int iz = 0;
1654                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1655                 {
1656                     block[iz++] = *it;
1657                 }
1658                 argv = block;
1659                 argc = newArgs.size();
1660                 worked = true;
1661             }
1662         }
1663 #ifdef REPLACEARGS_DEBUG
1664         else
1665         {
1666             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1667         }
1668 #endif // REPLACEARGS_DEBUG
1669     }
1670 #ifdef REPLACEARGS_DEBUG
1671     else
1672     {
1673         {
1674             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1675         }
1677         char* line2 = GetCommandLineA();
1678         if ( line2 )
1679         {
1680             gchar *safe = Inkscape::IO::sanitizeString(line2);
1681             {
1682                 {
1683                     char tmp[strlen(safe) + 32];
1684                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1685                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1686                 }
1687             }
1688         }
1689         else
1690         {
1691             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1692         }
1693     }
1694 #endif // REPLACEARGS_DEBUG
1696     return worked;
1698 #endif // WIN32
1700 static GSList *
1701 sp_process_args(poptContext ctx)
1703     GSList *fl = NULL;
1705     gint a;
1706     while ((a = poptGetNextOpt(ctx)) != -1) {
1707         switch (a) {
1708             case SP_ARG_FILE: {
1709                 gchar const *fn = poptGetOptArg(ctx);
1710                 if (fn != NULL) {
1711                     fl = g_slist_append(fl, g_strdup(fn));
1712                 }
1713                 break;
1714             }
1715             case SP_ARG_VERSION: {
1716                 printf("Inkscape %s (%s)\n", INKSCAPE_VERSION, __DATE__);
1717                 exit(0);
1718                 break;
1719             }
1720             case SP_ARG_EXTENSIONDIR: {
1721                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1722                 exit(0);
1723                 break;
1724             }
1725             case SP_ARG_VERB_LIST: {
1726                 // This really shouldn't go here, we should init the app.
1727                 // But, since we're just exiting in this path, there is
1728                 // no harm, and this is really a better place to put
1729                 // everything else.
1730                 Inkscape::Extension::init();
1731                 Inkscape::Verb::list();
1732                 exit(0);
1733                 break;
1734             }
1735             case SP_ARG_VERB:
1736             case SP_ARG_SELECT: {
1737                 gchar const *arg = poptGetOptArg(ctx);
1738                 if (arg != NULL) {
1739                     // printf("Adding in: %s\n", arg);
1740                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1741                 }
1742                 break;
1743             }
1744             case POPT_ERROR_BADOPT: {
1745                 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1746                 exit(1);
1747                 break;
1748             }
1749             default: {
1750                 break;
1751             }
1752         }
1753     }
1755     gchar const ** const args = poptGetArgs(ctx);
1756     if (args != NULL) {
1757         for (unsigned i = 0; args[i] != NULL; i++) {
1758             fl = g_slist_append(fl, g_strdup(args[i]));
1759         }
1760     }
1762     return fl;
1766 /*
1767   Local Variables:
1768   mode:c++
1769   c-file-style:"stroustrup"
1770   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1771   indent-tabs-mode:nil
1772   fill-column:99
1773   End:
1774 */
1775 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :