Code

added missing ';'
[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     CHAR tmp[_MAX_EXT];
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     strcpy(tmp, "PATH=");
437     strcat(tmp, szFullPath);
438     strcat(tmp, ";");
439     strcat(tmp, szFullPath);
440     strcat(tmp, "python\\");
441     strcat(tmp, ";%PATH%");
442     _putenv(tmp);
444     strcpy(tmp, "PYTHONPATH=");
445     strcat(tmp, szFullPath);
446     strcat(tmp, "python\\");
447     _putenv(tmp);
449     return 0;
451 #endif
453 int
454 main(int argc, char **argv)
456 #ifdef HAVE_FPSETMASK
457     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
458        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
459        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
460     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
461 #endif
463 #ifdef ENABLE_NLS
464 #ifdef WIN32
465         _win32_set_inkscape_env(argv[0]);
466     RegistryTool rt;
467     rt.setPathInfo();
468     gchar *pathBuf = g_strconcat(g_path_get_dirname(argv[0]), "\\", PACKAGE_LOCALE_DIR, NULL);
469     bindtextdomain(GETTEXT_PACKAGE, pathBuf);
470     g_free(pathBuf);
471 #else
472 #ifdef ENABLE_BINRELOC
473     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
474 #else
475     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
476 #endif
477 #endif
478     // Allow the user to override the locale directory by setting
479     // the environment variable INKSCAPE_LOCALEDIR.
480     char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
481     if (inkscape_localedir != NULL) {
482         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
483     }
484 #endif
486     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
488 #ifdef ENABLE_NLS
489     textdomain(GETTEXT_PACKAGE);
490 #endif
492     LIBXML_TEST_VERSION
494     Inkscape::GC::init();
496     Inkscape::Debug::Logger::init();
498     gboolean use_gui;
499 #ifndef WIN32
500     use_gui = (getenv("DISPLAY") != NULL);
501 #else
502     /*
503       Set the current directory to the directory of the
504       executable.  This seems redundant, but is needed for
505       when inkscape.exe is executed from another directory.
506       We use relative paths on win32.
507       HKCR\svgfile\shell\open\command is a good example
508     */
509     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
510     char *homedir = g_path_get_dirname(argv[0]);
511     SetCurrentDirectory(homedir);
512     g_free(homedir);
514     use_gui = TRUE;
515 #endif
516     /* Test whether with/without GUI is forced */
517     for (int i = 1; i < argc; i++) {
518         if (!strcmp(argv[i], "-z")
519             || !strcmp(argv[i], "--without-gui")
520             || !strcmp(argv[i], "-p")
521             || !strncmp(argv[i], "--print", 7)
522             || !strcmp(argv[i], "-e")
523             || !strncmp(argv[i], "--export-png", 12)
524             || !strcmp(argv[i], "-l")
525             || !strncmp(argv[i], "--export-plain-svg", 12)
526             || !strcmp(argv[i], "-i")
527             || !strncmp(argv[i], "--export-area-drawing", 21)
528             || !strcmp(argv[i], "-D")
529             || !strncmp(argv[i], "--export-area-canvas", 20)
530             || !strcmp(argv[i], "-C")
531             || !strncmp(argv[i], "--export-id", 12)
532             || !strcmp(argv[i], "-P")
533             || !strncmp(argv[i], "--export-ps", 11)
534             || !strcmp(argv[i], "-E")
535             || !strncmp(argv[i], "--export-eps", 12)
536             || !strcmp(argv[i], "-A")
537             || !strncmp(argv[i], "--export-pdf", 12)
538 #ifdef WIN32
539             || !strcmp(argv[i], "-M")
540             || !strncmp(argv[i], "--export-emf", 12)
541 #endif //WIN32
542             || !strcmp(argv[i], "-W")
543             || !strncmp(argv[i], "--query-width", 13)
544             || !strcmp(argv[i], "-H")
545             || !strncmp(argv[i], "--query-height", 14)
546             || !strcmp(argv[i], "-S")
547             || !strncmp(argv[i], "--query-all", 11)
548             || !strcmp(argv[i], "-X")
549             || !strncmp(argv[i], "--query-x", 13)
550             || !strcmp(argv[i], "-Y")
551             || !strncmp(argv[i], "--query-y", 14)
552             || !strcmp(argv[i], "--vacuum-defs")
553            )
554         {
555             /* main_console handles any exports -- not the gui */
556             use_gui = FALSE;
557             break;
558         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
559             use_gui = TRUE;
560             break;
561         }
562     }
564 #ifdef WIN32
565 #ifndef REPLACEARGS_ANSI
566     if ( PrintWin32::is_os_wide() )
567 #endif // REPLACEARGS_ANSI
568     {
569         // If the call fails, we'll need to convert charsets
570         needToRecodeParams = !replaceArgs( argc, argv );
571     }
572 #endif // WIN32
574     /// \todo  Should this be a static object (see inkscape.cpp)?
575     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
577     return app.run();
580 void fixupSingleFilename( gchar **orig, gchar **spare )
582     if ( orig && *orig && **orig ) {
583         GError *error = NULL;
584         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
585         if ( newFileName )
586         {
587             *orig = newFileName;
588             if ( spare ) {
589                 *spare = newFileName;
590             }
591 //             g_message("Set a replacement fixup");
592         }
593     }
596 GSList *fixupFilenameEncoding( GSList* fl )
598     GSList *newFl = NULL;
599     while ( fl ) {
600         gchar *fn = static_cast<gchar*>(fl->data);
601         fl = g_slist_remove( fl, fl->data );
602         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
603         if ( newFileName ) {
605             if ( 0 )
606             {
607                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
608                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
609                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
610                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
611                 gtk_dialog_run (GTK_DIALOG (w));
612                 gtk_widget_destroy (w);
613                 g_free(safeNewFn);
614                 g_free(safeFn);
615             }
617             g_free( fn );
618             fn = newFileName;
619             newFileName = 0;
620         }
621         else
622             if ( 0 )
623         {
624             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
625             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
626             gtk_dialog_run (GTK_DIALOG (w));
627             gtk_widget_destroy (w);
628             g_free(safeFn);
629         }
630         newFl = g_slist_append( newFl, fn );
631     }
632     return newFl;
635 int sp_common_main( int argc, char const **argv, GSList **flDest )
637     /// \todo fixme: Move these to some centralized location (Lauris)
638     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
639     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
642     // temporarily switch gettext encoding to locale, so that help messages can be output properly
643     gchar const *charset;
644     g_get_charset(&charset);
646     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
648     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
649     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
650     g_return_val_if_fail(ctx != NULL, 1);
652     /* Collect own arguments */
653     GSList *fl = sp_process_args(ctx);
654     poptFreeContext(ctx);
656     // now switch gettext back to UTF-8 (for GUI)
657     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
659     // Now let's see if the file list still holds up
660     if ( needToRecodeParams )
661     {
662         fl = fixupFilenameEncoding( fl );
663     }
665     // Check the globals for filename-fixup
666     if ( needToRecodeParams )
667     {
668         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
669         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
670         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
671     }
672     else
673     {
674         if ( sp_export_png )
675             sp_export_png_utf8 = g_strdup( sp_export_png );
676         if ( sp_export_svg )
677             sp_export_svg_utf8 = g_strdup( sp_export_svg );
678         if ( sp_global_printer )
679             sp_global_printer_utf8 = g_strdup( sp_global_printer );
680     }
682     // Return the list if wanted, else free it up.
683     if ( flDest ) {
684         *flDest = fl;
685         fl = 0;
686     } else {
687         while ( fl ) {
688             g_free( fl->data );
689             fl = g_slist_remove( fl, fl->data );
690         }
691     }
692     return 0;
695 static void
696 snooper(GdkEvent *event, gpointer /*data*/) {
697     if(inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
698     {
699         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
700         switch (event->type) {
701             case GDK_MOTION_NOTIFY:
702                 if(event->motion.state & mapping) {
703                     event->motion.state|=GDK_MOD1_MASK;
704                 }
705                 break;
706             case GDK_BUTTON_PRESS:
707                 if(event->button.state & mapping) {
708                     event->button.state|=GDK_MOD1_MASK;
709                 }
710                 break;
711              case GDK_KEY_PRESS:
712                  if(event->key.state & mapping) {
713                      event->key.state|=GDK_MOD1_MASK;
714                  }
715                  break;
716         default:
717             break;
718         }
719     }
720     gtk_main_do_event (event);
723 int
724 sp_main_gui(int argc, char const **argv)
726     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
728     GSList *fl = NULL;
729     int retVal = sp_common_main( argc, argv, &fl );
730     g_return_val_if_fail(retVal == 0, 1);
732     inkscape_gtk_stock_init();
734     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
736     Inkscape::Debug::log_display_config();
738     /* Set default icon */
739     gchar *filename = (gchar *) g_build_filename (INKSCAPE_APPICONDIR, "inkscape.png", NULL);
740     if (Inkscape::IO::file_test(filename, (GFileTest)(G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))) {
741         gtk_window_set_default_icon_from_file(filename, NULL);
742     }
743     g_free (filename);
744     filename = 0;
746     gboolean create_new = TRUE;
748     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
749     inkscape_application_init(argv[0], true);
751     while (fl) {
752         if (sp_file_open((gchar *)fl->data,NULL)) {
753             create_new=FALSE;
754         }
755         fl = g_slist_remove(fl, fl->data);
756     }
757     if (create_new) {
758         sp_file_new_default();
759     }
761     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
762     main_instance.run();
764 #ifdef WIN32
765     //We might not need anything here
766     //sp_win32_finish(); <-- this is a NOP func
767 #endif
769     return 0;
772 int
773 sp_main_console(int argc, char const **argv)
775     /* We are started in text mode */
777     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
778      * in a non-Gtk environment.  Used in libnrtype's
779      * FontInstance.cpp and FontFactory.cpp.
780      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
781      */
782     g_type_init();
783     char **argv2 = const_cast<char **>(argv);
784     gtk_init_check( &argc, &argv2 );
785     //setlocale(LC_ALL, "");
787     GSList *fl = NULL;
788     int retVal = sp_common_main( argc, argv, &fl );
789     g_return_val_if_fail(retVal == 0, 1);
791     if (fl == NULL) {
792         g_print("Nothing to do!\n");
793         exit(0);
794     }
796     inkscape_application_init(argv[0], false);
798     while (fl) {
799         SPDocument *doc;
801         doc = Inkscape::Extension::open(NULL, (gchar *)fl->data);
802         if (doc == NULL) {
803             doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), (gchar *)fl->data);
804         }
805         if (doc == NULL) {
806             g_warning("Specified document %s cannot be opened (is it valid SVG file?)", (gchar *) fl->data);
807         } else {
808             if (sp_vacuum_defs) {
809                 vacuum_document(doc);
810             }
811             if (sp_vacuum_defs && !sp_export_svg) {
812                 // save under the name given in the command line
813                 sp_repr_save_file(doc->rdoc, (gchar *)fl->data, SP_SVG_NS_URI);
814             }
815             if (sp_global_printer) {
816                 sp_print_document_to_file(doc, sp_global_printer);
817             }
818             if (sp_export_png) {
819                 sp_do_export_png(doc);
820             }
821             if (sp_export_svg) {
822                 Inkscape::XML::Document *rdoc;
823                 Inkscape::XML::Node *repr;
824                 rdoc = sp_repr_document_new("svg:svg");
825                 repr = rdoc->root();
826                 repr = sp_document_root(doc)->updateRepr(repr, SP_OBJECT_WRITE_BUILD);
827                 sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
828             }
829             if (sp_export_ps) {
830                 do_export_ps(doc, sp_export_ps, "image/x-postscript");
831             }
832             if (sp_export_eps) {
833                 do_export_ps(doc, sp_export_eps, "image/x-e-postscript");
834             }
835             if (sp_export_pdf) {
836                 do_export_pdf(doc, sp_export_pdf, "application/pdf");
837             }
838 #ifdef WIN32
839             if (sp_export_emf) {
840                 do_export_emf(doc, sp_export_emf, "image/x-emf");
841             }
842 #endif //WIN32
843             if (sp_query_all) {
844                 do_query_all (doc);
845             } else if (sp_query_width || sp_query_height) {
846                 do_query_dimension (doc, true, sp_query_width? NR::X : NR::Y, sp_query_id);
847             } else if (sp_query_x || sp_query_y) {
848                 do_query_dimension (doc, false, sp_query_x? NR::X : NR::Y, sp_query_id);
849             }
850         }
852         fl = g_slist_remove(fl, fl->data);
853     }
855     inkscape_unref();
857     return 0;
860 static void
861 do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id)
863     SPObject *o = NULL;
865     if (id) {
866         o = doc->getObjectById(id);
867         if (o) {
868             if (!SP_IS_ITEM (o)) {
869                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
870                 return;
871             }
872         } else {
873             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
874             return;
875         }
876     } else {
877         o = SP_DOCUMENT_ROOT(doc);
878     }
880     if (o) {
881         sp_document_ensure_up_to_date (doc);
882         SPItem *item = ((SPItem *) o);
884         // "true" SVG bbox for scripting
885         NR::Maybe<NR::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
886         if (area) {
887             Inkscape::SVGOStringStream os;
888             if (extent) {
889                 os << area->extent(axis);
890             } else {
891                 os << area->min()[axis];
892             }
893             g_print ("%s", os.str().c_str());
894         } else {
895             g_print("0");
896         }
897     }
900 static void
901 do_query_all (SPDocument *doc)
903     SPObject *o = NULL;
905     o = SP_DOCUMENT_ROOT(doc);
907     if (o) {
908         sp_document_ensure_up_to_date (doc);
909         do_query_all_recurse(o);
910     }
913 static void
914 do_query_all_recurse (SPObject *o)
916     SPItem *item = ((SPItem *) o);
917     if (o->id && SP_IS_ITEM(item)) {
918         NR::Maybe<NR::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
919         if (area) {
920             Inkscape::SVGOStringStream os;
921             os << o->id;
922             os << "," << area->min()[NR::X];
923             os << "," << area->min()[NR::Y];
924             os << "," << area->extent(NR::X);
925             os << "," << area->extent(NR::Y);
926             g_print ("%s\n", os.str().c_str());
927         }
928     }
930     SPObject *child = o->children;
931     while (child) {
932         do_query_all_recurse (child);
933         child = child->next;
934     }
938 static void
939 sp_do_export_png(SPDocument *doc)
941     const gchar *filename = NULL;
942     gdouble dpi = 0.0;
944     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
945         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
946     }
948     GSList *items = NULL;
950     NRRect area;
951     if (sp_export_id || sp_export_area_drawing) {
953         SPObject *o = NULL;
954         SPObject *o_area = NULL;
955         if (sp_export_id && sp_export_area_drawing) {
956             o = doc->getObjectById(sp_export_id);
957             o_area = SP_DOCUMENT_ROOT (doc);
958         } else if (sp_export_id) {
959             o = doc->getObjectById(sp_export_id);
960             o_area = o;
961         } else if (sp_export_area_drawing) {
962             o = SP_DOCUMENT_ROOT (doc);
963             o_area = o;
964         }
966         if (o) {
967             if (!SP_IS_ITEM (o)) {
968                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
969                 return;
970             }
972             items = g_slist_prepend (items, SP_ITEM(o));
974             if (sp_export_id_only) {
975                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
976             }
978             if (sp_export_use_hints) {
980                 // retrieve export filename hint
981                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
982                 if (fn_hint) {
983                     if (sp_export_png) {
984                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
985                         filename = sp_export_png;
986                     } else {
987                         filename = fn_hint;
988                     }
989                 } else {
990                     g_warning ("Export filename hint not found for the object.");
991                     filename = sp_export_png;
992                 }
994                 // retrieve export dpi hints
995                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
996                 if (dpi_hint) {
997                     if (sp_export_dpi || sp_export_width || sp_export_height) {
998                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
999                     } else {
1000                         dpi = atof(dpi_hint);
1001                     }
1002                 } else {
1003                     g_warning ("Export DPI hint not found for the object.");
1004                 }
1006             }
1008             // write object bbox to area
1009             sp_document_ensure_up_to_date (doc);
1010             sp_item_invoke_bbox((SPItem *) o_area, &area, sp_item_i2r_affine((SPItem *) o_area), TRUE);
1011         } else {
1012             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1013             return;
1014         }
1015     }
1017     if (sp_export_area) {
1018         /* Try to parse area (given in SVG pixels) */
1019         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &area.x0, &area.y0, &area.x1, &area.y1) == 4) {
1020             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1021             return;
1022         }
1023         if ((area.x0 >= area.x1) || (area.y0 >= area.y1)) {
1024             g_warning("Export area '%s' has negative width or height. Nothing exported.", sp_export_area);
1025             return;
1026         }
1027     } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1028         /* Export the whole canvas */
1029         sp_document_ensure_up_to_date (doc);
1030         area.x0 = SP_ROOT(doc->root)->x.computed;
1031         area.y0 = SP_ROOT(doc->root)->y.computed;
1032         area.x1 = area.x0 + sp_document_width (doc);
1033         area.y1 = area.y0 + sp_document_height (doc);
1034     }
1036     // set filename and dpi from options, if not yet set from the hints
1037     if (!filename) {
1038         if (!sp_export_png) {
1039             g_warning ("No export filename given and no filename hint. Nothing exported.");
1040             return;
1041         }
1042         filename = sp_export_png;
1043     }
1045     if (sp_export_dpi && dpi == 0.0) {
1046         dpi = atof(sp_export_dpi);
1047         if ((dpi < 0.1) || (dpi > 10000.0)) {
1048             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1049             return;
1050         }
1051         g_print("DPI: %g\n", dpi);
1052     }
1054     if (sp_export_area_snap) {
1055         area.x0 = std::floor (area.x0);
1056         area.y0 = std::floor (area.y0);
1057         area.x1 = std::ceil (area.x1);
1058         area.y1 = std::ceil (area.y1);
1059     }
1061     // default dpi
1062     if (dpi == 0.0)
1063         dpi = PX_PER_IN;
1065     unsigned long int width = 0;
1066     unsigned long int height = 0;
1068     if (sp_export_width) {
1069         width = strtoul(sp_export_width, NULL, 0);
1070         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1071             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1072             return;
1073         }
1074         dpi = (gdouble) width * PX_PER_IN / (area.x1 - area.x0);
1075     }
1077     if (sp_export_height) {
1078         height = strtoul(sp_export_height, NULL, 0);
1079         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1080             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1081             return;
1082         }
1083         dpi = (gdouble) height * PX_PER_IN / (area.y1 - area.y0);
1084     }
1086     if (!sp_export_width) {
1087         width = (unsigned long int) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1088     }
1090     if (!sp_export_height) {
1091         height = (unsigned long int) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1092     }
1094     guint32 bgcolor = 0x00000000;
1095     if (sp_export_background) {
1096         // override the page color
1097         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1098         bgcolor |= 0xff; // default is no opacity
1099     } else {
1100         // read from namedview
1101         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1102         if (nv && nv->attribute("pagecolor"))
1103             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1104         if (nv && nv->attribute("inkscape:pageopacity"))
1105             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1106     }
1108     if (sp_export_background_opacity) {
1109         // override opacity
1110         gfloat value;
1111         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1112             if (value > 1.0) {
1113                 value = CLAMP (value, 1.0f, 255.0f);
1114                 bgcolor &= (guint32) 0xffffff00;
1115                 bgcolor |= (guint32) floor(value);
1116             } else {
1117                 value = CLAMP (value, 0.0f, 1.0f);
1118                 bgcolor &= (guint32) 0xffffff00;
1119                 bgcolor |= SP_COLOR_F_TO_U(value);
1120             }
1121         }
1122     }
1124     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1126     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);
1128     g_print("Bitmap saved as: %s\n", filename);
1130     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1131         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);
1132     } else {
1133         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1134     }
1136     g_slist_free (items);
1140 /**
1141  *  Perform an export of either PS or EPS.
1142  *
1143  *  \param doc Document to export.
1144  *  \param uri URI to export to.
1145  *  \param mime MIME type to export as.
1146  */
1148 static void do_export_ps(SPDocument* doc, gchar const* uri, char const* mime)
1150     Inkscape::Extension::DB::OutputList o;
1151     Inkscape::Extension::db.get_output_list(o);
1152     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1153     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1154         i++;
1155     }
1157     if (i == o.end())
1158     {
1159         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1160         return;
1161     }
1163     bool old_text_to_path = false;
1164     bool old_font_embedded = false;
1165     bool old_bbox_page = false;
1167     try {
1168         old_text_to_path = (*i)->get_param_bool("textToPath");
1169         (*i)->set_param_bool("textToPath", sp_export_text_to_path);
1170     }
1171     catch (...) {
1172         g_warning ("Could not set export-text-to-path option for this export.");
1173     }
1175     try {
1176         old_font_embedded = (*i)->get_param_bool("fontEmbedded");
1177         (*i)->set_param_bool("fontEmbedded", sp_export_font);
1178     }
1179     catch (...) {
1180         g_warning ("Could not set export-font option for this export.");
1181     }
1183     try {
1184         old_bbox_page = (*i)->get_param_bool("pageBoundingBox");
1185         (*i)->set_param_bool("pageBoundingBox", sp_export_bbox_page);
1186     }
1187     catch (...) {
1188         g_warning ("Could not set export-bbox-page option for this export.");
1189     }
1191     (*i)->save(doc, uri);
1193     try {
1194         (*i)->set_param_bool("textToPath", old_text_to_path);
1195         (*i)->set_param_bool("fontEmbedded", old_font_embedded);
1196         (*i)->set_param_bool("pageBoundingBox", old_bbox_page);
1197     }
1198     catch (...) {
1200     }
1203 /**
1204  *  Perform a PDF export
1205  *
1206  *  \param doc Document to export.
1207  *  \param uri URI to export to.
1208  *  \param mime MIME type to export as.
1209  */
1211 static void do_export_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1213     Inkscape::Extension::DB::OutputList o;
1214     Inkscape::Extension::db.get_output_list(o);
1215     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1216     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1217         i++;
1218     }
1220     if (i == o.end())
1221     {
1222         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1223         return;
1224     }
1226     if (sp_export_id) {
1227         SPObject *o = doc->getObjectById(sp_export_id);
1228         if (o == NULL) {
1229             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1230             return;
1231         }
1232         (*i)->set_param_string ("exportId", sp_export_id);
1233     } else {
1234         (*i)->set_param_string ("exportId", "");
1235     }
1237     if (sp_export_area_drawing) {
1238         (*i)->set_param_bool ("exportDrawing", TRUE);
1239     } else {
1240         (*i)->set_param_bool ("exportDrawing", FALSE);
1241     }
1243     (*i)->save(doc, uri);
1246 #ifdef WIN32
1247 /**
1248  *  Export a document to EMF
1249  *
1250  *  \param doc Document to export.
1251  *  \param uri URI to export to.
1252  *  \param mime MIME type to export as (should be "image/x-emf")
1253  */
1255 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1257     Inkscape::Extension::DB::OutputList o;
1258     Inkscape::Extension::db.get_output_list(o);
1259     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1260     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1261         i++;
1262     }
1264     if (i == o.end())
1265     {
1266         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1267         return;
1268     }
1270     (*i)->save(doc, uri);
1272 #endif //WIN32
1274 #ifdef WIN32
1275 bool replaceArgs( int& argc, char**& argv )
1277     bool worked = false;
1279 #ifdef REPLACEARGS_DEBUG
1280     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1281 #endif // REPLACEARGS_DEBUG
1283     wchar_t* line = GetCommandLineW();
1284     if ( line )
1285     {
1286 #ifdef REPLACEARGS_DEBUG
1287         {
1288             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1289             if ( utf8Line )
1290             {
1291                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1292                 {
1293                     char tmp[strlen(safe) + 32];
1294                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1295                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1296                 }
1297             }
1298         }
1299 #endif // REPLACEARGS_DEBUG
1301         int numArgs = 0;
1302         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1304 #ifdef REPLACEARGS_ANSI
1305 // test code for trying things on Win95/98/ME
1306         if ( !parsed )
1307         {
1308 #ifdef REPLACEARGS_DEBUG
1309             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1310 #endif // REPLACEARGS_DEBUG
1311             int lineLen = wcslen(line) + 1;
1312             wchar_t* lineDup = new wchar_t[lineLen];
1313             wcsncpy( lineDup, line, lineLen );
1315             int pos = 0;
1316             bool inQuotes = false;
1317             bool inWhitespace = true;
1318             std::vector<int> places;
1319             while ( lineDup[pos] )
1320             {
1321                 if ( inQuotes )
1322                 {
1323                     if ( lineDup[pos] == L'"' )
1324                     {
1325                         inQuotes = false;
1326                     }
1327                 }
1328                 else if ( lineDup[pos] == L'"' )
1329                 {
1330                     inQuotes = true;
1331                     inWhitespace = false;
1332                     places.push_back(pos);
1333                 }
1334                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1335                 {
1336                     if ( !inWhitespace )
1337                     {
1338                         inWhitespace = true;
1339                         lineDup[pos] = 0;
1340                     }
1341                 }
1342                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1343                 {
1344                     inWhitespace = false;
1345                     places.push_back(pos);
1346                 }
1347                 else
1348                 {
1349                     // consume
1350                 }
1351                 pos++;
1352             }
1353 #ifdef REPLACEARGS_DEBUG
1354             {
1355                 char tmp[256];
1356                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1357                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1358             }
1359 #endif // REPLACEARGS_DEBUG
1361             wchar_t** block = new wchar_t*[places.size()];
1362             int i = 0;
1363             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1364             {
1365                 block[i++] = &lineDup[*it];
1366             }
1367             parsed = block;
1368             numArgs = places.size();
1369         }
1370 #endif // REPLACEARGS_ANSI
1372         if ( parsed )
1373         {
1374             std::vector<wchar_t*>expandedArgs;
1375             if ( numArgs > 0 )
1376             {
1377                 expandedArgs.push_back( parsed[0] );
1378             }
1380             for ( int i1 = 1; i1 < numArgs; i1++ )
1381             {
1382                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1383                 wildcarded &= parsed[i1][0] != L'"';
1384                 wildcarded &= parsed[i1][0] != L'-';
1385                 if ( wildcarded )
1386                 {
1387 #ifdef REPLACEARGS_ANSI
1388                     WIN32_FIND_DATAA data = {0};
1389 #else
1390                     WIN32_FIND_DATAW data = {0};
1391 #endif // REPLACEARGS_ANSI
1393                     int baseLen = wcslen(parsed[i1]) + 2;
1394                     wchar_t* base = new wchar_t[baseLen];
1395                     wcsncpy( base, parsed[i1], baseLen );
1396                     wchar_t* last = wcsrchr( base, L'\\' );
1397                     if ( last )
1398                     {
1399                         last[1] = 0;
1400                     }
1401                     else
1402                     {
1403                         base[0] = 0;
1404                     }
1405                     baseLen = wcslen( base );
1407 #ifdef REPLACEARGS_ANSI
1408                     char target[MAX_PATH];
1409                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1410                     {
1411                         HANDLE hf = FindFirstFileA( target, &data );
1412 #else
1413                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1414 #endif // REPLACEARGS_ANSI
1415                         if ( hf != INVALID_HANDLE_VALUE )
1416                         {
1417                             BOOL found = TRUE;
1418                             do
1419                             {
1420 #ifdef REPLACEARGS_ANSI
1421                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1422                                 if ( howMany > 0 )
1423                                 {
1424                                     howMany += baseLen;
1425                                     wchar_t* tmp = new wchar_t[howMany + 1];
1426                                     wcsncpy( tmp, base, howMany + 1 );
1427                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1428                                     expandedArgs.push_back( tmp );
1429                                     found = FindNextFileA( hf, &data );
1430                                 }
1431 #else
1432                                 int howMany = wcslen(data.cFileName) + baseLen;
1433                                 wchar_t* tmp = new wchar_t[howMany + 1];
1434                                 wcsncpy( tmp, base, howMany + 1 );
1435                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1436                                 expandedArgs.push_back( tmp );
1437                                 found = FindNextFileW( hf, &data );
1438 #endif // REPLACEARGS_ANSI
1439                             } while ( found );
1441                             FindClose( hf );
1442                         }
1443                         else
1444                         {
1445                             expandedArgs.push_back( parsed[i1] );
1446                         }
1447 #ifdef REPLACEARGS_ANSI
1448                     }
1449 #endif // REPLACEARGS_ANSI
1451                     delete[] base;
1452                 }
1453                 else
1454                 {
1455                     expandedArgs.push_back( parsed[i1] );
1456                 }
1457             }
1459             {
1460                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1461                 int iz = 0;
1462                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1463                 {
1464                     block[iz++] = *it;
1465                 }
1466                 parsed = block;
1467                 numArgs = expandedArgs.size();
1468             }
1470             std::vector<gchar*> newArgs;
1471             for ( int i = 0; i < numArgs; i++ )
1472             {
1473                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1474                 if ( replacement )
1475                 {
1476 #ifdef REPLACEARGS_DEBUG
1477                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1479                     if ( safe2 )
1480                     {
1481                         {
1482                             char tmp[1024];
1483                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1484                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1485                         }
1486                         g_free( safe2 );
1487                     }
1488 #endif // REPLACEARGS_DEBUG
1490                     newArgs.push_back( replacement );
1491                 }
1492                 else
1493                 {
1494                     newArgs.push_back( blankParam );
1495                 }
1496             }
1498             // Now push our munged params to be the new argv and argc
1499             {
1500                 char** block = new char*[newArgs.size()];
1501                 int iz = 0;
1502                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1503                 {
1504                     block[iz++] = *it;
1505                 }
1506                 argv = block;
1507                 argc = newArgs.size();
1508                 worked = true;
1509             }
1510         }
1511 #ifdef REPLACEARGS_DEBUG
1512         else
1513         {
1514             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1515         }
1516 #endif // REPLACEARGS_DEBUG
1517     }
1518 #ifdef REPLACEARGS_DEBUG
1519     else
1520     {
1521         {
1522             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1523         }
1525         char* line2 = GetCommandLineA();
1526         if ( line2 )
1527         {
1528             gchar *safe = Inkscape::IO::sanitizeString(line2);
1529             {
1530                 {
1531                     char tmp[strlen(safe) + 32];
1532                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1533                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1534                 }
1535             }
1536         }
1537         else
1538         {
1539             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1540         }
1541     }
1542 #endif // REPLACEARGS_DEBUG
1544     return worked;
1546 #endif // WIN32
1548 static GSList *
1549 sp_process_args(poptContext ctx)
1551     GSList *fl = NULL;
1553     gint a;
1554     while ((a = poptGetNextOpt(ctx)) >= 0) {
1555         switch (a) {
1556             case SP_ARG_FILE: {
1557                 gchar const *fn = poptGetOptArg(ctx);
1558                 if (fn != NULL) {
1559                     fl = g_slist_append(fl, g_strdup(fn));
1560                 }
1561                 break;
1562             }
1563             case SP_ARG_VERSION: {
1564                 printf("Inkscape %s (%s)\n", INKSCAPE_VERSION, __DATE__);
1565                 exit(0);
1566                 break;
1567             }
1568             case SP_ARG_EXTENSIONDIR: {
1569                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1570                 exit(0);
1571                 break;
1572             }
1573             case SP_ARG_VERB_LIST: {
1574                 // This really shouldn't go here, we should init the app.
1575                 // But, since we're just exiting in this path, there is
1576                 // no harm, and this is really a better place to put
1577                 // everything else.
1578                 Inkscape::Extension::init();
1579                 Inkscape::Verb::list();
1580                 exit(0);
1581                 break;
1582             }
1583             case SP_ARG_VERB:
1584             case SP_ARG_SELECT: {
1585                 gchar const *arg = poptGetOptArg(ctx);
1586                 if (arg != NULL) {
1587                     // printf("Adding in: %s\n", arg);
1588                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1589                 }
1590                 break;
1591             }
1592             default: {
1593                 break;
1594             }
1595         }
1596     }
1598     gchar const ** const args = poptGetArgs(ctx);
1599     if (args != NULL) {
1600         for (unsigned i = 0; args[i] != NULL; i++) {
1601             fl = g_slist_append(fl, g_strdup(args[i]));
1602         }
1603     }
1605     return fl;
1609 /*
1610   Local Variables:
1611   mode:c++
1612   c-file-style:"stroustrup"
1613   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1614   indent-tabs-mode:nil
1615   fill-column:99
1616   End:
1617 */
1618 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :