Code

noop: address ‘no newline at end of file’ warning
[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     if (sp_export_area_canvas) {
1259         (*i)->set_param_bool ("exportCanvas", TRUE);
1260     } else {
1261         (*i)->set_param_bool ("exportCanvas", FALSE);
1262     }
1264     (*i)->save(doc, uri);
1267 #ifdef WIN32
1268 /**
1269  *  Export a document to EMF
1270  *
1271  *  \param doc Document to export.
1272  *  \param uri URI to export to.
1273  *  \param mime MIME type to export as (should be "image/x-emf")
1274  */
1276 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1278     Inkscape::Extension::DB::OutputList o;
1279     Inkscape::Extension::db.get_output_list(o);
1280     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1281     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1282         i++;
1283     }
1285     if (i == o.end())
1286     {
1287         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1288         return;
1289     }
1291     (*i)->save(doc, uri);
1293 #endif //WIN32
1295 #ifdef WIN32
1296 bool replaceArgs( int& argc, char**& argv )
1298     bool worked = false;
1300 #ifdef REPLACEARGS_DEBUG
1301     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1302 #endif // REPLACEARGS_DEBUG
1304     wchar_t* line = GetCommandLineW();
1305     if ( line )
1306     {
1307 #ifdef REPLACEARGS_DEBUG
1308         {
1309             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1310             if ( utf8Line )
1311             {
1312                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1313                 {
1314                     char tmp[strlen(safe) + 32];
1315                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1316                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1317                 }
1318             }
1319         }
1320 #endif // REPLACEARGS_DEBUG
1322         int numArgs = 0;
1323         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1325 #ifdef REPLACEARGS_ANSI
1326 // test code for trying things on Win95/98/ME
1327         if ( !parsed )
1328         {
1329 #ifdef REPLACEARGS_DEBUG
1330             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1331 #endif // REPLACEARGS_DEBUG
1332             int lineLen = wcslen(line) + 1;
1333             wchar_t* lineDup = new wchar_t[lineLen];
1334             wcsncpy( lineDup, line, lineLen );
1336             int pos = 0;
1337             bool inQuotes = false;
1338             bool inWhitespace = true;
1339             std::vector<int> places;
1340             while ( lineDup[pos] )
1341             {
1342                 if ( inQuotes )
1343                 {
1344                     if ( lineDup[pos] == L'"' )
1345                     {
1346                         inQuotes = false;
1347                     }
1348                 }
1349                 else if ( lineDup[pos] == L'"' )
1350                 {
1351                     inQuotes = true;
1352                     inWhitespace = false;
1353                     places.push_back(pos);
1354                 }
1355                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1356                 {
1357                     if ( !inWhitespace )
1358                     {
1359                         inWhitespace = true;
1360                         lineDup[pos] = 0;
1361                     }
1362                 }
1363                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1364                 {
1365                     inWhitespace = false;
1366                     places.push_back(pos);
1367                 }
1368                 else
1369                 {
1370                     // consume
1371                 }
1372                 pos++;
1373             }
1374 #ifdef REPLACEARGS_DEBUG
1375             {
1376                 char tmp[256];
1377                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1378                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1379             }
1380 #endif // REPLACEARGS_DEBUG
1382             wchar_t** block = new wchar_t*[places.size()];
1383             int i = 0;
1384             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1385             {
1386                 block[i++] = &lineDup[*it];
1387             }
1388             parsed = block;
1389             numArgs = places.size();
1390         }
1391 #endif // REPLACEARGS_ANSI
1393         if ( parsed )
1394         {
1395             std::vector<wchar_t*>expandedArgs;
1396             if ( numArgs > 0 )
1397             {
1398                 expandedArgs.push_back( parsed[0] );
1399             }
1401             for ( int i1 = 1; i1 < numArgs; i1++ )
1402             {
1403                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1404                 wildcarded &= parsed[i1][0] != L'"';
1405                 wildcarded &= parsed[i1][0] != L'-';
1406                 if ( wildcarded )
1407                 {
1408 #ifdef REPLACEARGS_ANSI
1409                     WIN32_FIND_DATAA data = {0};
1410 #else
1411                     WIN32_FIND_DATAW data = {0};
1412 #endif // REPLACEARGS_ANSI
1414                     int baseLen = wcslen(parsed[i1]) + 2;
1415                     wchar_t* base = new wchar_t[baseLen];
1416                     wcsncpy( base, parsed[i1], baseLen );
1417                     wchar_t* last = wcsrchr( base, L'\\' );
1418                     if ( last )
1419                     {
1420                         last[1] = 0;
1421                     }
1422                     else
1423                     {
1424                         base[0] = 0;
1425                     }
1426                     baseLen = wcslen( base );
1428 #ifdef REPLACEARGS_ANSI
1429                     char target[MAX_PATH];
1430                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1431                     {
1432                         HANDLE hf = FindFirstFileA( target, &data );
1433 #else
1434                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1435 #endif // REPLACEARGS_ANSI
1436                         if ( hf != INVALID_HANDLE_VALUE )
1437                         {
1438                             BOOL found = TRUE;
1439                             do
1440                             {
1441 #ifdef REPLACEARGS_ANSI
1442                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1443                                 if ( howMany > 0 )
1444                                 {
1445                                     howMany += baseLen;
1446                                     wchar_t* tmp = new wchar_t[howMany + 1];
1447                                     wcsncpy( tmp, base, howMany + 1 );
1448                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1449                                     expandedArgs.push_back( tmp );
1450                                     found = FindNextFileA( hf, &data );
1451                                 }
1452 #else
1453                                 int howMany = wcslen(data.cFileName) + baseLen;
1454                                 wchar_t* tmp = new wchar_t[howMany + 1];
1455                                 wcsncpy( tmp, base, howMany + 1 );
1456                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1457                                 expandedArgs.push_back( tmp );
1458                                 found = FindNextFileW( hf, &data );
1459 #endif // REPLACEARGS_ANSI
1460                             } while ( found );
1462                             FindClose( hf );
1463                         }
1464                         else
1465                         {
1466                             expandedArgs.push_back( parsed[i1] );
1467                         }
1468 #ifdef REPLACEARGS_ANSI
1469                     }
1470 #endif // REPLACEARGS_ANSI
1472                     delete[] base;
1473                 }
1474                 else
1475                 {
1476                     expandedArgs.push_back( parsed[i1] );
1477                 }
1478             }
1480             {
1481                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1482                 int iz = 0;
1483                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1484                 {
1485                     block[iz++] = *it;
1486                 }
1487                 parsed = block;
1488                 numArgs = expandedArgs.size();
1489             }
1491             std::vector<gchar*> newArgs;
1492             for ( int i = 0; i < numArgs; i++ )
1493             {
1494                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1495                 if ( replacement )
1496                 {
1497 #ifdef REPLACEARGS_DEBUG
1498                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1500                     if ( safe2 )
1501                     {
1502                         {
1503                             char tmp[1024];
1504                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1505                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1506                         }
1507                         g_free( safe2 );
1508                     }
1509 #endif // REPLACEARGS_DEBUG
1511                     newArgs.push_back( replacement );
1512                 }
1513                 else
1514                 {
1515                     newArgs.push_back( blankParam );
1516                 }
1517             }
1519             // Now push our munged params to be the new argv and argc
1520             {
1521                 char** block = new char*[newArgs.size()];
1522                 int iz = 0;
1523                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1524                 {
1525                     block[iz++] = *it;
1526                 }
1527                 argv = block;
1528                 argc = newArgs.size();
1529                 worked = true;
1530             }
1531         }
1532 #ifdef REPLACEARGS_DEBUG
1533         else
1534         {
1535             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1536         }
1537 #endif // REPLACEARGS_DEBUG
1538     }
1539 #ifdef REPLACEARGS_DEBUG
1540     else
1541     {
1542         {
1543             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1544         }
1546         char* line2 = GetCommandLineA();
1547         if ( line2 )
1548         {
1549             gchar *safe = Inkscape::IO::sanitizeString(line2);
1550             {
1551                 {
1552                     char tmp[strlen(safe) + 32];
1553                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1554                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1555                 }
1556             }
1557         }
1558         else
1559         {
1560             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1561         }
1562     }
1563 #endif // REPLACEARGS_DEBUG
1565     return worked;
1567 #endif // WIN32
1569 static GSList *
1570 sp_process_args(poptContext ctx)
1572     GSList *fl = NULL;
1574     gint a;
1575     while ((a = poptGetNextOpt(ctx)) >= 0) {
1576         switch (a) {
1577             case SP_ARG_FILE: {
1578                 gchar const *fn = poptGetOptArg(ctx);
1579                 if (fn != NULL) {
1580                     fl = g_slist_append(fl, g_strdup(fn));
1581                 }
1582                 break;
1583             }
1584             case SP_ARG_VERSION: {
1585                 printf("Inkscape %s (%s)\n", INKSCAPE_VERSION, __DATE__);
1586                 exit(0);
1587                 break;
1588             }
1589             case SP_ARG_EXTENSIONDIR: {
1590                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1591                 exit(0);
1592                 break;
1593             }
1594             case SP_ARG_VERB_LIST: {
1595                 // This really shouldn't go here, we should init the app.
1596                 // But, since we're just exiting in this path, there is
1597                 // no harm, and this is really a better place to put
1598                 // everything else.
1599                 Inkscape::Extension::init();
1600                 Inkscape::Verb::list();
1601                 exit(0);
1602                 break;
1603             }
1604             case SP_ARG_VERB:
1605             case SP_ARG_SELECT: {
1606                 gchar const *arg = poptGetOptArg(ctx);
1607                 if (arg != NULL) {
1608                     // printf("Adding in: %s\n", arg);
1609                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1610                 }
1611                 break;
1612             }
1613             default: {
1614                 break;
1615             }
1616         }
1617     }
1619     gchar const ** const args = poptGetArgs(ctx);
1620     if (args != NULL) {
1621         for (unsigned i = 0; args[i] != NULL; i++) {
1622             fl = g_slist_append(fl, g_strdup(args[i]));
1623         }
1624     }
1626     return fl;
1630 /*
1631   Local Variables:
1632   mode:c++
1633   c-file-style:"stroustrup"
1634   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1635   indent-tabs-mode:nil
1636   fill-column:99
1637   End:
1638 */
1639 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :