Code

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