Code

Replace char buffer by std::string to prevent buffer overflow when computing the...
[inkscape.git] / src / main.cpp
1 #define __MAIN_C__
3 /** \file
4  * Inkscape - an ambitious vector drawing program
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Frank Felfe <innerspace@iname.com>
9  *   Davide Puricelli <evo@debian.org>
10  *   Mitsuru Oka <oka326@parkcity.ne.jp>
11  *   Masatake YAMATO  <jet@gyve.org>
12  *   F.J.Franklin <F.J.Franklin@sheffield.ac.uk>
13  *   Michael Meeks <michael@helixcode.com>
14  *   Chema Celorio <chema@celorio.com>
15  *   Pawel Palucha
16  *   Bryce Harrington <bryce@bryceharrington.com>
17  * ... and various people who have worked with various projects
18  *
19  * Copyright (C) 1999-2004 authors
20  * Copyright (C) 2001-2002 Ximian, Inc.
21  *
22  * Released under GNU GPL, read the file 'COPYING' for more information
23  */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 #include "path-prefix.h"
31 #include <gtk/gtkmessagedialog.h>
33 #ifdef HAVE_IEEEFP_H
34 #include <ieeefp.h>
35 #endif
36 #include <cstring>
37 #include <string>
38 #include <locale.h>
40 #include <popt.h>
41 #ifndef POPT_TABLEEND
42 #define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
43 #endif /* Not def: POPT_TABLEEND */
45 #include <libxml/tree.h>
46 #include <glib-object.h>
47 #include <gtk/gtkmain.h>
48 #include <gtk/gtksignal.h>
49 #include <gtk/gtkwindow.h>
50 #include <gtk/gtkbox.h>
52 #include "gc-core.h"
54 #include "macros.h"
55 #include "file.h"
56 #include "document.h"
57 #include "sp-object.h"
58 #include "interface.h"
59 #include "print.h"
60 #include "color.h"
61 #include "sp-item.h"
62 #include "sp-root.h"
63 #include "unit-constants.h"
65 #include "svg/svg.h"
66 #include "svg/svg-color.h"
67 #include "svg/stringstream.h"
69 #include "inkscape-private.h"
70 #include "inkscape-stock.h"
71 #include "inkscape_version.h"
73 #include "sp-namedview.h"
74 #include "sp-guide.h"
75 #include "sp-object-repr.h"
76 #include "xml/repr.h"
78 #include "io/sys.h"
80 #include "debug/logger.h"
81 #include "debug/log-display-config.h"
83 #include "helper/png-write.h"
85 #include <extension/extension.h>
86 #include <extension/system.h>
87 #include <extension/db.h>
88 #include <extension/output.h>
90 #ifdef WIN32
91 //#define REPLACEARGS_ANSI
92 //#define REPLACEARGS_DEBUG
94 #include "registrytool.h"
96 #include "extension/internal/win32.h"
97 using Inkscape::Extension::Internal::PrintWin32;
99 #endif // WIN32
101 #include "extension/init.h"
103 #include <glibmm/i18n.h>
104 #include <gtkmm/main.h>
106 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
107 #define bind_textdomain_codeset(p,c)
108 #endif
110 #include "application/application.h"
112 #include "main-cmdlineact.h"
114 #include <png.h>
115 #include <errno.h>
117 enum {
118     SP_ARG_NONE,
119     SP_ARG_NOGUI,
120     SP_ARG_GUI,
121     SP_ARG_FILE,
122     SP_ARG_PRINT,
123     SP_ARG_EXPORT_PNG,
124     SP_ARG_EXPORT_DPI,
125     SP_ARG_EXPORT_AREA,
126     SP_ARG_EXPORT_AREA_DRAWING,
127     SP_ARG_EXPORT_AREA_CANVAS,
128     SP_ARG_EXPORT_AREA_SNAP,
129     SP_ARG_EXPORT_WIDTH,
130     SP_ARG_EXPORT_HEIGHT,
131     SP_ARG_EXPORT_ID,
132     SP_ARG_EXPORT_ID_ONLY,
133     SP_ARG_EXPORT_USE_HINTS,
134     SP_ARG_EXPORT_BACKGROUND,
135     SP_ARG_EXPORT_BACKGROUND_OPACITY,
136     SP_ARG_EXPORT_SVG,
137     SP_ARG_EXPORT_PS,
138     SP_ARG_EXPORT_EPS,
139     SP_ARG_EXPORT_PDF,
140 #ifdef WIN32
141     SP_ARG_EXPORT_EMF,
142 #endif //WIN32
143     SP_ARG_EXPORT_TEXT_TO_PATH,
144     SP_ARG_EXPORT_FONT,
145     SP_ARG_EXPORT_BBOX_PAGE,
146     SP_ARG_EXTENSIONDIR,
147     SP_ARG_FIT_PAGE_TO_DRAWING,
148     SP_ARG_QUERY_X,
149     SP_ARG_QUERY_Y,
150     SP_ARG_QUERY_WIDTH,
151     SP_ARG_QUERY_HEIGHT,
152     SP_ARG_QUERY_ALL,
153     SP_ARG_QUERY_ID,
154     SP_ARG_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(SPDocument* doc, gchar const* uri, char const *mime);
166 static void do_export_pdf(SPDocument* doc, gchar const* uri, char const *mime);
167 #ifdef WIN32
168 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
169 #endif //WIN32
170 static void do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id);
171 static void do_query_all (SPDocument *doc);
172 static void do_query_all_recurse (SPObject *o);
174 static gchar *sp_global_printer = NULL;
175 static gchar *sp_export_png = NULL;
176 static gchar *sp_export_dpi = NULL;
177 static gchar *sp_export_area = NULL;
178 static gboolean sp_export_area_drawing = FALSE;
179 static gboolean sp_export_area_canvas = FALSE;
180 static gchar *sp_export_width = NULL;
181 static gchar *sp_export_height = NULL;
182 static gchar *sp_export_id = NULL;
183 static gchar *sp_export_background = NULL;
184 static gchar *sp_export_background_opacity = NULL;
185 static gboolean sp_export_area_snap = FALSE;
186 static gboolean sp_export_use_hints = FALSE;
187 static gboolean sp_export_id_only = FALSE;
188 static gchar *sp_export_svg = NULL;
189 static gchar *sp_export_ps = NULL;
190 static gchar *sp_export_eps = NULL;
191 static gchar *sp_export_pdf = NULL;
192 #ifdef WIN32
193 static gchar *sp_export_emf = NULL;
194 #endif //WIN32
195 static gboolean sp_export_text_to_path = FALSE;
196 static gboolean sp_export_font = FALSE;
197 static gboolean sp_export_bbox_page = FALSE;
198 static gboolean sp_query_x = FALSE;
199 static gboolean sp_query_y = FALSE;
200 static gboolean sp_query_width = FALSE;
201 static gboolean sp_query_height = FALSE;
202 static gboolean sp_query_all = FALSE;
203 static gchar *sp_query_id = NULL;
204 static int sp_new_gui = FALSE;
205 static gboolean sp_vacuum_defs = FALSE;
207 static gchar *sp_export_png_utf8 = NULL;
208 static gchar *sp_export_svg_utf8 = NULL;
209 static gchar *sp_global_printer_utf8 = NULL;
211 #ifdef WIN32
212 static bool replaceArgs( int& argc, char**& argv );
213 #endif
214 static GSList *sp_process_args(poptContext ctx);
215 struct poptOption options[] = {
216     {"version", 'V',
217      POPT_ARG_NONE, NULL, SP_ARG_VERSION,
218      N_("Print the Inkscape version number"),
219      NULL},
221     {"without-gui", 'z',
222      POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
223      N_("Do not use X server (only process files from console)"),
224      NULL},
226     {"with-gui", 'g',
227      POPT_ARG_NONE, NULL, SP_ARG_GUI,
228      N_("Try to use X server (even if $DISPLAY is not set)"),
229      NULL},
231     {"file", 'f',
232      POPT_ARG_STRING, NULL, SP_ARG_FILE,
233      N_("Open specified document(s) (option string may be excluded)"),
234      N_("FILENAME")},
236     {"print", 'p',
237      POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
238      N_("Print document(s) to specified output file (use '| program' for pipe)"),
239      N_("FILENAME")},
241     {"export-png", 'e',
242      POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
243      N_("Export document to a PNG file"),
244      N_("FILENAME")},
246     {"export-dpi", 'd',
247      POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
248      N_("The resolution used for exporting SVG into bitmap (default 90)"),
249      N_("DPI")},
251     {"export-area", 'a',
252      POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
253      N_("Exported area in SVG user units (default is the canvas; 0,0 is lower-left corner)"),
254      N_("x0:y0:x1:y1")},
256     {"export-area-drawing", 'D',
257      POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
258      N_("Exported area is the entire drawing (not canvas)"),
259      NULL},
261     {"export-area-canvas", 'C',
262      POPT_ARG_NONE, &sp_export_area_canvas, SP_ARG_EXPORT_AREA_CANVAS,
263      N_("Exported area is the entire canvas"),
264      NULL},
266     {"export-area-snap", 0,
267      POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
268      N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
269      NULL},
271     {"export-width", 'w',
272      POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
273      N_("The width of exported bitmap in pixels (overrides export-dpi)"),
274      N_("WIDTH")},
276     {"export-height", 'h',
277      POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
278      N_("The height of exported bitmap in pixels (overrides export-dpi)"),
279      N_("HEIGHT")},
281     {"export-id", 'i',
282      POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
283      N_("The ID of the object to export"),
284      N_("ID")},
286     {"export-id-only", 'j',
287      POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
288      // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
289      //  See "man inkscape" for details.
290      N_("Export just the object with export-id, hide all others (only with export-id)"),
291      NULL},
293     {"export-use-hints", 't',
294      POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
295      N_("Use stored filename and DPI hints when exporting (only with export-id)"),
296      NULL},
298     {"export-background", 'b',
299      POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
300      N_("Background color of exported bitmap (any SVG-supported color string)"),
301      N_("COLOR")},
303     {"export-background-opacity", 'y',
304      POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
305      N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
306      N_("VALUE")},
308     {"export-plain-svg", 'l',
309      POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
310      N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
311      N_("FILENAME")},
313     {"export-ps", 'P',
314      POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
315      N_("Export document to a PS file"),
316      N_("FILENAME")},
318     {"export-eps", 'E',
319      POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
320      N_("Export document to an EPS file"),
321      N_("FILENAME")},
323     {"export-pdf", 'A',
324      POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
325      N_("Export document to a PDF file"),
326      N_("FILENAME")},
328 #ifdef WIN32
329     {"export-emf", 'M',
330      POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
331      N_("Export document to an Enhanced Metafile (EMF) File"),
332      N_("FILENAME")},
333 #endif //WIN32
335     {"export-text-to-path", 'T',
336      POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
337      N_("Convert text object to paths on export (EPS)"),
338      NULL},
340     {"export-embed-fonts", 'F',
341      POPT_ARG_NONE, &sp_export_font, SP_ARG_EXPORT_FONT,
342      N_("Embed fonts on export (Type 1 only) (EPS)"),
343      NULL},
345     {"export-bbox-page", 'B',
346      POPT_ARG_NONE, &sp_export_bbox_page, SP_ARG_EXPORT_BBOX_PAGE,
347      N_("Export files with the bounding box set to the page size (EPS)"),
348      NULL},
350     {"query-x", 'X',
351      POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
352      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
353      N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
354      NULL},
356     {"query-y", 'Y',
357      POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
358      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
359      N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
360      NULL},
362     {"query-width", 'W',
363      POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
364      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
365      N_("Query the width of the drawing or, if specified, of the object with --query-id"),
366      NULL},
368     {"query-height", 'H',
369      POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
370      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
371      N_("Query the height of the drawing or, if specified, of the object with --query-id"),
372      NULL},
374     {"query-all", 'S',
375      POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
376      N_("List id,x,y,w,h for all objects"),
377      NULL},
379     {"query-id", 'I',
380      POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
381      N_("The ID of the object whose dimensions are queried"),
382      N_("ID")},
384     {"extension-directory", 'x',
385      POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
386      // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
387      N_("Print out the extension directory and exit"),
388      NULL},
390     {"vacuum-defs", 0,
391      POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
392      N_("Remove unused definitions from the defs section(s) of the document"),
393      NULL},
395     {"verb-list", 0,
396      POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
397      N_("List the IDs of all the verbs in Inkscape"),
398      NULL},
400     {"verb", 0,
401      POPT_ARG_STRING, NULL, SP_ARG_VERB,
402      N_("Verb to call when Inkscape opens."),
403      N_("VERB-ID")},
405     {"select", 0,
406      POPT_ARG_STRING, NULL, SP_ARG_SELECT,
407      N_("Object ID to select when Inkscape opens."),
408      N_("OBJECT-ID")},
410     POPT_AUTOHELP POPT_TABLEEND
411 };
413 static bool needToRecodeParams = true;
414 gchar* blankParam = "";
416 #ifdef WIN32
417 static int _win32_set_inkscape_env(char *argv0)
419     CHAR szFullPath[_MAX_PATH];
421     CHAR szDrive[_MAX_DRIVE];
422     CHAR szDir[_MAX_DIR];
423     CHAR szFile[_MAX_FNAME];
424     CHAR szExt[_MAX_EXT];
426     std::string tmp;
428     if (GetModuleFileName(NULL, szFullPath, sizeof(szFullPath)) == 0) {
429         strcpy(szFullPath, argv0);
430     }
432     _splitpath(szFullPath, szDrive, szDir, szFile, szExt);
433     strcpy(szFullPath, szDrive);
434     strcat(szFullPath, szDir);
436     char *oldenv = getenv("PATH");
437     tmp = "PATH=";
438     tmp += szFullPath;
439     tmp += ";";
440     tmp += szFullPath;
441     tmp += "python;";
442     tmp += szFullPath;
443     tmp += "perl";
444     if(oldenv != NULL) {
445         tmp += ";";
446         tmp += oldenv;
447     }
448     _putenv(tmp.c_str());
450     oldenv = getenv("PYTHONPATH");
451     tmp = "PYTHONPATH=";
452     tmp += szFullPath;
453     tmp += "python;";
454     tmp += szFullPath;
455     tmp += "python\\Lib;";
456     tmp += szFullPath;
457     tmp += "python\\DLLs";
458     if(oldenv != NULL) {
459         tmp += ";";
460         tmp += oldenv;
461     }
462     _putenv(tmp.c_str());
464     return 0;
466 #endif
468 int
469 main(int argc, char **argv)
471 #ifdef HAVE_FPSETMASK
472     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
473        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
474        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
475     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
476 #endif
478 #ifdef ENABLE_NLS
479 #ifdef WIN32
480         _win32_set_inkscape_env(argv[0]);
481     RegistryTool rt;
482     rt.setPathInfo();
483     gchar *pathBuf = g_strconcat(g_path_get_dirname(argv[0]), "\\", PACKAGE_LOCALE_DIR, NULL);
484     bindtextdomain(GETTEXT_PACKAGE, pathBuf);
485     g_free(pathBuf);
486 #else
487 #ifdef ENABLE_BINRELOC
488     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
489 #else
490     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
491 #endif
492 #endif
493     // Allow the user to override the locale directory by setting
494     // the environment variable INKSCAPE_LOCALEDIR.
495     char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
496     if (inkscape_localedir != NULL) {
497         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
498     }
499 #endif
501     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
503 #ifdef ENABLE_NLS
504     textdomain(GETTEXT_PACKAGE);
505 #endif
507     LIBXML_TEST_VERSION
509     Inkscape::GC::init();
511     Inkscape::Debug::Logger::init();
513     gboolean use_gui;
514 #ifndef WIN32
515     use_gui = (getenv("DISPLAY") != NULL);
516 #else
517     /*
518       Set the current directory to the directory of the
519       executable.  This seems redundant, but is needed for
520       when inkscape.exe is executed from another directory.
521       We use relative paths on win32.
522       HKCR\svgfile\shell\open\command is a good example
523     */
524     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
525     char *homedir = g_path_get_dirname(argv[0]);
526     SetCurrentDirectory(homedir);
527     g_free(homedir);
529     use_gui = TRUE;
530 #endif
531     /* Test whether with/without GUI is forced */
532     for (int i = 1; i < argc; i++) {
533         if (!strcmp(argv[i], "-z")
534             || !strcmp(argv[i], "--without-gui")
535             || !strcmp(argv[i], "-p")
536             || !strncmp(argv[i], "--print", 7)
537             || !strcmp(argv[i], "-e")
538             || !strncmp(argv[i], "--export-png", 12)
539             || !strcmp(argv[i], "-l")
540             || !strncmp(argv[i], "--export-plain-svg", 12)
541             || !strcmp(argv[i], "-i")
542             || !strncmp(argv[i], "--export-area-drawing", 21)
543             || !strcmp(argv[i], "-D")
544             || !strncmp(argv[i], "--export-area-canvas", 20)
545             || !strcmp(argv[i], "-C")
546             || !strncmp(argv[i], "--export-id", 12)
547             || !strcmp(argv[i], "-P")
548             || !strncmp(argv[i], "--export-ps", 11)
549             || !strcmp(argv[i], "-E")
550             || !strncmp(argv[i], "--export-eps", 12)
551             || !strcmp(argv[i], "-A")
552             || !strncmp(argv[i], "--export-pdf", 12)
553 #ifdef WIN32
554             || !strcmp(argv[i], "-M")
555             || !strncmp(argv[i], "--export-emf", 12)
556 #endif //WIN32
557             || !strcmp(argv[i], "-W")
558             || !strncmp(argv[i], "--query-width", 13)
559             || !strcmp(argv[i], "-H")
560             || !strncmp(argv[i], "--query-height", 14)
561             || !strcmp(argv[i], "-S")
562             || !strncmp(argv[i], "--query-all", 11)
563             || !strcmp(argv[i], "-X")
564             || !strncmp(argv[i], "--query-x", 13)
565             || !strcmp(argv[i], "-Y")
566             || !strncmp(argv[i], "--query-y", 14)
567             || !strcmp(argv[i], "--vacuum-defs")
568            )
569         {
570             /* main_console handles any exports -- not the gui */
571             use_gui = FALSE;
572             break;
573         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
574             use_gui = TRUE;
575             break;
576         }
577     }
579 #ifdef WIN32
580 #ifndef REPLACEARGS_ANSI
581     if ( PrintWin32::is_os_wide() )
582 #endif // REPLACEARGS_ANSI
583     {
584         // If the call fails, we'll need to convert charsets
585         needToRecodeParams = !replaceArgs( argc, argv );
586     }
587 #endif // WIN32
589     /// \todo  Should this be a static object (see inkscape.cpp)?
590     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
592     return app.run();
595 void fixupSingleFilename( gchar **orig, gchar **spare )
597     if ( orig && *orig && **orig ) {
598         GError *error = NULL;
599         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
600         if ( newFileName )
601         {
602             *orig = newFileName;
603             if ( spare ) {
604                 *spare = newFileName;
605             }
606 //             g_message("Set a replacement fixup");
607         }
608     }
611 GSList *fixupFilenameEncoding( GSList* fl )
613     GSList *newFl = NULL;
614     while ( fl ) {
615         gchar *fn = static_cast<gchar*>(fl->data);
616         fl = g_slist_remove( fl, fl->data );
617         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
618         if ( newFileName ) {
620             if ( 0 )
621             {
622                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
623                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
624                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
625                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
626                 gtk_dialog_run (GTK_DIALOG (w));
627                 gtk_widget_destroy (w);
628                 g_free(safeNewFn);
629                 g_free(safeFn);
630             }
632             g_free( fn );
633             fn = newFileName;
634             newFileName = 0;
635         }
636         else
637             if ( 0 )
638         {
639             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
640             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
641             gtk_dialog_run (GTK_DIALOG (w));
642             gtk_widget_destroy (w);
643             g_free(safeFn);
644         }
645         newFl = g_slist_append( newFl, fn );
646     }
647     return newFl;
650 int sp_common_main( int argc, char const **argv, GSList **flDest )
652     /// \todo fixme: Move these to some centralized location (Lauris)
653     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
654     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
657     // temporarily switch gettext encoding to locale, so that help messages can be output properly
658     gchar const *charset;
659     g_get_charset(&charset);
661     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
663     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
664     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
665     g_return_val_if_fail(ctx != NULL, 1);
667     /* Collect own arguments */
668     GSList *fl = sp_process_args(ctx);
669     poptFreeContext(ctx);
671     // now switch gettext back to UTF-8 (for GUI)
672     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
674     // Now let's see if the file list still holds up
675     if ( needToRecodeParams )
676     {
677         fl = fixupFilenameEncoding( fl );
678     }
680     // Check the globals for filename-fixup
681     if ( needToRecodeParams )
682     {
683         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
684         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
685         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
686     }
687     else
688     {
689         if ( sp_export_png )
690             sp_export_png_utf8 = g_strdup( sp_export_png );
691         if ( sp_export_svg )
692             sp_export_svg_utf8 = g_strdup( sp_export_svg );
693         if ( sp_global_printer )
694             sp_global_printer_utf8 = g_strdup( sp_global_printer );
695     }
697     // Return the list if wanted, else free it up.
698     if ( flDest ) {
699         *flDest = fl;
700         fl = 0;
701     } else {
702         while ( fl ) {
703             g_free( fl->data );
704             fl = g_slist_remove( fl, fl->data );
705         }
706     }
707     return 0;
710 static void
711 snooper(GdkEvent *event, gpointer /*data*/) {
712     if(inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
713     {
714         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
715         switch (event->type) {
716             case GDK_MOTION_NOTIFY:
717                 if(event->motion.state & mapping) {
718                     event->motion.state|=GDK_MOD1_MASK;
719                 }
720                 break;
721             case GDK_BUTTON_PRESS:
722                 if(event->button.state & mapping) {
723                     event->button.state|=GDK_MOD1_MASK;
724                 }
725                 break;
726              case GDK_KEY_PRESS:
727                  if(event->key.state & mapping) {
728                      event->key.state|=GDK_MOD1_MASK;
729                  }
730                  break;
731         default:
732             break;
733         }
734     }
735     gtk_main_do_event (event);
738 int
739 sp_main_gui(int argc, char const **argv)
741     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
743     GSList *fl = NULL;
744     int retVal = sp_common_main( argc, argv, &fl );
745     g_return_val_if_fail(retVal == 0, 1);
747     inkscape_gtk_stock_init();
749     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
751     Inkscape::Debug::log_display_config();
753     /* Set default icon */
754     gchar *filename = (gchar *) g_build_filename (INKSCAPE_APPICONDIR, "inkscape.png", NULL);
755     if (Inkscape::IO::file_test(filename, (GFileTest)(G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))) {
756         gtk_window_set_default_icon_from_file(filename, NULL);
757     }
758     g_free (filename);
759     filename = 0;
761     gboolean create_new = TRUE;
763     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
764     inkscape_application_init(argv[0], true);
766     while (fl) {
767         if (sp_file_open((gchar *)fl->data,NULL)) {
768             create_new=FALSE;
769         }
770         fl = g_slist_remove(fl, fl->data);
771     }
772     if (create_new) {
773         sp_file_new_default();
774     }
776     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
777     main_instance.run();
779 #ifdef WIN32
780     //We might not need anything here
781     //sp_win32_finish(); <-- this is a NOP func
782 #endif
784     return 0;
787 int
788 sp_main_console(int argc, char const **argv)
790     /* We are started in text mode */
792     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
793      * in a non-Gtk environment.  Used in libnrtype's
794      * FontInstance.cpp and FontFactory.cpp.
795      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
796      */
797     g_type_init();
798     char **argv2 = const_cast<char **>(argv);
799     gtk_init_check( &argc, &argv2 );
800     //setlocale(LC_ALL, "");
802     GSList *fl = NULL;
803     int retVal = sp_common_main( argc, argv, &fl );
804     g_return_val_if_fail(retVal == 0, 1);
806     if (fl == NULL) {
807         g_print("Nothing to do!\n");
808         exit(0);
809     }
811     inkscape_application_init(argv[0], false);
813     while (fl) {
814         SPDocument *doc;
816         doc = Inkscape::Extension::open(NULL, (gchar *)fl->data);
817         if (doc == NULL) {
818             doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), (gchar *)fl->data);
819         }
820         if (doc == NULL) {
821             g_warning("Specified document %s cannot be opened (is it valid SVG file?)", (gchar *) fl->data);
822         } else {
823             if (sp_vacuum_defs) {
824                 vacuum_document(doc);
825             }
826             if (sp_vacuum_defs && !sp_export_svg) {
827                 // save under the name given in the command line
828                 sp_repr_save_file(doc->rdoc, (gchar *)fl->data, SP_SVG_NS_URI);
829             }
830             if (sp_global_printer) {
831                 sp_print_document_to_file(doc, sp_global_printer);
832             }
833             if (sp_export_png) {
834                 sp_do_export_png(doc);
835             }
836             if (sp_export_svg) {
837                 Inkscape::XML::Document *rdoc;
838                 Inkscape::XML::Node *repr;
839                 rdoc = sp_repr_document_new("svg:svg");
840                 repr = rdoc->root();
841                 repr = sp_document_root(doc)->updateRepr(repr, SP_OBJECT_WRITE_BUILD);
842                 sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
843             }
844             if (sp_export_ps) {
845                 do_export_ps(doc, sp_export_ps, "image/x-postscript");
846             }
847             if (sp_export_eps) {
848                 do_export_ps(doc, sp_export_eps, "image/x-e-postscript");
849             }
850             if (sp_export_pdf) {
851                 do_export_pdf(doc, sp_export_pdf, "application/pdf");
852             }
853 #ifdef WIN32
854             if (sp_export_emf) {
855                 do_export_emf(doc, sp_export_emf, "image/x-emf");
856             }
857 #endif //WIN32
858             if (sp_query_all) {
859                 do_query_all (doc);
860             } else if (sp_query_width || sp_query_height) {
861                 do_query_dimension (doc, true, sp_query_width? NR::X : NR::Y, sp_query_id);
862             } else if (sp_query_x || sp_query_y) {
863                 do_query_dimension (doc, false, sp_query_x? NR::X : NR::Y, sp_query_id);
864             }
865         }
867         fl = g_slist_remove(fl, fl->data);
868     }
870     inkscape_unref();
872     return 0;
875 static void
876 do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id)
878     SPObject *o = NULL;
880     if (id) {
881         o = doc->getObjectById(id);
882         if (o) {
883             if (!SP_IS_ITEM (o)) {
884                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
885                 return;
886             }
887         } else {
888             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
889             return;
890         }
891     } else {
892         o = SP_DOCUMENT_ROOT(doc);
893     }
895     if (o) {
896         sp_document_ensure_up_to_date (doc);
897         SPItem *item = ((SPItem *) o);
899         // "true" SVG bbox for scripting
900         NR::Maybe<NR::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
901         if (area) {
902             Inkscape::SVGOStringStream os;
903             if (extent) {
904                 os << area->extent(axis);
905             } else {
906                 os << area->min()[axis];
907             }
908             g_print ("%s", os.str().c_str());
909         } else {
910             g_print("0");
911         }
912     }
915 static void
916 do_query_all (SPDocument *doc)
918     SPObject *o = NULL;
920     o = SP_DOCUMENT_ROOT(doc);
922     if (o) {
923         sp_document_ensure_up_to_date (doc);
924         do_query_all_recurse(o);
925     }
928 static void
929 do_query_all_recurse (SPObject *o)
931     SPItem *item = ((SPItem *) o);
932     if (o->id && SP_IS_ITEM(item)) {
933         NR::Maybe<NR::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
934         if (area) {
935             Inkscape::SVGOStringStream os;
936             os << o->id;
937             os << "," << area->min()[NR::X];
938             os << "," << area->min()[NR::Y];
939             os << "," << area->extent(NR::X);
940             os << "," << area->extent(NR::Y);
941             g_print ("%s\n", os.str().c_str());
942         }
943     }
945     SPObject *child = o->children;
946     while (child) {
947         do_query_all_recurse (child);
948         child = child->next;
949     }
953 static void
954 sp_do_export_png(SPDocument *doc)
956     const gchar *filename = NULL;
957     gdouble dpi = 0.0;
959     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
960         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
961     }
963     GSList *items = NULL;
965     NRRect area;
966     if (sp_export_id || sp_export_area_drawing) {
968         SPObject *o = NULL;
969         SPObject *o_area = NULL;
970         if (sp_export_id && sp_export_area_drawing) {
971             o = doc->getObjectById(sp_export_id);
972             o_area = SP_DOCUMENT_ROOT (doc);
973         } else if (sp_export_id) {
974             o = doc->getObjectById(sp_export_id);
975             o_area = o;
976         } else if (sp_export_area_drawing) {
977             o = SP_DOCUMENT_ROOT (doc);
978             o_area = o;
979         }
981         if (o) {
982             if (!SP_IS_ITEM (o)) {
983                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
984                 return;
985             }
987             items = g_slist_prepend (items, SP_ITEM(o));
989             if (sp_export_id_only) {
990                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
991             }
993             if (sp_export_use_hints) {
995                 // retrieve export filename hint
996                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
997                 if (fn_hint) {
998                     if (sp_export_png) {
999                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1000                         filename = sp_export_png;
1001                     } else {
1002                         filename = fn_hint;
1003                     }
1004                 } else {
1005                     g_warning ("Export filename hint not found for the object.");
1006                     filename = sp_export_png;
1007                 }
1009                 // retrieve export dpi hints
1010                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1011                 if (dpi_hint) {
1012                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1013                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1014                     } else {
1015                         dpi = atof(dpi_hint);
1016                     }
1017                 } else {
1018                     g_warning ("Export DPI hint not found for the object.");
1019                 }
1021             }
1023             // write object bbox to area
1024             sp_document_ensure_up_to_date (doc);
1025             sp_item_invoke_bbox((SPItem *) o_area, &area, sp_item_i2r_affine((SPItem *) o_area), TRUE);
1026         } else {
1027             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1028             return;
1029         }
1030     }
1032     if (sp_export_area) {
1033         /* Try to parse area (given in SVG pixels) */
1034         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &area.x0, &area.y0, &area.x1, &area.y1) == 4) {
1035             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1036             return;
1037         }
1038         if ((area.x0 >= area.x1) || (area.y0 >= area.y1)) {
1039             g_warning("Export area '%s' has negative width or height. Nothing exported.", sp_export_area);
1040             return;
1041         }
1042     } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1043         /* Export the whole canvas */
1044         sp_document_ensure_up_to_date (doc);
1045         area.x0 = SP_ROOT(doc->root)->x.computed;
1046         area.y0 = SP_ROOT(doc->root)->y.computed;
1047         area.x1 = area.x0 + sp_document_width (doc);
1048         area.y1 = area.y0 + sp_document_height (doc);
1049     }
1051     // set filename and dpi from options, if not yet set from the hints
1052     if (!filename) {
1053         if (!sp_export_png) {
1054             g_warning ("No export filename given and no filename hint. Nothing exported.");
1055             return;
1056         }
1057         filename = sp_export_png;
1058     }
1060     if (sp_export_dpi && dpi == 0.0) {
1061         dpi = atof(sp_export_dpi);
1062         if ((dpi < 0.1) || (dpi > 10000.0)) {
1063             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1064             return;
1065         }
1066         g_print("DPI: %g\n", dpi);
1067     }
1069     if (sp_export_area_snap) {
1070         area.x0 = std::floor (area.x0);
1071         area.y0 = std::floor (area.y0);
1072         area.x1 = std::ceil (area.x1);
1073         area.y1 = std::ceil (area.y1);
1074     }
1076     // default dpi
1077     if (dpi == 0.0)
1078         dpi = PX_PER_IN;
1080     unsigned long int width = 0;
1081     unsigned long int height = 0;
1083     if (sp_export_width) {
1084         width = strtoul(sp_export_width, NULL, 0);
1085         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1086             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1087             return;
1088         }
1089         dpi = (gdouble) width * PX_PER_IN / (area.x1 - area.x0);
1090     }
1092     if (sp_export_height) {
1093         height = strtoul(sp_export_height, NULL, 0);
1094         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1095             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1096             return;
1097         }
1098         dpi = (gdouble) height * PX_PER_IN / (area.y1 - area.y0);
1099     }
1101     if (!sp_export_width) {
1102         width = (unsigned long int) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1103     }
1105     if (!sp_export_height) {
1106         height = (unsigned long int) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1107     }
1109     guint32 bgcolor = 0x00000000;
1110     if (sp_export_background) {
1111         // override the page color
1112         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1113         bgcolor |= 0xff; // default is no opacity
1114     } else {
1115         // read from namedview
1116         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1117         if (nv && nv->attribute("pagecolor"))
1118             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1119         if (nv && nv->attribute("inkscape:pageopacity"))
1120             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1121     }
1123     if (sp_export_background_opacity) {
1124         // override opacity
1125         gfloat value;
1126         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1127             if (value > 1.0) {
1128                 value = CLAMP (value, 1.0f, 255.0f);
1129                 bgcolor &= (guint32) 0xffffff00;
1130                 bgcolor |= (guint32) floor(value);
1131             } else {
1132                 value = CLAMP (value, 0.0f, 1.0f);
1133                 bgcolor &= (guint32) 0xffffff00;
1134                 bgcolor |= SP_COLOR_F_TO_U(value);
1135             }
1136         }
1137     }
1139     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1141     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);
1143     g_print("Bitmap saved as: %s\n", filename);
1145     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1146         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);
1147     } else {
1148         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1149     }
1151     g_slist_free (items);
1155 /**
1156  *  Perform an export of either PS or EPS.
1157  *
1158  *  \param doc Document to export.
1159  *  \param uri URI to export to.
1160  *  \param mime MIME type to export as.
1161  */
1163 static void do_export_ps(SPDocument* doc, gchar const* uri, char const* mime)
1165     Inkscape::Extension::DB::OutputList o;
1166     Inkscape::Extension::db.get_output_list(o);
1167     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1168     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1169         i++;
1170     }
1172     if (i == o.end())
1173     {
1174         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1175         return;
1176     }
1178     bool old_text_to_path = false;
1179     bool old_font_embedded = false;
1180     bool old_bbox_page = false;
1182     try {
1183         old_text_to_path = (*i)->get_param_bool("textToPath");
1184         (*i)->set_param_bool("textToPath", sp_export_text_to_path);
1185     }
1186     catch (...) {
1187         g_warning ("Could not set export-text-to-path option for this export.");
1188     }
1190     try {
1191         old_font_embedded = (*i)->get_param_bool("fontEmbedded");
1192         (*i)->set_param_bool("fontEmbedded", sp_export_font);
1193     }
1194     catch (...) {
1195         g_warning ("Could not set export-font option for this export.");
1196     }
1198     try {
1199         old_bbox_page = (*i)->get_param_bool("pageBoundingBox");
1200         (*i)->set_param_bool("pageBoundingBox", sp_export_bbox_page);
1201     }
1202     catch (...) {
1203         g_warning ("Could not set export-bbox-page option for this export.");
1204     }
1206     (*i)->save(doc, uri);
1208     try {
1209         (*i)->set_param_bool("textToPath", old_text_to_path);
1210         (*i)->set_param_bool("fontEmbedded", old_font_embedded);
1211         (*i)->set_param_bool("pageBoundingBox", old_bbox_page);
1212     }
1213     catch (...) {
1215     }
1218 /**
1219  *  Perform a PDF export
1220  *
1221  *  \param doc Document to export.
1222  *  \param uri URI to export to.
1223  *  \param mime MIME type to export as.
1224  */
1226 static void do_export_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1228     Inkscape::Extension::DB::OutputList o;
1229     Inkscape::Extension::db.get_output_list(o);
1230     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1231     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1232         i++;
1233     }
1235     if (i == o.end())
1236     {
1237         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1238         return;
1239     }
1241     if (sp_export_id) {
1242         SPObject *o = doc->getObjectById(sp_export_id);
1243         if (o == NULL) {
1244             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1245             return;
1246         }
1247         (*i)->set_param_string ("exportId", sp_export_id);
1248     } else {
1249         (*i)->set_param_string ("exportId", "");
1250     }
1252     if (sp_export_area_drawing) {
1253         (*i)->set_param_bool ("exportDrawing", TRUE);
1254     } else {
1255         (*i)->set_param_bool ("exportDrawing", FALSE);
1256     }
1258     (*i)->save(doc, uri);
1261 #ifdef WIN32
1262 /**
1263  *  Export a document to EMF
1264  *
1265  *  \param doc Document to export.
1266  *  \param uri URI to export to.
1267  *  \param mime MIME type to export as (should be "image/x-emf")
1268  */
1270 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1272     Inkscape::Extension::DB::OutputList o;
1273     Inkscape::Extension::db.get_output_list(o);
1274     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1275     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1276         i++;
1277     }
1279     if (i == o.end())
1280     {
1281         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1282         return;
1283     }
1285     (*i)->save(doc, uri);
1287 #endif //WIN32
1289 #ifdef WIN32
1290 bool replaceArgs( int& argc, char**& argv )
1292     bool worked = false;
1294 #ifdef REPLACEARGS_DEBUG
1295     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1296 #endif // REPLACEARGS_DEBUG
1298     wchar_t* line = GetCommandLineW();
1299     if ( line )
1300     {
1301 #ifdef REPLACEARGS_DEBUG
1302         {
1303             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1304             if ( utf8Line )
1305             {
1306                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1307                 {
1308                     char tmp[strlen(safe) + 32];
1309                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1310                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1311                 }
1312             }
1313         }
1314 #endif // REPLACEARGS_DEBUG
1316         int numArgs = 0;
1317         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1319 #ifdef REPLACEARGS_ANSI
1320 // test code for trying things on Win95/98/ME
1321         if ( !parsed )
1322         {
1323 #ifdef REPLACEARGS_DEBUG
1324             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1325 #endif // REPLACEARGS_DEBUG
1326             int lineLen = wcslen(line) + 1;
1327             wchar_t* lineDup = new wchar_t[lineLen];
1328             wcsncpy( lineDup, line, lineLen );
1330             int pos = 0;
1331             bool inQuotes = false;
1332             bool inWhitespace = true;
1333             std::vector<int> places;
1334             while ( lineDup[pos] )
1335             {
1336                 if ( inQuotes )
1337                 {
1338                     if ( lineDup[pos] == L'"' )
1339                     {
1340                         inQuotes = false;
1341                     }
1342                 }
1343                 else if ( lineDup[pos] == L'"' )
1344                 {
1345                     inQuotes = true;
1346                     inWhitespace = false;
1347                     places.push_back(pos);
1348                 }
1349                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1350                 {
1351                     if ( !inWhitespace )
1352                     {
1353                         inWhitespace = true;
1354                         lineDup[pos] = 0;
1355                     }
1356                 }
1357                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1358                 {
1359                     inWhitespace = false;
1360                     places.push_back(pos);
1361                 }
1362                 else
1363                 {
1364                     // consume
1365                 }
1366                 pos++;
1367             }
1368 #ifdef REPLACEARGS_DEBUG
1369             {
1370                 char tmp[256];
1371                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1372                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1373             }
1374 #endif // REPLACEARGS_DEBUG
1376             wchar_t** block = new wchar_t*[places.size()];
1377             int i = 0;
1378             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1379             {
1380                 block[i++] = &lineDup[*it];
1381             }
1382             parsed = block;
1383             numArgs = places.size();
1384         }
1385 #endif // REPLACEARGS_ANSI
1387         if ( parsed )
1388         {
1389             std::vector<wchar_t*>expandedArgs;
1390             if ( numArgs > 0 )
1391             {
1392                 expandedArgs.push_back( parsed[0] );
1393             }
1395             for ( int i1 = 1; i1 < numArgs; i1++ )
1396             {
1397                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1398                 wildcarded &= parsed[i1][0] != L'"';
1399                 wildcarded &= parsed[i1][0] != L'-';
1400                 if ( wildcarded )
1401                 {
1402 #ifdef REPLACEARGS_ANSI
1403                     WIN32_FIND_DATAA data = {0};
1404 #else
1405                     WIN32_FIND_DATAW data = {0};
1406 #endif // REPLACEARGS_ANSI
1408                     int baseLen = wcslen(parsed[i1]) + 2;
1409                     wchar_t* base = new wchar_t[baseLen];
1410                     wcsncpy( base, parsed[i1], baseLen );
1411                     wchar_t* last = wcsrchr( base, L'\\' );
1412                     if ( last )
1413                     {
1414                         last[1] = 0;
1415                     }
1416                     else
1417                     {
1418                         base[0] = 0;
1419                     }
1420                     baseLen = wcslen( base );
1422 #ifdef REPLACEARGS_ANSI
1423                     char target[MAX_PATH];
1424                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1425                     {
1426                         HANDLE hf = FindFirstFileA( target, &data );
1427 #else
1428                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1429 #endif // REPLACEARGS_ANSI
1430                         if ( hf != INVALID_HANDLE_VALUE )
1431                         {
1432                             BOOL found = TRUE;
1433                             do
1434                             {
1435 #ifdef REPLACEARGS_ANSI
1436                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1437                                 if ( howMany > 0 )
1438                                 {
1439                                     howMany += baseLen;
1440                                     wchar_t* tmp = new wchar_t[howMany + 1];
1441                                     wcsncpy( tmp, base, howMany + 1 );
1442                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1443                                     expandedArgs.push_back( tmp );
1444                                     found = FindNextFileA( hf, &data );
1445                                 }
1446 #else
1447                                 int howMany = wcslen(data.cFileName) + baseLen;
1448                                 wchar_t* tmp = new wchar_t[howMany + 1];
1449                                 wcsncpy( tmp, base, howMany + 1 );
1450                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1451                                 expandedArgs.push_back( tmp );
1452                                 found = FindNextFileW( hf, &data );
1453 #endif // REPLACEARGS_ANSI
1454                             } while ( found );
1456                             FindClose( hf );
1457                         }
1458                         else
1459                         {
1460                             expandedArgs.push_back( parsed[i1] );
1461                         }
1462 #ifdef REPLACEARGS_ANSI
1463                     }
1464 #endif // REPLACEARGS_ANSI
1466                     delete[] base;
1467                 }
1468                 else
1469                 {
1470                     expandedArgs.push_back( parsed[i1] );
1471                 }
1472             }
1474             {
1475                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1476                 int iz = 0;
1477                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1478                 {
1479                     block[iz++] = *it;
1480                 }
1481                 parsed = block;
1482                 numArgs = expandedArgs.size();
1483             }
1485             std::vector<gchar*> newArgs;
1486             for ( int i = 0; i < numArgs; i++ )
1487             {
1488                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1489                 if ( replacement )
1490                 {
1491 #ifdef REPLACEARGS_DEBUG
1492                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1494                     if ( safe2 )
1495                     {
1496                         {
1497                             char tmp[1024];
1498                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1499                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1500                         }
1501                         g_free( safe2 );
1502                     }
1503 #endif // REPLACEARGS_DEBUG
1505                     newArgs.push_back( replacement );
1506                 }
1507                 else
1508                 {
1509                     newArgs.push_back( blankParam );
1510                 }
1511             }
1513             // Now push our munged params to be the new argv and argc
1514             {
1515                 char** block = new char*[newArgs.size()];
1516                 int iz = 0;
1517                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1518                 {
1519                     block[iz++] = *it;
1520                 }
1521                 argv = block;
1522                 argc = newArgs.size();
1523                 worked = true;
1524             }
1525         }
1526 #ifdef REPLACEARGS_DEBUG
1527         else
1528         {
1529             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1530         }
1531 #endif // REPLACEARGS_DEBUG
1532     }
1533 #ifdef REPLACEARGS_DEBUG
1534     else
1535     {
1536         {
1537             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1538         }
1540         char* line2 = GetCommandLineA();
1541         if ( line2 )
1542         {
1543             gchar *safe = Inkscape::IO::sanitizeString(line2);
1544             {
1545                 {
1546                     char tmp[strlen(safe) + 32];
1547                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1548                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1549                 }
1550             }
1551         }
1552         else
1553         {
1554             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1555         }
1556     }
1557 #endif // REPLACEARGS_DEBUG
1559     return worked;
1561 #endif // WIN32
1563 static GSList *
1564 sp_process_args(poptContext ctx)
1566     GSList *fl = NULL;
1568     gint a;
1569     while ((a = poptGetNextOpt(ctx)) >= 0) {
1570         switch (a) {
1571             case SP_ARG_FILE: {
1572                 gchar const *fn = poptGetOptArg(ctx);
1573                 if (fn != NULL) {
1574                     fl = g_slist_append(fl, g_strdup(fn));
1575                 }
1576                 break;
1577             }
1578             case SP_ARG_VERSION: {
1579                 printf("Inkscape %s (%s)\n", INKSCAPE_VERSION, __DATE__);
1580                 exit(0);
1581                 break;
1582             }
1583             case SP_ARG_EXTENSIONDIR: {
1584                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1585                 exit(0);
1586                 break;
1587             }
1588             case SP_ARG_VERB_LIST: {
1589                 // This really shouldn't go here, we should init the app.
1590                 // But, since we're just exiting in this path, there is
1591                 // no harm, and this is really a better place to put
1592                 // everything else.
1593                 Inkscape::Extension::init();
1594                 Inkscape::Verb::list();
1595                 exit(0);
1596                 break;
1597             }
1598             case SP_ARG_VERB:
1599             case SP_ARG_SELECT: {
1600                 gchar const *arg = poptGetOptArg(ctx);
1601                 if (arg != NULL) {
1602                     // printf("Adding in: %s\n", arg);
1603                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1604                 }
1605                 break;
1606             }
1607             default: {
1608                 break;
1609             }
1610         }
1611     }
1613     gchar const ** const args = poptGetArgs(ctx);
1614     if (args != NULL) {
1615         for (unsigned i = 0; args[i] != NULL; i++) {
1616             fl = g_slist_append(fl, g_strdup(args[i]));
1617         }
1618     }
1620     return fl;
1624 /*
1625   Local Variables:
1626   mode:c++
1627   c-file-style:"stroustrup"
1628   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1629   indent-tabs-mode:nil
1630   fill-column:99
1631   End:
1632 */
1633 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :