Code

Allow Inkscape to run from Unicode directories on Windows
[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 // This has to be included prior to anything that includes setjmp.h, it croaks otherwise
32 #include <png.h>
34 #include <gtk/gtkmessagedialog.h>
36 #ifdef HAVE_IEEEFP_H
37 #include <ieeefp.h>
38 #endif
39 #include <cstring>
40 #include <string>
41 #include <locale.h>
42 #include <stdlib.h>
44 #include <popt.h>
45 #ifndef POPT_TABLEEND
46 #define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
47 #endif /* Not def: POPT_TABLEEND */
49 #include <libxml/tree.h>
50 #include <glib.h>
51 #include <glib/gprintf.h>
52 #include <glib-object.h>
53 #include <gtk/gtk.h>
54 #include <gtk/gtkmain.h>
55 #include <gtk/gtksignal.h>
56 #include <gtk/gtkwindow.h>
57 #include <gtk/gtkbox.h>
59 #include "gc-core.h"
61 #ifdef AND
62 #undef AND
63 #endif
65 #include "macros.h"
66 #include "file.h"
67 #include "document.h"
68 #include "sp-object.h"
69 #include "interface.h"
70 #include "print.h"
71 #include "color.h"
72 #include "sp-item.h"
73 #include "sp-root.h"
74 #include "unit-constants.h"
76 #include "svg/svg.h"
77 #include "svg/svg-color.h"
78 #include "svg/stringstream.h"
80 #include "inkscape-private.h"
81 #include "inkscape-version.h"
83 #include "sp-namedview.h"
84 #include "sp-guide.h"
85 #include "sp-object-repr.h"
86 #include "xml/repr.h"
88 #include "io/sys.h"
90 #include "debug/logger.h"
91 #include "debug/log-display-config.h"
93 #include "helper/png-write.h"
94 #include "helper/geom.h"
96 #include <extension/extension.h>
97 #include <extension/system.h>
98 #include <extension/db.h>
99 #include <extension/output.h>
100 #include <extension/input.h>
102 #ifdef WIN32
103 #include "registrytool.h"
104 #include "extension/internal/win32.h"
105 using Inkscape::Extension::Internal::PrintWin32;
106 #endif // WIN32
108 #include "extension/init.h"
110 #include <glibmm/i18n.h>
111 #include <gtkmm/main.h>
113 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
114 #define bind_textdomain_codeset(p,c)
115 #endif
117 #include "application/application.h"
118 #include "main-cmdlineact.h"
119 #include "widgets/icon.h"
120 #include "ui/widget/panel.h"
122 #include <errno.h>
124 enum {
125     SP_ARG_NONE,
126     SP_ARG_NOGUI,
127     SP_ARG_GUI,
128     SP_ARG_FILE,
129     SP_ARG_PRINT,
130     SP_ARG_EXPORT_PNG,
131     SP_ARG_EXPORT_DPI,
132     SP_ARG_EXPORT_AREA,
133     SP_ARG_EXPORT_AREA_DRAWING,
134     SP_ARG_EXPORT_AREA_PAGE,
135     SP_ARG_EXPORT_AREA_SNAP,
136     SP_ARG_EXPORT_WIDTH,
137     SP_ARG_EXPORT_HEIGHT,
138     SP_ARG_EXPORT_ID,
139     SP_ARG_EXPORT_ID_ONLY,
140     SP_ARG_EXPORT_USE_HINTS,
141     SP_ARG_EXPORT_BACKGROUND,
142     SP_ARG_EXPORT_BACKGROUND_OPACITY,
143     SP_ARG_EXPORT_SVG,
144     SP_ARG_EXPORT_PS,
145     SP_ARG_EXPORT_EPS,
146     SP_ARG_EXPORT_PDF,
147     SP_ARG_EXPORT_LATEX,
148 #ifdef WIN32
149     SP_ARG_EXPORT_EMF,
150 #endif //WIN32
151     SP_ARG_EXPORT_TEXT_TO_PATH,
152     SP_ARG_EXPORT_IGNORE_FILTERS,
153     SP_ARG_EXTENSIONDIR,
154     SP_ARG_QUERY_X,
155     SP_ARG_QUERY_Y,
156     SP_ARG_QUERY_WIDTH,
157     SP_ARG_QUERY_HEIGHT,
158     SP_ARG_QUERY_ALL,
159     SP_ARG_QUERY_ID,
160     SP_ARG_SHELL,
161     SP_ARG_VERSION,
162     SP_ARG_VACUUM_DEFS,
163     SP_ARG_VERB_LIST,
164     SP_ARG_VERB,
165     SP_ARG_SELECT,
166     SP_ARG_LAST
167 };
169 int sp_main_gui(int argc, char const **argv);
170 int sp_main_console(int argc, char const **argv);
171 static void sp_do_export_png(SPDocument *doc);
172 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime);
173 #ifdef WIN32
174 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
175 #endif //WIN32
176 static void do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id);
177 static void do_query_all (SPDocument *doc);
178 static void do_query_all_recurse (SPObject *o);
180 static gchar *sp_global_printer = NULL;
181 static gchar *sp_export_png = NULL;
182 static gchar *sp_export_dpi = NULL;
183 static gchar *sp_export_area = NULL;
184 static gboolean sp_export_area_drawing = FALSE;
185 static gboolean sp_export_area_page = FALSE;
186 static gboolean sp_export_latex = FALSE;
187 static gchar *sp_export_width = NULL;
188 static gchar *sp_export_height = NULL;
189 static gchar *sp_export_id = NULL;
190 static gchar *sp_export_background = NULL;
191 static gchar *sp_export_background_opacity = NULL;
192 static gboolean sp_export_area_snap = FALSE;
193 static gboolean sp_export_use_hints = FALSE;
194 static gboolean sp_export_id_only = FALSE;
195 static gchar *sp_export_svg = NULL;
196 static gchar *sp_export_ps = NULL;
197 static gchar *sp_export_eps = NULL;
198 static gchar *sp_export_pdf = NULL;
199 #ifdef WIN32
200 static gchar *sp_export_emf = NULL;
201 #endif //WIN32
202 static gboolean sp_export_text_to_path = FALSE;
203 static gboolean sp_export_ignore_filters = FALSE;
204 static gboolean sp_export_font = FALSE;
205 static gboolean sp_query_x = FALSE;
206 static gboolean sp_query_y = FALSE;
207 static gboolean sp_query_width = FALSE;
208 static gboolean sp_query_height = FALSE;
209 static gboolean sp_query_all = FALSE;
210 static gchar *sp_query_id = NULL;
211 static int sp_new_gui = FALSE;
212 static gboolean sp_shell = FALSE;
213 static gboolean sp_vacuum_defs = FALSE;
215 static gchar *sp_export_png_utf8 = NULL;
216 static gchar *sp_export_svg_utf8 = NULL;
217 static gchar *sp_global_printer_utf8 = NULL;
220 /**
221  *  Reset variables to default values.
222  */
223 static void resetCommandlineGlobals() {
224         sp_global_printer = NULL;
225         sp_export_png = NULL;
226         sp_export_dpi = NULL;
227         sp_export_area = NULL;
228         sp_export_area_drawing = FALSE;
229         sp_export_area_page = FALSE;
230         sp_export_latex = FALSE;
231         sp_export_width = NULL;
232         sp_export_height = NULL;
233         sp_export_id = NULL;
234         sp_export_background = NULL;
235         sp_export_background_opacity = NULL;
236         sp_export_area_snap = FALSE;
237         sp_export_use_hints = FALSE;
238         sp_export_id_only = FALSE;
239         sp_export_svg = NULL;
240         sp_export_ps = NULL;
241         sp_export_eps = NULL;
242         sp_export_pdf = NULL;
243 #ifdef WIN32
244         sp_export_emf = NULL;
245 #endif //WIN32
246         sp_export_text_to_path = FALSE;
247         sp_export_ignore_filters = FALSE;
248         sp_export_font = FALSE;
249         sp_query_x = FALSE;
250         sp_query_y = FALSE;
251         sp_query_width = FALSE;
252         sp_query_height = FALSE;
253         sp_query_all = FALSE;
254         sp_query_id = NULL;
255         sp_vacuum_defs = FALSE;
257         sp_export_png_utf8 = NULL;
258         sp_export_svg_utf8 = NULL;
259         sp_global_printer_utf8 = NULL;
262 #ifdef WIN32
263 static bool replaceArgs( int& argc, char**& argv );
264 #endif
265 static GSList *sp_process_args(poptContext ctx);
266 struct poptOption options[] = {
267     {"version", 'V',
268      POPT_ARG_NONE, NULL, SP_ARG_VERSION,
269      N_("Print the Inkscape version number"),
270      NULL},
272     {"without-gui", 'z',
273      POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
274      N_("Do not use X server (only process files from console)"),
275      NULL},
277     {"with-gui", 'g',
278      POPT_ARG_NONE, NULL, SP_ARG_GUI,
279      N_("Try to use X server (even if $DISPLAY is not set)"),
280      NULL},
282     {"file", 'f',
283      POPT_ARG_STRING, NULL, SP_ARG_FILE,
284      N_("Open specified document(s) (option string may be excluded)"),
285      N_("FILENAME")},
287     {"print", 'p',
288      POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
289      N_("Print document(s) to specified output file (use '| program' for pipe)"),
290      N_("FILENAME")},
292     {"export-png", 'e',
293      POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
294      N_("Export document to a PNG file"),
295      N_("FILENAME")},
297     {"export-dpi", 'd',
298      POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
299      N_("Resolution for exporting to bitmap and for rasterization of filters in PS/EPS/PDF (default 90)"),
300      N_("DPI")},
302     {"export-area", 'a',
303      POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
304      N_("Exported area in SVG user units (default is the page; 0,0 is lower-left corner)"),
305      N_("x0:y0:x1:y1")},
307     {"export-area-drawing", 'D',
308      POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
309      N_("Exported area is the entire drawing (not page)"),
310      NULL},
312     {"export-area-page", 'C',
313      POPT_ARG_NONE, &sp_export_area_page, SP_ARG_EXPORT_AREA_PAGE,
314      N_("Exported area is the entire page"),
315      NULL},
317     {"export-area-snap", 0,
318      POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
319      N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
320      NULL},
322     {"export-width", 'w',
323      POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
324      N_("The width of exported bitmap in pixels (overrides export-dpi)"),
325      N_("WIDTH")},
327     {"export-height", 'h',
328      POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
329      N_("The height of exported bitmap in pixels (overrides export-dpi)"),
330      N_("HEIGHT")},
332     {"export-id", 'i',
333      POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
334      N_("The ID of the object to export"),
335      N_("ID")},
337     {"export-id-only", 'j',
338      POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
339      // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
340      //  See "man inkscape" for details.
341      N_("Export just the object with export-id, hide all others (only with export-id)"),
342      NULL},
344     {"export-use-hints", 't',
345      POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
346      N_("Use stored filename and DPI hints when exporting (only with export-id)"),
347      NULL},
349     {"export-background", 'b',
350      POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
351      N_("Background color of exported bitmap (any SVG-supported color string)"),
352      N_("COLOR")},
354     {"export-background-opacity", 'y',
355      POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
356      N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
357      N_("VALUE")},
359     {"export-plain-svg", 'l',
360      POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
361      N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
362      N_("FILENAME")},
364     {"export-ps", 'P',
365      POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
366      N_("Export document to a PS file"),
367      N_("FILENAME")},
369     {"export-eps", 'E',
370      POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
371      N_("Export document to an EPS file"),
372      N_("FILENAME")},
374     {"export-pdf", 'A',
375      POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
376      N_("Export document to a PDF file"),
377      N_("FILENAME")},
379     {"export-latex", 0,
380      POPT_ARG_NONE, &sp_export_latex, SP_ARG_EXPORT_LATEX,
381      N_("Export PDF/PS/EPS without text. Besides the PDF/PS/EPS, a LaTeX file is exported, putting the text on top of the PDF/PS/EPS file. Include the result in LaTeX like: \\input{latexfile.tex}"),
382      NULL},
384 #ifdef WIN32
385     {"export-emf", 'M',
386      POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
387      N_("Export document to an Enhanced Metafile (EMF) File"),
388      N_("FILENAME")},
389 #endif //WIN32
391     {"export-text-to-path", 'T',
392      POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
393      N_("Convert text object to paths on export (PS, EPS, PDF)"),
394      NULL},
396     {"export-ignore-filters", 0,
397      POPT_ARG_NONE, &sp_export_ignore_filters, SP_ARG_EXPORT_IGNORE_FILTERS,
398      N_("Render filtered objects without filters, instead of rasterizing (PS, EPS, PDF)"),
399      NULL},
401     {"query-x", 'X',
402      POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
403      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
404      N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
405      NULL},
407     {"query-y", 'Y',
408      POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
409      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
410      N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
411      NULL},
413     {"query-width", 'W',
414      POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
415      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
416      N_("Query the width of the drawing or, if specified, of the object with --query-id"),
417      NULL},
419     {"query-height", 'H',
420      POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
421      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
422      N_("Query the height of the drawing or, if specified, of the object with --query-id"),
423      NULL},
425     {"query-all", 'S',
426      POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
427      N_("List id,x,y,w,h for all objects"),
428      NULL},
430     {"query-id", 'I',
431      POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
432      N_("The ID of the object whose dimensions are queried"),
433      N_("ID")},
435     {"extension-directory", 'x',
436      POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
437      // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
438      N_("Print out the extension directory and exit"),
439      NULL},
441     {"vacuum-defs", 0,
442      POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
443      N_("Remove unused definitions from the defs section(s) of the document"),
444      NULL},
446     {"verb-list", 0,
447      POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
448      N_("List the IDs of all the verbs in Inkscape"),
449      NULL},
451     {"verb", 0,
452      POPT_ARG_STRING, NULL, SP_ARG_VERB,
453      N_("Verb to call when Inkscape opens."),
454      N_("VERB-ID")},
456     {"select", 0,
457      POPT_ARG_STRING, NULL, SP_ARG_SELECT,
458      N_("Object ID to select when Inkscape opens."),
459      N_("OBJECT-ID")},
461     {"shell", 0,
462      POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
463      N_("Start Inkscape in interactive shell mode."),
464      NULL},
466     POPT_AUTOHELP POPT_TABLEEND
467 };
469 static bool needToRecodeParams = true;
470 gchar * blankParam = g_strdup("");
474 #ifdef WIN32
476 /**
477  * Set up the PATH and PYTHONPATH environment variables on
478  * win32
479  * @param exe Inkscape executable directory in UTF-8
480  */
481 static void _win32_set_inkscape_env(gchar const *exe)
483     gchar const *path = g_getenv("PATH");
484     gchar const *pythonpath = g_getenv("PYTHONPATH");
486     gchar *python = g_build_filename(exe, "python", NULL);
487     gchar *scripts = g_build_filename(exe, "python", "Scripts", NULL);
488     gchar *perl = g_build_filename(exe, "python", NULL);
489     gchar *pythonlib = g_build_filename(exe, "python", "Lib", NULL);
490     gchar *pythondll = g_build_filename(exe, "python", "DLLs", NULL);
491     
492     // Python 2.x needs short paths in PYTHONPATH.
493     // Otherwise it doesn't work when Inkscape is installed in Unicode directories.
494     // g_win32_locale_filename_from_utf8 is the Glib equivalent
495     // of GetShortPathName.
496     // Remove this once we move to Python 3.0.
497     gchar *python_s = g_win32_locale_filename_from_utf8(python);
498     gchar *pythonlib_s = g_win32_locale_filename_from_utf8(pythonlib);
499     gchar *pythondll_s = g_win32_locale_filename_from_utf8(pythondll);
501     gchar *new_path;
502     gchar *new_pythonpath;
503     if (path) {
504         new_path = g_strdup_printf("%s;%s;%s;%s;%s", exe, python, scripts, perl, path);
505     } else {
506         new_path = g_strdup_printf("%s;%s;%s;%s", exe, python, scripts, perl);
507     }
508     if (pythonpath) {
509         new_pythonpath = g_strdup_printf("%s;%s;%s;%s", python_s, pythonlib_s, pythondll_s, pythonpath);
510     } else {
511         new_pythonpath = g_strdup_printf("%s;%s;%s", python_s, pythonlib_s, pythondll_s);
512     }
514     g_setenv("PATH", new_path, TRUE);
515     g_setenv("PYTHONPATH", new_pythonpath, TRUE);
517     /*
518     printf("PATH = %s\n\n", g_getenv("PATH"));
519     printf("PYTHONPATH = %s\n\n", g_getenv("PYTHONPATH"));
521     gchar *p = g_find_program_in_path("python");
522     if (p) {
523         printf("python in %s\n\n", p);
524         g_free(p);
525     } else {
526         printf("python not found\n\n");
527     }*/
529     g_free(python);
530     g_free(scripts);
531     g_free(perl);
532     g_free(pythonlib);
533     g_free(pythondll);
534     
535     g_free(python_s);
536     g_free(pythonlib_s);
537     g_free(pythondll_s);
539     g_free(new_path);
540     g_free(new_pythonpath);
542 #endif
544 /**
545  * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
546  * can find inkex.py et al. (Bug #197475)
547  */
548 static int set_extensions_env()
550     char *oldenv = getenv("PYTHONPATH");
551     Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
552     if (oldenv != NULL) {
553         tmp += G_SEARCHPATH_SEPARATOR;
554         tmp += oldenv;
555     }
556     g_setenv("PYTHONPATH", tmp.c_str(), TRUE);
558     return 0;
562 /**
563  * This is the classic main() entry point of the program, though on some
564  * architectures it might be called by something else.
565  */
566 int
567 main(int argc, char **argv)
569 #ifdef HAVE_FPSETMASK
570     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
571        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
572        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
573     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
574 #endif
576 #ifdef WIN32
577     /*
578       Set the current directory to the directory of the
579       executable.  This seems redundant, but is needed for
580       when inkscape.exe is executed from another directory.
581       We use relative paths on win32.
582       HKCR\svgfile\shell\open\command is a good example
584       TODO: this breaks the CLI on Windows, see LP #167455
585       However, the CLI is broken anyway, because we are a GUI app
586     */
587     const int pathbuf = 2048;
588     gunichar2 *path = g_new(gunichar2, pathbuf);
589     GetModuleFileNameW(NULL, (WCHAR*) path, pathbuf);
590     gchar *inkscape = g_utf16_to_utf8(path, -1, NULL, NULL, NULL);
591     gchar *exedir = g_path_get_dirname(inkscape);
592     gunichar2 *dirw = g_utf8_to_utf16(exedir, -1, NULL, NULL, NULL);
593     SetCurrentDirectoryW((WCHAR*) dirw);
594     _win32_set_inkscape_env(exedir);
596 # ifdef ENABLE_NLS
597     // obtain short path to executable dir and pass it
598     // to bindtextdomain (it doesn't understand UTF-8)
599     gchar *shortexedir = g_win32_locale_filename_from_utf8(exedir);
600     gchar *localepath = g_build_filename(shortexedir, PACKAGE_LOCALE_DIR, NULL);
601     bindtextdomain(GETTEXT_PACKAGE, localepath);
602     g_free(shortexedir);
603     g_free(localepath);
604 # endif
605     
606     g_free(path);
607     g_free(inkscape);
608     g_free(exedir);
609     g_free(dirw);
611     // Don't touch the registry (works fine without it) for Inkscape Portable
612     gchar const *val = g_getenv("INKSCAPE_PORTABLE_PROFILE_DIR");
613     if (!val) {
614         RegistryTool rt;
615         rt.setPathInfo();
616     }
617 #elif defined(ENABLE_NLS)
618 # ifdef ENABLE_BINRELOC
619     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
620 # else
621     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
622 # endif
623 #endif
625     // the bit below compiles regardless of platform
626 #ifdef ENABLE_NLS
627     // Allow the user to override the locale directory by setting
628     // the environment variable INKSCAPE_LOCALEDIR.
629     char const *inkscape_localedir = g_getenv("INKSCAPE_LOCALEDIR");
630     if (inkscape_localedir != NULL) {
631         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
632     }
634     // common setup
635     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
636     textdomain(GETTEXT_PACKAGE);
637 #endif
639     // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
640     Gtk::Main::init_gtkmm_internals();
642     // Bug #197475
643     set_extensions_env();
645     LIBXML_TEST_VERSION
647     Inkscape::GC::init();
649     Inkscape::Debug::Logger::init();
651     gboolean use_gui;
653 #ifndef WIN32
654     use_gui = (g_getenv("DISPLAY") != NULL);
655 #else
656     use_gui = TRUE;
657 #endif
658     /* Test whether with/without GUI is forced */
659     for (int i = 1; i < argc; i++) {
660         if (!strcmp(argv[i], "-z")
661             || !strcmp(argv[i], "--without-gui")
662             || !strcmp(argv[i], "-p")
663             || !strncmp(argv[i], "--print", 7)
664             || !strcmp(argv[i], "-e")
665             || !strncmp(argv[i], "--export-png", 12)
666             || !strcmp(argv[i], "-l")
667             || !strncmp(argv[i], "--export-plain-svg", 18)
668             || !strcmp(argv[i], "-i")
669             || !strncmp(argv[i], "--export-area-drawing", 21)
670             || !strcmp(argv[i], "-D")
671             || !strncmp(argv[i], "--export-area-page", 18)
672             || !strcmp(argv[i], "-C")
673             || !strncmp(argv[i], "--export-id", 11)
674             || !strcmp(argv[i], "-P")
675             || !strncmp(argv[i], "--export-ps", 11)
676             || !strcmp(argv[i], "-E")
677             || !strncmp(argv[i], "--export-eps", 12)
678             || !strcmp(argv[i], "-A")
679             || !strncmp(argv[i], "--export-pdf", 12)
680             || !strncmp(argv[i], "--export-latex", 14)
681 #ifdef WIN32
682             || !strcmp(argv[i], "-M")
683             || !strncmp(argv[i], "--export-emf", 12)
684 #endif //WIN32
685             || !strcmp(argv[i], "-W")
686             || !strncmp(argv[i], "--query-width", 13)
687             || !strcmp(argv[i], "-H")
688             || !strncmp(argv[i], "--query-height", 14)
689             || !strcmp(argv[i], "-S")
690             || !strncmp(argv[i], "--query-all", 11)
691             || !strcmp(argv[i], "-X")
692             || !strncmp(argv[i], "--query-x", 9)
693             || !strcmp(argv[i], "-Y")
694             || !strncmp(argv[i], "--query-y", 9)
695             || !strcmp(argv[i], "--vacuum-defs")
696             || !strcmp(argv[i], "--shell")
697            )
698         {
699             /* main_console handles any exports -- not the gui */
700             use_gui = FALSE;
701             break;
702         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
703             use_gui = TRUE;
704             break;
705         }
706     }
708 #ifdef WIN32
709 #ifndef REPLACEARGS_ANSI
710     if ( PrintWin32::is_os_wide() )
711 #endif // REPLACEARGS_ANSI
712     {
713         // If the call fails, we'll need to convert charsets
714         needToRecodeParams = !replaceArgs( argc, argv );
715     }
716 #endif // WIN32
718     /// \todo  Should this be a static object (see inkscape.cpp)?
719     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
721     return app.run();
727 void fixupSingleFilename( gchar **orig, gchar **spare )
729     if ( orig && *orig && **orig ) {
730         GError *error = NULL;
731         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
732         if ( newFileName )
733         {
734             *orig = newFileName;
735             if ( spare ) {
736                 *spare = newFileName;
737             }
738 //             g_message("Set a replacement fixup");
739         }
740     }
745 GSList *fixupFilenameEncoding( GSList* fl )
747     GSList *newFl = NULL;
748     while ( fl ) {
749         gchar *fn = static_cast<gchar*>(fl->data);
750         fl = g_slist_remove( fl, fl->data );
751         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
752         if ( newFileName ) {
754             if ( 0 )
755             {
756                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
757                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
758                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
759                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
760                 gtk_dialog_run (GTK_DIALOG (w));
761                 gtk_widget_destroy (w);
762                 g_free(safeNewFn);
763                 g_free(safeFn);
764             }
766             g_free( fn );
767             fn = newFileName;
768             newFileName = 0;
769         }
770         else
771             if ( 0 )
772         {
773             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
774             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
775             gtk_dialog_run (GTK_DIALOG (w));
776             gtk_widget_destroy (w);
777             g_free(safeFn);
778         }
779         newFl = g_slist_append( newFl, fn );
780     }
781     return newFl;
784 int sp_common_main( int argc, char const **argv, GSList **flDest )
786     /// \todo fixme: Move these to some centralized location (Lauris)
787     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
788     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
791     // temporarily switch gettext encoding to locale, so that help messages can be output properly
792     gchar const *charset;
793     g_get_charset(&charset);
795     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
797     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
798     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
799     g_return_val_if_fail(ctx != NULL, 1);
801     /* Collect own arguments */
802     GSList *fl = sp_process_args(ctx);
803     poptFreeContext(ctx);
805     // now switch gettext back to UTF-8 (for GUI)
806     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
808     // Now let's see if the file list still holds up
809     if ( needToRecodeParams )
810     {
811         fl = fixupFilenameEncoding( fl );
812     }
814     // Check the globals for filename-fixup
815     if ( needToRecodeParams )
816     {
817         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
818         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
819         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
820     }
821     else
822     {
823         if ( sp_export_png )
824             sp_export_png_utf8 = g_strdup( sp_export_png );
825         if ( sp_export_svg )
826             sp_export_svg_utf8 = g_strdup( sp_export_svg );
827         if ( sp_global_printer )
828             sp_global_printer_utf8 = g_strdup( sp_global_printer );
829     }
831     // Return the list if wanted, else free it up.
832     if ( flDest ) {
833         *flDest = fl;
834         fl = 0;
835     } else {
836         while ( fl ) {
837             g_free( fl->data );
838             fl = g_slist_remove( fl, fl->data );
839         }
840     }
841     return 0;
844 static void
845 snooper(GdkEvent *event, gpointer /*data*/) {
846     if (inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
847     {
848         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
849         switch (event->type) {
850             case GDK_MOTION_NOTIFY:
851                 if(event->motion.state & mapping) {
852                     event->motion.state|=GDK_MOD1_MASK;
853                 }
854                 break;
855             case GDK_BUTTON_PRESS:
856                 if(event->button.state & mapping) {
857                     event->button.state|=GDK_MOD1_MASK;
858                 }
859                 break;
860              case GDK_KEY_PRESS:
861                  if(event->key.state & mapping) {
862                      event->key.state|=GDK_MOD1_MASK;
863                  }
864                  break;
865         default:
866             break;
867         }
868     }
870     if (inkscape_trackalt()) {
871         // MacOS X with X11 has some problem with the default
872         // xmodmapping.  A ~/.xmodmap solution does not work reliably due
873         // to the way we package our executable in a .app that can launch
874         // X11 or use an already-running X11.  The same problem has been
875         // reported on Linux but there is no .app/X11 to get in the way
876         // of ~/.xmodmap fixes.  So we make this a preference.
877         //
878         // For some reason, Gdk senses changes in Alt (Mod1) state for
879         // many message types, but not for keystrokes!  So this ugly hack
880         // tracks what the state of Alt-pressing is, and ensures
881         // GDK_MOD1_MASK is in the event->key.state as appropriate.
882         //
883         static gboolean altL_pressed = FALSE;
884         static gboolean altR_pressed = FALSE;
885         static gboolean alt_pressed = FALSE;
886         guint get_group0_keyval(GdkEventKey* event);
887         guint keyval = 0;
888         switch (event->type) {
889         case GDK_MOTION_NOTIFY:
890             alt_pressed = TRUE && (event->motion.state & GDK_MOD1_MASK);
891             break;
892         case GDK_BUTTON_PRESS:
893             alt_pressed = TRUE && (event->button.state & GDK_MOD1_MASK);
894             break;
895         case GDK_KEY_PRESS:
896             keyval = get_group0_keyval(&event->key);
897             if (keyval == GDK_Alt_L) altL_pressed = TRUE;
898             if (keyval == GDK_Alt_R) altR_pressed = TRUE;
899             alt_pressed = alt_pressed || altL_pressed || altR_pressed;
900             alt_pressed = alt_pressed || (event->button.state & GDK_MOD1_MASK);
901             if (alt_pressed)
902                 event->key.state |= GDK_MOD1_MASK;
903             else
904                 event->key.state &= ~GDK_MOD1_MASK;
905             break;
906         case GDK_KEY_RELEASE:
907             keyval = get_group0_keyval(&event->key);
908             if (keyval == GDK_Alt_L) altL_pressed = FALSE;
909             if (keyval == GDK_Alt_R) altR_pressed = FALSE;
910             if (!altL_pressed && !altR_pressed)
911                 alt_pressed = FALSE;
912             break;
913         default:
914             break;
915         }
916         //printf("alt_pressed: %s\n", alt_pressed? "+" : "-");
917     }
919     gtk_main_do_event (event);
922 static std::vector<Glib::ustring> getDirectorySet(const gchar* userDir, const gchar* const * systemDirs) {
923     std::vector<Glib::ustring> listing;
924     listing.push_back(userDir);
925     for ( const char* const* cur = systemDirs; *cur; cur++ )
926     {
927         listing.push_back(*cur);
928     }
929     return listing;
932 int
933 sp_main_gui(int argc, char const **argv)
935     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
937     GSList *fl = NULL;
938     int retVal = sp_common_main( argc, argv, &fl );
939     g_return_val_if_fail(retVal == 0, 1);
941     // Add possible icon entry directories
942     std::vector<Glib::ustring> dataDirs = getDirectorySet( g_get_user_data_dir(),
943                                                            g_get_system_data_dirs() );
944     for (std::vector<Glib::ustring>::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
945     {
946         std::vector<Glib::ustring> listing;
947         listing.push_back(*it);
948         listing.push_back("inkscape");
949         listing.push_back("icons");
950         Glib::ustring dir = Glib::build_filename(listing);
951         gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir.c_str());
952     }
954     // Add our icon directory to the search path for icon theme lookups.
955     gchar *usericondir = profile_path("icons");
956     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), usericondir);
957     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
958     g_free(usericondir);
960     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
961     Inkscape::Debug::log_display_config();
963     // Set default window icon. Obeys the theme.
964     gtk_window_set_default_icon_name("inkscape");
965     // Do things that were previously in inkscape_gtk_stock_init().
966     sp_icon_get_phys_size(GTK_ICON_SIZE_MENU);
967     Inkscape::UI::Widget::Panel::prep();
969     gboolean create_new = TRUE;
971     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
972     inkscape_application_init(argv[0], true);
974     while (fl) {
975         if (sp_file_open((gchar *)fl->data,NULL)) {
976             create_new=FALSE;
977         }
978         fl = g_slist_remove(fl, fl->data);
979     }
980     if (create_new) {
981         sp_file_new_default();
982     }
984     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
985     main_instance.run();
987 #ifdef WIN32
988     //We might not need anything here
989     //sp_win32_finish(); <-- this is a NOP func
990 #endif
992     return 0;
995 /**
996  * Process file list
997  */
998 void sp_process_file_list(GSList *fl)
1000     while (fl) {
1001         const gchar *filename = (gchar *)fl->data;
1003         SPDocument *doc = NULL;
1004         try {
1005             doc = Inkscape::Extension::open(NULL, filename);
1006         } catch (Inkscape::Extension::Input::no_extension_found &e) {
1007             doc = NULL;
1008         } catch (Inkscape::Extension::Input::open_failed &e) {
1009             doc = NULL;
1010         }
1012         if (doc == NULL) {
1013             try {
1014                 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
1015             } catch (Inkscape::Extension::Input::no_extension_found &e) {
1016                 doc = NULL;
1017             } catch (Inkscape::Extension::Input::open_failed &e) {
1018                 doc = NULL;
1019             }
1020         }
1021         if (doc == NULL) {
1022             g_warning("Specified document %s cannot be opened (does not exist or not a valid SVG file)", filename);
1023         } else {
1024             if (sp_vacuum_defs) {
1025                 vacuum_document(doc);
1026             }
1027             if (sp_vacuum_defs && !sp_export_svg) {
1028                 // save under the name given in the command line
1029                 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
1030             }
1031             if (sp_global_printer) {
1032                 sp_print_document_to_file(doc, sp_global_printer);
1033             }
1034             if (sp_export_png || (sp_export_id && sp_export_use_hints)) {
1035                 sp_do_export_png(doc);
1036             }
1037             if (sp_export_svg) {
1038                 Inkscape::XML::Document *rdoc;
1039                 Inkscape::XML::Node *repr;
1040                 rdoc = sp_repr_document_new("svg:svg");
1041                 repr = rdoc->root();
1042                 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
1043                 sp_repr_save_rebased_file(repr->document(), sp_export_svg, SP_SVG_NS_URI,
1044                                           doc->base, sp_export_svg);
1045             }
1046             if (sp_export_ps) {
1047                 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
1048             }
1049             if (sp_export_eps) {
1050                 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
1051             }
1052             if (sp_export_pdf) {
1053                 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
1054             }
1055 #ifdef WIN32
1056             if (sp_export_emf) {
1057                 do_export_emf(doc, sp_export_emf, "image/x-emf");
1058             }
1059 #endif //WIN32
1060             if (sp_query_all) {
1061                 do_query_all (doc);
1062             } else if (sp_query_width || sp_query_height) {
1063                 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
1064             } else if (sp_query_x || sp_query_y) {
1065                 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
1066             }
1068             delete doc;
1069         }
1070         fl = g_slist_remove(fl, fl->data);
1071     }
1074 /**
1075  * Run the application as an interactive shell, parsing command lines from stdin
1076  * Returns -1 on error.
1077  */
1078 int sp_main_shell(char const* command_name)
1080     int retval = 0;
1082     const unsigned int buffer_size = 4096;
1083     gchar *command_line = g_strnfill(buffer_size, 0);
1084     g_strlcpy(command_line, command_name, buffer_size);
1085     gsize offset = g_strlcat(command_line, " ", buffer_size);
1086     gsize sizeLeft = buffer_size - offset;
1087     gchar *useme = command_line + offset;
1089     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
1090     fflush(stdout);
1091     char* linedata = 0;
1092     do {
1093         fprintf(stdout, ">");
1094         fflush(stdout);
1095         if ((linedata = fgets(useme, sizeLeft, stdin))) {
1096             size_t len = strlen(useme);
1097             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
1098                 fprintf(stdout, "ERROR: Command line too long\n");
1099                 // Consume rest of line
1100                 retval = -1; // If the while loop completes, this remains -1
1101                 while (fgets(useme, sizeLeft, stdin) && retval) {
1102                     len = strlen(command_line);
1103                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
1104                         retval = 0;
1105                     }
1106                 }
1107             } else {
1108                 useme[--len] = '\0';  // Strip newline
1109                 if (useme[len - 1] == '\r') {
1110                     useme[--len] = '\0';
1111                 }
1112                 if ( strcmp(useme, "quit") == 0 ) {
1113                     // Time to quit
1114                     fflush(stdout);
1115                     linedata = 0; // mark for exit
1116                 } else if ( len < 1 ) {
1117                     // blank string. Do nothing.
1118                 } else {
1119                     GError* parseError = 0;
1120                     gchar** argv = 0;
1121                     gint argc = 0;
1122                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
1123                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
1124                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
1125                         if ( ctx ) {
1126                             GSList *fl = sp_process_args(ctx);
1127                             sp_process_file_list(fl);
1128                             poptFreeContext(ctx);
1129                         } else {
1130                             retval = 1; // not sure why. But this was the previous return value
1131                         }
1132                         resetCommandlineGlobals();
1133                         g_strfreev(argv);
1134                     } else {
1135                         g_warning("Cannot parse commandline: %s", useme);
1136                     }
1137                 }
1138             }
1139         } // if (linedata...
1140     } while (linedata && (retval == 0));
1142     g_free(command_line);
1143     return retval;
1146 int sp_main_console(int argc, char const **argv)
1148     /* We are started in text mode */
1150     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1151      * in a non-Gtk environment.  Used in libnrtype's
1152      * FontInstance.cpp and FontFactory.cpp.
1153      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1154      */
1155     g_type_init();
1156     char **argv2 = const_cast<char **>(argv);
1157     gtk_init_check( &argc, &argv2 );
1158     //setlocale(LC_ALL, "");
1160     GSList *fl = NULL;
1161     int retVal = sp_common_main( argc, argv, &fl );
1162     g_return_val_if_fail(retVal == 0, 1);
1164     if (fl == NULL && !sp_shell) {
1165         g_print("Nothing to do!\n");
1166         exit(0);
1167     }
1169     inkscape_application_init(argv[0], false);
1171     if (sp_shell) {
1172         sp_main_shell(argv[0]); // Run as interactive shell
1173         exit(0);
1174     } else {
1175         sp_process_file_list(fl); // Normal command line invokation
1176     }
1178     return 0;
1181 static void
1182 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1184     SPObject *o = NULL;
1186     if (id) {
1187         o = doc->getObjectById(id);
1188         if (o) {
1189             if (!SP_IS_ITEM (o)) {
1190                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1191                 return;
1192             }
1193         } else {
1194             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1195             return;
1196         }
1197     } else {
1198         o = SP_DOCUMENT_ROOT(doc);
1199     }
1201     if (o) {
1202         sp_document_ensure_up_to_date (doc);
1203         SPItem *item = ((SPItem *) o);
1205         // "true" SVG bbox for scripting
1206         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1207         if (area) {
1208             Inkscape::SVGOStringStream os;
1209             if (extent) {
1210                 os << area->dimensions()[axis];
1211             } else {
1212                 os << area->min()[axis];
1213             }
1214             g_print ("%s", os.str().c_str());
1215         } else {
1216             g_print("0");
1217         }
1218     }
1221 static void
1222 do_query_all (SPDocument *doc)
1224     SPObject *o = NULL;
1226     o = SP_DOCUMENT_ROOT(doc);
1228     if (o) {
1229         sp_document_ensure_up_to_date (doc);
1230         do_query_all_recurse(o);
1231     }
1234 static void
1235 do_query_all_recurse (SPObject *o)
1237     SPItem *item = ((SPItem *) o);
1238     if (o->getId() && SP_IS_ITEM(item)) {
1239         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1240         if (area) {
1241             Inkscape::SVGOStringStream os;
1242             os << o->getId();
1243             os << "," << area->min()[Geom::X];
1244             os << "," << area->min()[Geom::Y];
1245             os << "," << area->dimensions()[Geom::X];
1246             os << "," << area->dimensions()[Geom::Y];
1247             g_print ("%s\n", os.str().c_str());
1248         }
1249     }
1251     SPObject *child = o->children;
1252     while (child) {
1253         do_query_all_recurse (child);
1254         child = child->next;
1255     }
1259 static void
1260 sp_do_export_png(SPDocument *doc)
1262     const gchar *filename = NULL;
1263     bool filename_from_hint = false;
1264     gdouble dpi = 0.0;
1266     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1267         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1268     }
1270     GSList *items = NULL;
1272     Geom::Rect area;
1273     if (sp_export_id || sp_export_area_drawing) {
1275         SPObject *o = NULL;
1276         SPObject *o_area = NULL;
1277         if (sp_export_id && sp_export_area_drawing) {
1278             o = doc->getObjectById(sp_export_id);
1279             o_area = SP_DOCUMENT_ROOT (doc);
1280         } else if (sp_export_id) {
1281             o = doc->getObjectById(sp_export_id);
1282             o_area = o;
1283         } else if (sp_export_area_drawing) {
1284             o = SP_DOCUMENT_ROOT (doc);
1285             o_area = o;
1286         }
1288         if (o) {
1289             if (!SP_IS_ITEM (o)) {
1290                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1291                 return;
1292             }
1294             items = g_slist_prepend (items, SP_ITEM(o));
1296             if (sp_export_id_only) {
1297                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1298             }
1300             if (sp_export_use_hints) {
1302                 // retrieve export filename hint
1303                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1304                 if (fn_hint) {
1305                     if (sp_export_png) {
1306                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1307                         filename = sp_export_png;
1308                     } else {
1309                         filename = fn_hint;
1310                         filename_from_hint = true;
1311                     }
1312                 } else {
1313                     g_warning ("Export filename hint not found for the object.");
1314                     filename = sp_export_png;
1315                 }
1317                 // retrieve export dpi hints
1318                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1319                 if (dpi_hint) {
1320                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1321                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1322                     } else {
1323                         dpi = atof(dpi_hint);
1324                     }
1325                 } else {
1326                     g_warning ("Export DPI hint not found for the object.");
1327                 }
1329             }
1331             // write object bbox to area
1332             sp_document_ensure_up_to_date (doc);
1333             Geom::OptRect areaMaybe;
1334             sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1335             if (areaMaybe) {
1336                 area = *areaMaybe;
1337             } else {
1338                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1339                 return;
1340             }
1341         } else {
1342             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1343             return;
1344         }
1345     }
1347     if (sp_export_area) {
1348         /* Try to parse area (given in SVG pixels) */
1349         gdouble x0,y0,x1,y1;
1350         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1351             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1352             return;
1353         }
1354         area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1355     } else if (sp_export_area_page || !(sp_export_id || sp_export_area_drawing)) {
1356         /* Export the whole page: note: Inkscape uses 'page' in all menus and dialogs, not 'canvas' */
1357         sp_document_ensure_up_to_date (doc);
1358         Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1359         area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1360     }
1362     // set filename and dpi from options, if not yet set from the hints
1363     if (!filename) {
1364         if (!sp_export_png) {
1365             g_warning ("No export filename given and no filename hint. Nothing exported.");
1366             return;
1367         }
1368         filename = sp_export_png;
1369     }
1371     if (sp_export_dpi && dpi == 0.0) {
1372         dpi = atof(sp_export_dpi);
1373         if ((dpi < 0.1) || (dpi > 10000.0)) {
1374             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1375             return;
1376         }
1377         g_print("DPI: %g\n", dpi);
1378     }
1380     if (sp_export_area_snap) {
1381         round_rectangle_outwards(area);
1382     }
1384     // default dpi
1385     if (dpi == 0.0) {
1386         dpi = PX_PER_IN;
1387     }
1389     unsigned long int width = 0;
1390     unsigned long int height = 0;
1392     if (sp_export_width) {
1393         errno=0;
1394         width = strtoul(sp_export_width, NULL, 0);
1395         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1396             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1397             return;
1398         }
1399         dpi = (gdouble) width * PX_PER_IN / area.width();
1400     }
1402     if (sp_export_height) {
1403         errno=0;
1404         height = strtoul(sp_export_height, NULL, 0);
1405         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1406             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1407             return;
1408         }
1409         dpi = (gdouble) height * PX_PER_IN / area.height();
1410     }
1412     if (!sp_export_width) {
1413         width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1414     }
1416     if (!sp_export_height) {
1417         height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1418     }
1420     guint32 bgcolor = 0x00000000;
1421     if (sp_export_background) {
1422         // override the page color
1423         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1424         bgcolor |= 0xff; // default is no opacity
1425     } else {
1426         // read from namedview
1427         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1428         if (nv && nv->attribute("pagecolor"))
1429             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1430         if (nv && nv->attribute("inkscape:pageopacity"))
1431             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1432     }
1434     if (sp_export_background_opacity) {
1435         // override opacity
1436         gfloat value;
1437         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1438             if (value > 1.0) {
1439                 value = CLAMP (value, 1.0f, 255.0f);
1440                 bgcolor &= (guint32) 0xffffff00;
1441                 bgcolor |= (guint32) floor(value);
1442             } else {
1443                 value = CLAMP (value, 0.0f, 1.0f);
1444                 bgcolor &= (guint32) 0xffffff00;
1445                 bgcolor |= SP_COLOR_F_TO_U(value);
1446             }
1447         }
1448     }
1450     gchar *path = 0;
1451     if (filename_from_hint) {
1452         //Make relative paths go from the document location, if possible:
1453         if (!g_path_is_absolute(filename) && doc->uri) {
1454             gchar *dirname = g_path_get_dirname(doc->uri);
1455             if (dirname) {
1456                 path = g_build_filename(dirname, filename, NULL);
1457                 g_free(dirname);
1458             }
1459         }
1460         if (!path) {
1461             path = g_strdup(filename);
1462         }
1463     } else {
1464         path = g_strdup(filename);
1465     }
1467     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1469     g_print("Area %g:%g:%g:%g exported to %lu x %lu pixels (%g dpi)\n", area[Geom::X][0], area[Geom::Y][0], area[Geom::X][1], area[Geom::Y][1], width, height, dpi);
1471     g_print("Bitmap saved as: %s\n", filename);
1473     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1474         sp_export_png_file(doc, path, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1475     } else {
1476         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1477     }
1479     g_free (path);
1480     g_slist_free (items);
1484 /**
1485  *  Perform a PDF/PS/EPS export
1486  *
1487  *  \param doc Document to export.
1488  *  \param uri URI to export to.
1489  *  \param mime MIME type to export as.
1490  */
1492 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1494     Inkscape::Extension::DB::OutputList o;
1495     Inkscape::Extension::db.get_output_list(o);
1496     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1497     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1498         i++;
1499     }
1501     if (i == o.end())
1502     {
1503         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1504         return;
1505     }
1507     if (sp_export_id) {
1508         SPObject *o = doc->getObjectById(sp_export_id);
1509         if (o == NULL) {
1510             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1511             return;
1512         }
1513         (*i)->set_param_string ("exportId", sp_export_id);
1514     } else {
1515         (*i)->set_param_string ("exportId", "");
1516     }
1518     if (sp_export_area_page && sp_export_area_drawing) {
1519         g_warning ("You cannot use --export-area-page and --export-area-drawing at the same time; only the former will take effect.");
1520         sp_export_area_drawing = false;
1521     }
1523     if (sp_export_area_drawing) {
1524         (*i)->set_param_bool ("areaDrawing", TRUE);
1525     } else {
1526         (*i)->set_param_bool ("areaDrawing", FALSE);
1527     }
1529     if (sp_export_area_page) {
1530         if (sp_export_eps) {
1531             g_warning ("EPS cannot have its bounding box extend beyond its content, so if your drawing is smaller than the page, --export-area-page will clip it to drawing.");
1532         }
1533         (*i)->set_param_bool ("areaPage", TRUE);
1534     } else {
1535         (*i)->set_param_bool ("areaPage", FALSE);
1536     }
1538     if (!sp_export_area_drawing && !sp_export_area_page && !sp_export_id) {
1539         // neither is set, set page as default for ps/pdf and drawing for eps
1540         if (sp_export_eps) {
1541             try {
1542                (*i)->set_param_bool("areaDrawing", TRUE);
1543             } catch (...) {}
1544         }
1545     }
1547     if (sp_export_text_to_path) {
1548         (*i)->set_param_bool("textToPath", TRUE);
1549     } else {
1550         (*i)->set_param_bool("textToPath", FALSE);
1551     }
1553     if (sp_export_latex) {
1554         (*i)->set_param_bool("textToLaTeX", TRUE);
1555     } else {
1556         (*i)->set_param_bool("textToLaTeX", FALSE);
1557     }
1559     if (sp_export_ignore_filters) {
1560         (*i)->set_param_bool("blurToBitmap", FALSE);
1561     } else {
1562         (*i)->set_param_bool("blurToBitmap", TRUE);
1564         gdouble dpi = 90.0;
1565         if (sp_export_dpi) {
1566             dpi = atof(sp_export_dpi);
1567             if ((dpi < 1) || (dpi > 10000.0)) {
1568                 g_warning("DPI value %s out of range [1 - 10000]. Using 90 dpi instead.", sp_export_dpi);
1569                 dpi = 90;
1570             }
1571         }
1573         (*i)->set_param_int("resolution", (int) dpi);
1574     }
1576     (*i)->save(doc, uri);
1579 #ifdef WIN32
1580 /**
1581  *  Export a document to EMF
1582  *
1583  *  \param doc Document to export.
1584  *  \param uri URI to export to.
1585  *  \param mime MIME type to export as (should be "image/x-emf")
1586  */
1588 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1590     Inkscape::Extension::DB::OutputList o;
1591     Inkscape::Extension::db.get_output_list(o);
1592     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1593     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1594         i++;
1595     }
1597     if (i == o.end())
1598     {
1599         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1600         return;
1601     }
1603     (*i)->save(doc, uri);
1605 #endif //WIN32
1607 #ifdef WIN32
1608 bool replaceArgs( int& argc, char**& argv )
1610     bool worked = false;
1612 #ifdef REPLACEARGS_DEBUG
1613     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1614 #endif // REPLACEARGS_DEBUG
1616     wchar_t* line = GetCommandLineW();
1617     if ( line )
1618     {
1619 #ifdef REPLACEARGS_DEBUG
1620         {
1621             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1622             if ( utf8Line )
1623             {
1624                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1625                 {
1626                     char tmp[strlen(safe) + 32];
1627                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1628                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1629                 }
1630             }
1631         }
1632 #endif // REPLACEARGS_DEBUG
1634         int numArgs = 0;
1635         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1637 #ifdef REPLACEARGS_ANSI
1638 // test code for trying things on Win95/98/ME
1639         if ( !parsed )
1640         {
1641 #ifdef REPLACEARGS_DEBUG
1642             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1643 #endif // REPLACEARGS_DEBUG
1644             int lineLen = wcslen(line) + 1;
1645             wchar_t* lineDup = new wchar_t[lineLen];
1646             wcsncpy( lineDup, line, lineLen );
1648             int pos = 0;
1649             bool inQuotes = false;
1650             bool inWhitespace = true;
1651             std::vector<int> places;
1652             while ( lineDup[pos] )
1653             {
1654                 if ( inQuotes )
1655                 {
1656                     if ( lineDup[pos] == L'"' )
1657                     {
1658                         inQuotes = false;
1659                     }
1660                 }
1661                 else if ( lineDup[pos] == L'"' )
1662                 {
1663                     inQuotes = true;
1664                     inWhitespace = false;
1665                     places.push_back(pos);
1666                 }
1667                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1668                 {
1669                     if ( !inWhitespace )
1670                     {
1671                         inWhitespace = true;
1672                         lineDup[pos] = 0;
1673                     }
1674                 }
1675                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1676                 {
1677                     inWhitespace = false;
1678                     places.push_back(pos);
1679                 }
1680                 else
1681                 {
1682                     // consume
1683                 }
1684                 pos++;
1685             }
1686 #ifdef REPLACEARGS_DEBUG
1687             {
1688                 char tmp[256];
1689                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1690                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1691             }
1692 #endif // REPLACEARGS_DEBUG
1694             wchar_t** block = new wchar_t*[places.size()];
1695             int i = 0;
1696             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1697             {
1698                 block[i++] = &lineDup[*it];
1699             }
1700             parsed = block;
1701             numArgs = places.size();
1702         }
1703 #endif // REPLACEARGS_ANSI
1705         if ( parsed )
1706         {
1707             std::vector<wchar_t*>expandedArgs;
1708             if ( numArgs > 0 )
1709             {
1710                 expandedArgs.push_back( parsed[0] );
1711             }
1713             for ( int i1 = 1; i1 < numArgs; i1++ )
1714             {
1715                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1716                 wildcarded &= parsed[i1][0] != L'"';
1717                 wildcarded &= parsed[i1][0] != L'-';
1718                 if ( wildcarded )
1719                 {
1720 #ifdef REPLACEARGS_ANSI
1721                     WIN32_FIND_DATAA data;
1722 #else
1723                     WIN32_FIND_DATAW data;
1724 #endif // REPLACEARGS_ANSI
1726                     memset((void *)&data, 0, sizeof(data));
1728                     int baseLen = wcslen(parsed[i1]) + 2;
1729                     wchar_t* base = new wchar_t[baseLen];
1730                     wcsncpy( base, parsed[i1], baseLen );
1731                     wchar_t* last = wcsrchr( base, L'\\' );
1732                     if ( last )
1733                     {
1734                         last[1] = 0;
1735                     }
1736                     else
1737                     {
1738                         base[0] = 0;
1739                     }
1740                     baseLen = wcslen( base );
1742 #ifdef REPLACEARGS_ANSI
1743                     char target[MAX_PATH];
1744                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1745                     {
1746                         HANDLE hf = FindFirstFileA( target, &data );
1747 #else
1748                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1749 #endif // REPLACEARGS_ANSI
1750                         if ( hf != INVALID_HANDLE_VALUE )
1751                         {
1752                             BOOL found = TRUE;
1753                             do
1754                             {
1755 #ifdef REPLACEARGS_ANSI
1756                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1757                                 if ( howMany > 0 )
1758                                 {
1759                                     howMany += baseLen;
1760                                     wchar_t* tmp = new wchar_t[howMany + 1];
1761                                     wcsncpy( tmp, base, howMany + 1 );
1762                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1763                                     expandedArgs.push_back( tmp );
1764                                     found = FindNextFileA( hf, &data );
1765                                 }
1766 #else
1767                                 int howMany = wcslen(data.cFileName) + baseLen;
1768                                 wchar_t* tmp = new wchar_t[howMany + 1];
1769                                 wcsncpy( tmp, base, howMany + 1 );
1770                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1771                                 expandedArgs.push_back( tmp );
1772                                 found = FindNextFileW( hf, &data );
1773 #endif // REPLACEARGS_ANSI
1774                             } while ( found );
1776                             FindClose( hf );
1777                         }
1778                         else
1779                         {
1780                             expandedArgs.push_back( parsed[i1] );
1781                         }
1782 #ifdef REPLACEARGS_ANSI
1783                     }
1784 #endif // REPLACEARGS_ANSI
1786                     delete[] base;
1787                 }
1788                 else
1789                 {
1790                     expandedArgs.push_back( parsed[i1] );
1791                 }
1792             }
1794             {
1795                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1796                 int iz = 0;
1797                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1798                 {
1799                     block[iz++] = *it;
1800                 }
1801                 parsed = block;
1802                 numArgs = expandedArgs.size();
1803             }
1805             std::vector<gchar*> newArgs;
1806             for ( int i = 0; i < numArgs; i++ )
1807             {
1808                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1809                 if ( replacement )
1810                 {
1811 #ifdef REPLACEARGS_DEBUG
1812                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1814                     if ( safe2 )
1815                     {
1816                         {
1817                             char tmp[1024];
1818                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1819                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1820                         }
1821                         g_free( safe2 );
1822                     }
1823 #endif // REPLACEARGS_DEBUG
1825                     newArgs.push_back( replacement );
1826                 }
1827                 else
1828                 {
1829                     newArgs.push_back( blankParam );
1830                 }
1831             }
1833             // Now push our munged params to be the new argv and argc
1834             {
1835                 char** block = new char*[newArgs.size()];
1836                 int iz = 0;
1837                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1838                 {
1839                     block[iz++] = *it;
1840                 }
1841                 argv = block;
1842                 argc = newArgs.size();
1843                 worked = true;
1844             }
1845         }
1846 #ifdef REPLACEARGS_DEBUG
1847         else
1848         {
1849             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1850         }
1851 #endif // REPLACEARGS_DEBUG
1852     }
1853 #ifdef REPLACEARGS_DEBUG
1854     else
1855     {
1856         {
1857             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1858         }
1860         char* line2 = GetCommandLineA();
1861         if ( line2 )
1862         {
1863             gchar *safe = Inkscape::IO::sanitizeString(line2);
1864             {
1865                 {
1866                     char tmp[strlen(safe) + 32];
1867                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1868                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1869                 }
1870             }
1871         }
1872         else
1873         {
1874             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1875         }
1876     }
1877 #endif // REPLACEARGS_DEBUG
1879     return worked;
1881 #endif // WIN32
1883 static GSList *
1884 sp_process_args(poptContext ctx)
1886     GSList *fl = NULL;
1888     gint a;
1889     while ((a = poptGetNextOpt(ctx)) != -1) {
1890         switch (a) {
1891             case SP_ARG_FILE: {
1892                 gchar const *fn = poptGetOptArg(ctx);
1893                 if (fn != NULL) {
1894                     fl = g_slist_append(fl, g_strdup(fn));
1895                 }
1896                 break;
1897             }
1898             case SP_ARG_VERSION: {
1899                 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1900                 exit(0);
1901                 break;
1902             }
1903             case SP_ARG_EXTENSIONDIR: {
1904                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1905                 exit(0);
1906                 break;
1907             }
1908             case SP_ARG_VERB_LIST: {
1909                 // This really shouldn't go here, we should init the app.
1910                 // But, since we're just exiting in this path, there is
1911                 // no harm, and this is really a better place to put
1912                 // everything else.
1913                 Inkscape::Extension::init();
1914                 Inkscape::Verb::list();
1915                 exit(0);
1916                 break;
1917             }
1918             case SP_ARG_VERB:
1919             case SP_ARG_SELECT: {
1920                 gchar const *arg = poptGetOptArg(ctx);
1921                 if (arg != NULL) {
1922                     // printf("Adding in: %s\n", arg);
1923                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1924                 }
1925                 break;
1926             }
1927             case POPT_ERROR_BADOPT: {
1928                 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1929                 exit(1);
1930                 break;
1931             }
1932             default: {
1933                 break;
1934             }
1935         }
1936     }
1938     gchar const ** const args = poptGetArgs(ctx);
1939     if (args != NULL) {
1940         for (unsigned i = 0; args[i] != NULL; i++) {
1941             fl = g_slist_append(fl, g_strdup(args[i]));
1942         }
1943     }
1945     return fl;
1949 /*
1950   Local Variables:
1951   mode:c++
1952   c-file-style:"stroustrup"
1953   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1954   indent-tabs-mode:nil
1955   fill-column:99
1956   End:
1957 */
1958 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :