Code

Do not print PYTHONPATH on every invocation (stale debug output)
[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 Windows
478  * @param exe Inkscape executable directory in UTF-8
479  */
480 static void _win32_set_inkscape_env(gchar const *exe)
482     gchar const *path = g_getenv("PATH");
483     gchar const *pythonpath = g_getenv("PYTHONPATH");
485     gchar *python = g_build_filename(exe, "python", NULL);
486     gchar *scripts = g_build_filename(exe, "python", "Scripts", NULL);
487     gchar *perl = g_build_filename(exe, "python", NULL);
488     gchar *pythonlib = g_build_filename(exe, "python", "Lib", NULL);
489     gchar *pythondll = g_build_filename(exe, "python", "DLLs", NULL);
490     
491     // Python 2.x needs short paths in PYTHONPATH.
492     // Otherwise it doesn't work when Inkscape is installed in Unicode directories.
493     // g_win32_locale_filename_from_utf8 is the GLib wrapper for GetShortPathName.
494     // Remove this once we move to Python 3.0.
495     gchar *python_s = g_win32_locale_filename_from_utf8(python);
496     gchar *pythonlib_s = g_win32_locale_filename_from_utf8(pythonlib);
497     gchar *pythondll_s = g_win32_locale_filename_from_utf8(pythondll);
499     gchar *new_path;
500     gchar *new_pythonpath;
501     if (path) {
502         new_path = g_strdup_printf("%s;%s;%s;%s;%s", exe, python, scripts, perl, path);
503     } else {
504         new_path = g_strdup_printf("%s;%s;%s;%s", exe, python, scripts, perl);
505     }
506     if (pythonpath) {
507         new_pythonpath = g_strdup_printf("%s;%s;%s;%s",
508              python_s, pythonlib_s, pythondll_s, pythonpath);
509     } else {
510         new_pythonpath = g_strdup_printf("%s;%s;%s",
511             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 static void set_extensions_env()
546     gchar const *pythonpath = g_getenv("PYTHONPATH");
547     gchar *extdir;
548     gchar *new_pythonpath;
549     
550 #ifdef WIN32
551     extdir = g_win32_locale_filename_from_utf8(INKSCAPE_EXTENSIONDIR);
552 #else
553     extdir = g_strdup(INKSCAPE_EXTENSIONDIR);
554 #endif
556     // On some platforms, INKSCAPE_EXTENSIONDIR is not absolute,
557     // but relative to the directory that contains the Inkscape executable.
558     // Since we spawn Python chdir'ed into the script's directory,
559     // we need to obtain the absolute path here.
560     if (!g_path_is_absolute(extdir)) {
561         gchar *curdir = g_get_current_dir();
562         gchar *extdir_new = g_build_filename(curdir, extdir, NULL);
563         g_free(extdir);
564         g_free(curdir);
565         extdir = extdir_new;
566     }
568     if (pythonpath) {
569         new_pythonpath = g_strdup_printf("%s" G_SEARCHPATH_SEPARATOR_S "%s",
570                                          extdir, pythonpath);
571         g_free(extdir);
572     } else {
573         new_pythonpath = extdir;
574     }
576     g_setenv("PYTHONPATH", new_pythonpath, TRUE);
577     g_free(new_pythonpath);
578     //printf("PYTHONPATH = %s\n", g_getenv("PYTHONPATH"));
581 /**
582  * This is the classic main() entry point of the program, though on some
583  * architectures it might be called by something else.
584  */
585 int
586 main(int argc, char **argv)
588 #ifdef HAVE_FPSETMASK
589     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
590        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
591        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
592     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
593 #endif
595 #ifdef WIN32
596     /*
597       Set the current directory to the directory of the
598       executable.  This seems redundant, but is needed for
599       when inkscape.exe is executed from another directory.
600       We use relative paths on win32.
601       HKCR\svgfile\shell\open\command is a good example
603       TODO: this breaks the CLI on Windows, see LP #167455
604     */
605     const int pathbuf = 2048;
606     gunichar2 *path = g_new(gunichar2, pathbuf);
607     GetModuleFileNameW(NULL, (WCHAR*) path, pathbuf);
608     gchar *inkscape = g_utf16_to_utf8(path, -1, NULL, NULL, NULL);
609     gchar *exedir = g_path_get_dirname(inkscape);
610     gunichar2 *dirw = g_utf8_to_utf16(exedir, -1, NULL, NULL, NULL);
611     SetCurrentDirectoryW((WCHAR*) dirw);
612     _win32_set_inkscape_env(exedir);
614 # ifdef ENABLE_NLS
615     // obtain short path to executable dir and pass it
616     // to bindtextdomain (it doesn't understand UTF-8)
617     gchar *shortexedir = g_win32_locale_filename_from_utf8(exedir);
618     gchar *localepath = g_build_filename(shortexedir, PACKAGE_LOCALE_DIR, NULL);
619     bindtextdomain(GETTEXT_PACKAGE, localepath);
620     g_free(shortexedir);
621     g_free(localepath);
622 # endif
623     
624     g_free(path);
625     g_free(inkscape);
626     g_free(exedir);
627     g_free(dirw);
629     // Don't touch the registry (works fine without it) for Inkscape Portable
630     gchar const *val = g_getenv("INKSCAPE_PORTABLE_PROFILE_DIR");
631     if (!val) {
632         RegistryTool rt;
633         rt.setPathInfo();
634     }
635 #elif defined(ENABLE_NLS)
636 # ifdef ENABLE_BINRELOC
637     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
638 # else
639     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
640 # endif
641 #endif
643     // the bit below compiles regardless of platform
644 #ifdef ENABLE_NLS
645     // Allow the user to override the locale directory by setting
646     // the environment variable INKSCAPE_LOCALEDIR.
647     char const *inkscape_localedir = g_getenv("INKSCAPE_LOCALEDIR");
648     if (inkscape_localedir != NULL) {
649         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
650     }
652     // common setup
653     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
654     textdomain(GETTEXT_PACKAGE);
655 #endif
657     set_extensions_env();
659     // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
660     Gtk::Main::init_gtkmm_internals();
662     LIBXML_TEST_VERSION
664     Inkscape::GC::init();
666     Inkscape::Debug::Logger::init();
668     gboolean use_gui;
670 #ifndef WIN32
671     use_gui = (g_getenv("DISPLAY") != NULL);
672 #else
673     use_gui = TRUE;
674 #endif
675     /* Test whether with/without GUI is forced */
676     for (int i = 1; i < argc; i++) {
677         if (!strcmp(argv[i], "-z")
678             || !strcmp(argv[i], "--without-gui")
679             || !strcmp(argv[i], "-p")
680             || !strncmp(argv[i], "--print", 7)
681             || !strcmp(argv[i], "-e")
682             || !strncmp(argv[i], "--export-png", 12)
683             || !strcmp(argv[i], "-l")
684             || !strncmp(argv[i], "--export-plain-svg", 18)
685             || !strcmp(argv[i], "-i")
686             || !strncmp(argv[i], "--export-area-drawing", 21)
687             || !strcmp(argv[i], "-D")
688             || !strncmp(argv[i], "--export-area-page", 18)
689             || !strcmp(argv[i], "-C")
690             || !strncmp(argv[i], "--export-id", 11)
691             || !strcmp(argv[i], "-P")
692             || !strncmp(argv[i], "--export-ps", 11)
693             || !strcmp(argv[i], "-E")
694             || !strncmp(argv[i], "--export-eps", 12)
695             || !strcmp(argv[i], "-A")
696             || !strncmp(argv[i], "--export-pdf", 12)
697             || !strncmp(argv[i], "--export-latex", 14)
698 #ifdef WIN32
699             || !strcmp(argv[i], "-M")
700             || !strncmp(argv[i], "--export-emf", 12)
701 #endif //WIN32
702             || !strcmp(argv[i], "-W")
703             || !strncmp(argv[i], "--query-width", 13)
704             || !strcmp(argv[i], "-H")
705             || !strncmp(argv[i], "--query-height", 14)
706             || !strcmp(argv[i], "-S")
707             || !strncmp(argv[i], "--query-all", 11)
708             || !strcmp(argv[i], "-X")
709             || !strncmp(argv[i], "--query-x", 9)
710             || !strcmp(argv[i], "-Y")
711             || !strncmp(argv[i], "--query-y", 9)
712             || !strcmp(argv[i], "--vacuum-defs")
713             || !strcmp(argv[i], "--shell")
714            )
715         {
716             /* main_console handles any exports -- not the gui */
717             use_gui = FALSE;
718             break;
719         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
720             use_gui = TRUE;
721             break;
722         }
723     }
725 #ifdef WIN32
726 #ifndef REPLACEARGS_ANSI
727     if ( PrintWin32::is_os_wide() )
728 #endif // REPLACEARGS_ANSI
729     {
730         // If the call fails, we'll need to convert charsets
731         needToRecodeParams = !replaceArgs( argc, argv );
732     }
733 #endif // WIN32
735     /// \todo  Should this be a static object (see inkscape.cpp)?
736     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
738     return app.run();
744 void fixupSingleFilename( gchar **orig, gchar **spare )
746     if ( orig && *orig && **orig ) {
747         GError *error = NULL;
748         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
749         if ( newFileName )
750         {
751             *orig = newFileName;
752             if ( spare ) {
753                 *spare = newFileName;
754             }
755 //             g_message("Set a replacement fixup");
756         }
757     }
762 GSList *fixupFilenameEncoding( GSList* fl )
764     GSList *newFl = NULL;
765     while ( fl ) {
766         gchar *fn = static_cast<gchar*>(fl->data);
767         fl = g_slist_remove( fl, fl->data );
768         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
769         if ( newFileName ) {
771             if ( 0 )
772             {
773                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
774                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
775                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
776                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
777                 gtk_dialog_run (GTK_DIALOG (w));
778                 gtk_widget_destroy (w);
779                 g_free(safeNewFn);
780                 g_free(safeFn);
781             }
783             g_free( fn );
784             fn = newFileName;
785             newFileName = 0;
786         }
787         else
788             if ( 0 )
789         {
790             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
791             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
792             gtk_dialog_run (GTK_DIALOG (w));
793             gtk_widget_destroy (w);
794             g_free(safeFn);
795         }
796         newFl = g_slist_append( newFl, fn );
797     }
798     return newFl;
801 int sp_common_main( int argc, char const **argv, GSList **flDest )
803     /// \todo fixme: Move these to some centralized location (Lauris)
804     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
805     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
808     // temporarily switch gettext encoding to locale, so that help messages can be output properly
809     gchar const *charset;
810     g_get_charset(&charset);
812     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
814     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
815     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
816     g_return_val_if_fail(ctx != NULL, 1);
818     /* Collect own arguments */
819     GSList *fl = sp_process_args(ctx);
820     poptFreeContext(ctx);
822     // now switch gettext back to UTF-8 (for GUI)
823     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
825     // Now let's see if the file list still holds up
826     if ( needToRecodeParams )
827     {
828         fl = fixupFilenameEncoding( fl );
829     }
831     // Check the globals for filename-fixup
832     if ( needToRecodeParams )
833     {
834         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
835         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
836         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
837     }
838     else
839     {
840         if ( sp_export_png )
841             sp_export_png_utf8 = g_strdup( sp_export_png );
842         if ( sp_export_svg )
843             sp_export_svg_utf8 = g_strdup( sp_export_svg );
844         if ( sp_global_printer )
845             sp_global_printer_utf8 = g_strdup( sp_global_printer );
846     }
848     // Return the list if wanted, else free it up.
849     if ( flDest ) {
850         *flDest = fl;
851         fl = 0;
852     } else {
853         while ( fl ) {
854             g_free( fl->data );
855             fl = g_slist_remove( fl, fl->data );
856         }
857     }
858     return 0;
861 static void
862 snooper(GdkEvent *event, gpointer /*data*/) {
863     if (inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
864     {
865         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
866         switch (event->type) {
867             case GDK_MOTION_NOTIFY:
868                 if(event->motion.state & mapping) {
869                     event->motion.state|=GDK_MOD1_MASK;
870                 }
871                 break;
872             case GDK_BUTTON_PRESS:
873                 if(event->button.state & mapping) {
874                     event->button.state|=GDK_MOD1_MASK;
875                 }
876                 break;
877              case GDK_KEY_PRESS:
878                  if(event->key.state & mapping) {
879                      event->key.state|=GDK_MOD1_MASK;
880                  }
881                  break;
882         default:
883             break;
884         }
885     }
887     if (inkscape_trackalt()) {
888         // MacOS X with X11 has some problem with the default
889         // xmodmapping.  A ~/.xmodmap solution does not work reliably due
890         // to the way we package our executable in a .app that can launch
891         // X11 or use an already-running X11.  The same problem has been
892         // reported on Linux but there is no .app/X11 to get in the way
893         // of ~/.xmodmap fixes.  So we make this a preference.
894         //
895         // For some reason, Gdk senses changes in Alt (Mod1) state for
896         // many message types, but not for keystrokes!  So this ugly hack
897         // tracks what the state of Alt-pressing is, and ensures
898         // GDK_MOD1_MASK is in the event->key.state as appropriate.
899         //
900         static gboolean altL_pressed = FALSE;
901         static gboolean altR_pressed = FALSE;
902         static gboolean alt_pressed = FALSE;
903         guint get_group0_keyval(GdkEventKey* event);
904         guint keyval = 0;
905         switch (event->type) {
906         case GDK_MOTION_NOTIFY:
907             alt_pressed = TRUE && (event->motion.state & GDK_MOD1_MASK);
908             break;
909         case GDK_BUTTON_PRESS:
910             alt_pressed = TRUE && (event->button.state & GDK_MOD1_MASK);
911             break;
912         case GDK_KEY_PRESS:
913             keyval = get_group0_keyval(&event->key);
914             if (keyval == GDK_Alt_L) altL_pressed = TRUE;
915             if (keyval == GDK_Alt_R) altR_pressed = TRUE;
916             alt_pressed = alt_pressed || altL_pressed || altR_pressed;
917             alt_pressed = alt_pressed || (event->button.state & GDK_MOD1_MASK);
918             if (alt_pressed)
919                 event->key.state |= GDK_MOD1_MASK;
920             else
921                 event->key.state &= ~GDK_MOD1_MASK;
922             break;
923         case GDK_KEY_RELEASE:
924             keyval = get_group0_keyval(&event->key);
925             if (keyval == GDK_Alt_L) altL_pressed = FALSE;
926             if (keyval == GDK_Alt_R) altR_pressed = FALSE;
927             if (!altL_pressed && !altR_pressed)
928                 alt_pressed = FALSE;
929             break;
930         default:
931             break;
932         }
933         //printf("alt_pressed: %s\n", alt_pressed? "+" : "-");
934     }
936     gtk_main_do_event (event);
939 static std::vector<Glib::ustring> getDirectorySet(const gchar* userDir, const gchar* const * systemDirs) {
940     std::vector<Glib::ustring> listing;
941     listing.push_back(userDir);
942     for ( const char* const* cur = systemDirs; *cur; cur++ )
943     {
944         listing.push_back(*cur);
945     }
946     return listing;
949 int
950 sp_main_gui(int argc, char const **argv)
952     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
954     GSList *fl = NULL;
955     int retVal = sp_common_main( argc, argv, &fl );
956     g_return_val_if_fail(retVal == 0, 1);
958     // Add possible icon entry directories
959     std::vector<Glib::ustring> dataDirs = getDirectorySet( g_get_user_data_dir(),
960                                                            g_get_system_data_dirs() );
961     for (std::vector<Glib::ustring>::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
962     {
963         std::vector<Glib::ustring> listing;
964         listing.push_back(*it);
965         listing.push_back("inkscape");
966         listing.push_back("icons");
967         Glib::ustring dir = Glib::build_filename(listing);
968         gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir.c_str());
969     }
971     // Add our icon directory to the search path for icon theme lookups.
972     gchar *usericondir = profile_path("icons");
973     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), usericondir);
974     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
975     g_free(usericondir);
977     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
978     Inkscape::Debug::log_display_config();
980     // Set default window icon. Obeys the theme.
981     gtk_window_set_default_icon_name("inkscape");
982     // Do things that were previously in inkscape_gtk_stock_init().
983     sp_icon_get_phys_size(GTK_ICON_SIZE_MENU);
984     Inkscape::UI::Widget::Panel::prep();
986     gboolean create_new = TRUE;
988     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
989     inkscape_application_init(argv[0], true);
991     while (fl) {
992         if (sp_file_open((gchar *)fl->data,NULL)) {
993             create_new=FALSE;
994         }
995         fl = g_slist_remove(fl, fl->data);
996     }
997     if (create_new) {
998         sp_file_new_default();
999     }
1001     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
1002     main_instance.run();
1004 #ifdef WIN32
1005     //We might not need anything here
1006     //sp_win32_finish(); <-- this is a NOP func
1007 #endif
1009     return 0;
1012 /**
1013  * Process file list
1014  */
1015 void sp_process_file_list(GSList *fl)
1017     while (fl) {
1018         const gchar *filename = (gchar *)fl->data;
1020         SPDocument *doc = NULL;
1021         try {
1022             doc = Inkscape::Extension::open(NULL, filename);
1023         } catch (Inkscape::Extension::Input::no_extension_found &e) {
1024             doc = NULL;
1025         } catch (Inkscape::Extension::Input::open_failed &e) {
1026             doc = NULL;
1027         }
1029         if (doc == NULL) {
1030             try {
1031                 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
1032             } catch (Inkscape::Extension::Input::no_extension_found &e) {
1033                 doc = NULL;
1034             } catch (Inkscape::Extension::Input::open_failed &e) {
1035                 doc = NULL;
1036             }
1037         }
1038         if (doc == NULL) {
1039             g_warning("Specified document %s cannot be opened (does not exist or not a valid SVG file)", filename);
1040         } else {
1041             if (sp_vacuum_defs) {
1042                 vacuum_document(doc);
1043             }
1044             if (sp_vacuum_defs && !sp_export_svg) {
1045                 // save under the name given in the command line
1046                 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
1047             }
1048             if (sp_global_printer) {
1049                 sp_print_document_to_file(doc, sp_global_printer);
1050             }
1051             if (sp_export_png || (sp_export_id && sp_export_use_hints)) {
1052                 sp_do_export_png(doc);
1053             }
1054             if (sp_export_svg) {
1055                 Inkscape::XML::Document *rdoc;
1056                 Inkscape::XML::Node *repr;
1057                 rdoc = sp_repr_document_new("svg:svg");
1058                 repr = rdoc->root();
1059                 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
1060                 sp_repr_save_rebased_file(repr->document(), sp_export_svg, SP_SVG_NS_URI,
1061                                           doc->base, sp_export_svg);
1062             }
1063             if (sp_export_ps) {
1064                 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
1065             }
1066             if (sp_export_eps) {
1067                 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
1068             }
1069             if (sp_export_pdf) {
1070                 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
1071             }
1072 #ifdef WIN32
1073             if (sp_export_emf) {
1074                 do_export_emf(doc, sp_export_emf, "image/x-emf");
1075             }
1076 #endif //WIN32
1077             if (sp_query_all) {
1078                 do_query_all (doc);
1079             } else if (sp_query_width || sp_query_height) {
1080                 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
1081             } else if (sp_query_x || sp_query_y) {
1082                 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
1083             }
1085             delete doc;
1086         }
1087         fl = g_slist_remove(fl, fl->data);
1088     }
1091 /**
1092  * Run the application as an interactive shell, parsing command lines from stdin
1093  * Returns -1 on error.
1094  */
1095 int sp_main_shell(char const* command_name)
1097     int retval = 0;
1099     const unsigned int buffer_size = 4096;
1100     gchar *command_line = g_strnfill(buffer_size, 0);
1101     g_strlcpy(command_line, command_name, buffer_size);
1102     gsize offset = g_strlcat(command_line, " ", buffer_size);
1103     gsize sizeLeft = buffer_size - offset;
1104     gchar *useme = command_line + offset;
1106     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
1107     fflush(stdout);
1108     char* linedata = 0;
1109     do {
1110         fprintf(stdout, ">");
1111         fflush(stdout);
1112         if ((linedata = fgets(useme, sizeLeft, stdin))) {
1113             size_t len = strlen(useme);
1114             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
1115                 fprintf(stdout, "ERROR: Command line too long\n");
1116                 // Consume rest of line
1117                 retval = -1; // If the while loop completes, this remains -1
1118                 while (fgets(useme, sizeLeft, stdin) && retval) {
1119                     len = strlen(command_line);
1120                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
1121                         retval = 0;
1122                     }
1123                 }
1124             } else {
1125                 useme[--len] = '\0';  // Strip newline
1126                 if (useme[len - 1] == '\r') {
1127                     useme[--len] = '\0';
1128                 }
1129                 if ( strcmp(useme, "quit") == 0 ) {
1130                     // Time to quit
1131                     fflush(stdout);
1132                     linedata = 0; // mark for exit
1133                 } else if ( len < 1 ) {
1134                     // blank string. Do nothing.
1135                 } else {
1136                     GError* parseError = 0;
1137                     gchar** argv = 0;
1138                     gint argc = 0;
1139                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
1140                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
1141                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
1142                         if ( ctx ) {
1143                             GSList *fl = sp_process_args(ctx);
1144                             sp_process_file_list(fl);
1145                             poptFreeContext(ctx);
1146                         } else {
1147                             retval = 1; // not sure why. But this was the previous return value
1148                         }
1149                         resetCommandlineGlobals();
1150                         g_strfreev(argv);
1151                     } else {
1152                         g_warning("Cannot parse commandline: %s", useme);
1153                     }
1154                 }
1155             }
1156         } // if (linedata...
1157     } while (linedata && (retval == 0));
1159     g_free(command_line);
1160     return retval;
1163 int sp_main_console(int argc, char const **argv)
1165     /* We are started in text mode */
1167     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1168      * in a non-Gtk environment.  Used in libnrtype's
1169      * FontInstance.cpp and FontFactory.cpp.
1170      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1171      */
1172     g_type_init();
1173     char **argv2 = const_cast<char **>(argv);
1174     gtk_init_check( &argc, &argv2 );
1175     //setlocale(LC_ALL, "");
1177     GSList *fl = NULL;
1178     int retVal = sp_common_main( argc, argv, &fl );
1179     g_return_val_if_fail(retVal == 0, 1);
1181     if (fl == NULL && !sp_shell) {
1182         g_print("Nothing to do!\n");
1183         exit(0);
1184     }
1186     inkscape_application_init(argv[0], false);
1188     if (sp_shell) {
1189         sp_main_shell(argv[0]); // Run as interactive shell
1190         exit(0);
1191     } else {
1192         sp_process_file_list(fl); // Normal command line invokation
1193     }
1195     return 0;
1198 static void
1199 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1201     SPObject *o = NULL;
1203     if (id) {
1204         o = doc->getObjectById(id);
1205         if (o) {
1206             if (!SP_IS_ITEM (o)) {
1207                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1208                 return;
1209             }
1210         } else {
1211             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1212             return;
1213         }
1214     } else {
1215         o = SP_DOCUMENT_ROOT(doc);
1216     }
1218     if (o) {
1219         sp_document_ensure_up_to_date (doc);
1220         SPItem *item = ((SPItem *) o);
1222         // "true" SVG bbox for scripting
1223         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1224         if (area) {
1225             Inkscape::SVGOStringStream os;
1226             if (extent) {
1227                 os << area->dimensions()[axis];
1228             } else {
1229                 os << area->min()[axis];
1230             }
1231             g_print ("%s", os.str().c_str());
1232         } else {
1233             g_print("0");
1234         }
1235     }
1238 static void
1239 do_query_all (SPDocument *doc)
1241     SPObject *o = NULL;
1243     o = SP_DOCUMENT_ROOT(doc);
1245     if (o) {
1246         sp_document_ensure_up_to_date (doc);
1247         do_query_all_recurse(o);
1248     }
1251 static void
1252 do_query_all_recurse (SPObject *o)
1254     SPItem *item = ((SPItem *) o);
1255     if (o->getId() && SP_IS_ITEM(item)) {
1256         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1257         if (area) {
1258             Inkscape::SVGOStringStream os;
1259             os << o->getId();
1260             os << "," << area->min()[Geom::X];
1261             os << "," << area->min()[Geom::Y];
1262             os << "," << area->dimensions()[Geom::X];
1263             os << "," << area->dimensions()[Geom::Y];
1264             g_print ("%s\n", os.str().c_str());
1265         }
1266     }
1268     SPObject *child = o->children;
1269     while (child) {
1270         do_query_all_recurse (child);
1271         child = child->next;
1272     }
1276 static void
1277 sp_do_export_png(SPDocument *doc)
1279     const gchar *filename = NULL;
1280     bool filename_from_hint = false;
1281     gdouble dpi = 0.0;
1283     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1284         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1285     }
1287     GSList *items = NULL;
1289     Geom::Rect area;
1290     if (sp_export_id || sp_export_area_drawing) {
1292         SPObject *o = NULL;
1293         SPObject *o_area = NULL;
1294         if (sp_export_id && sp_export_area_drawing) {
1295             o = doc->getObjectById(sp_export_id);
1296             o_area = SP_DOCUMENT_ROOT (doc);
1297         } else if (sp_export_id) {
1298             o = doc->getObjectById(sp_export_id);
1299             o_area = o;
1300         } else if (sp_export_area_drawing) {
1301             o = SP_DOCUMENT_ROOT (doc);
1302             o_area = o;
1303         }
1305         if (o) {
1306             if (!SP_IS_ITEM (o)) {
1307                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1308                 return;
1309             }
1311             items = g_slist_prepend (items, SP_ITEM(o));
1313             if (sp_export_id_only) {
1314                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1315             }
1317             if (sp_export_use_hints) {
1319                 // retrieve export filename hint
1320                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1321                 if (fn_hint) {
1322                     if (sp_export_png) {
1323                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1324                         filename = sp_export_png;
1325                     } else {
1326                         filename = fn_hint;
1327                         filename_from_hint = true;
1328                     }
1329                 } else {
1330                     g_warning ("Export filename hint not found for the object.");
1331                     filename = sp_export_png;
1332                 }
1334                 // retrieve export dpi hints
1335                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1336                 if (dpi_hint) {
1337                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1338                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1339                     } else {
1340                         dpi = atof(dpi_hint);
1341                     }
1342                 } else {
1343                     g_warning ("Export DPI hint not found for the object.");
1344                 }
1346             }
1348             // write object bbox to area
1349             sp_document_ensure_up_to_date (doc);
1350             Geom::OptRect areaMaybe;
1351             sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1352             if (areaMaybe) {
1353                 area = *areaMaybe;
1354             } else {
1355                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1356                 return;
1357             }
1358         } else {
1359             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1360             return;
1361         }
1362     }
1364     if (sp_export_area) {
1365         /* Try to parse area (given in SVG pixels) */
1366         gdouble x0,y0,x1,y1;
1367         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1368             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1369             return;
1370         }
1371         area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1372     } else if (sp_export_area_page || !(sp_export_id || sp_export_area_drawing)) {
1373         /* Export the whole page: note: Inkscape uses 'page' in all menus and dialogs, not 'canvas' */
1374         sp_document_ensure_up_to_date (doc);
1375         Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1376         area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1377     }
1379     // set filename and dpi from options, if not yet set from the hints
1380     if (!filename) {
1381         if (!sp_export_png) {
1382             g_warning ("No export filename given and no filename hint. Nothing exported.");
1383             return;
1384         }
1385         filename = sp_export_png;
1386     }
1388     if (sp_export_dpi && dpi == 0.0) {
1389         dpi = atof(sp_export_dpi);
1390         if ((dpi < 0.1) || (dpi > 10000.0)) {
1391             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1392             return;
1393         }
1394         g_print("DPI: %g\n", dpi);
1395     }
1397     if (sp_export_area_snap) {
1398         round_rectangle_outwards(area);
1399     }
1401     // default dpi
1402     if (dpi == 0.0) {
1403         dpi = PX_PER_IN;
1404     }
1406     unsigned long int width = 0;
1407     unsigned long int height = 0;
1409     if (sp_export_width) {
1410         errno=0;
1411         width = strtoul(sp_export_width, NULL, 0);
1412         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1413             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1414             return;
1415         }
1416         dpi = (gdouble) width * PX_PER_IN / area.width();
1417     }
1419     if (sp_export_height) {
1420         errno=0;
1421         height = strtoul(sp_export_height, NULL, 0);
1422         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1423             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1424             return;
1425         }
1426         dpi = (gdouble) height * PX_PER_IN / area.height();
1427     }
1429     if (!sp_export_width) {
1430         width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1431     }
1433     if (!sp_export_height) {
1434         height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1435     }
1437     guint32 bgcolor = 0x00000000;
1438     if (sp_export_background) {
1439         // override the page color
1440         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1441         bgcolor |= 0xff; // default is no opacity
1442     } else {
1443         // read from namedview
1444         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1445         if (nv && nv->attribute("pagecolor"))
1446             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1447         if (nv && nv->attribute("inkscape:pageopacity"))
1448             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1449     }
1451     if (sp_export_background_opacity) {
1452         // override opacity
1453         gfloat value;
1454         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1455             if (value > 1.0) {
1456                 value = CLAMP (value, 1.0f, 255.0f);
1457                 bgcolor &= (guint32) 0xffffff00;
1458                 bgcolor |= (guint32) floor(value);
1459             } else {
1460                 value = CLAMP (value, 0.0f, 1.0f);
1461                 bgcolor &= (guint32) 0xffffff00;
1462                 bgcolor |= SP_COLOR_F_TO_U(value);
1463             }
1464         }
1465     }
1467     gchar *path = 0;
1468     if (filename_from_hint) {
1469         //Make relative paths go from the document location, if possible:
1470         if (!g_path_is_absolute(filename) && doc->uri) {
1471             gchar *dirname = g_path_get_dirname(doc->uri);
1472             if (dirname) {
1473                 path = g_build_filename(dirname, filename, NULL);
1474                 g_free(dirname);
1475             }
1476         }
1477         if (!path) {
1478             path = g_strdup(filename);
1479         }
1480     } else {
1481         path = g_strdup(filename);
1482     }
1484     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1486     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);
1488     g_print("Bitmap saved as: %s\n", filename);
1490     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1491         sp_export_png_file(doc, path, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1492     } else {
1493         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1494     }
1496     g_free (path);
1497     g_slist_free (items);
1501 /**
1502  *  Perform a PDF/PS/EPS export
1503  *
1504  *  \param doc Document to export.
1505  *  \param uri URI to export to.
1506  *  \param mime MIME type to export as.
1507  */
1509 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1511     Inkscape::Extension::DB::OutputList o;
1512     Inkscape::Extension::db.get_output_list(o);
1513     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1514     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1515         i++;
1516     }
1518     if (i == o.end())
1519     {
1520         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1521         return;
1522     }
1524     if (sp_export_id) {
1525         SPObject *o = doc->getObjectById(sp_export_id);
1526         if (o == NULL) {
1527             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1528             return;
1529         }
1530         (*i)->set_param_string ("exportId", sp_export_id);
1531     } else {
1532         (*i)->set_param_string ("exportId", "");
1533     }
1535     if (sp_export_area_page && sp_export_area_drawing) {
1536         g_warning ("You cannot use --export-area-page and --export-area-drawing at the same time; only the former will take effect.");
1537         sp_export_area_drawing = false;
1538     }
1540     if (sp_export_area_drawing) {
1541         (*i)->set_param_bool ("areaDrawing", TRUE);
1542     } else {
1543         (*i)->set_param_bool ("areaDrawing", FALSE);
1544     }
1546     if (sp_export_area_page) {
1547         if (sp_export_eps) {
1548             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.");
1549         }
1550         (*i)->set_param_bool ("areaPage", TRUE);
1551     } else {
1552         (*i)->set_param_bool ("areaPage", FALSE);
1553     }
1555     if (!sp_export_area_drawing && !sp_export_area_page && !sp_export_id) {
1556         // neither is set, set page as default for ps/pdf and drawing for eps
1557         if (sp_export_eps) {
1558             try {
1559                (*i)->set_param_bool("areaDrawing", TRUE);
1560             } catch (...) {}
1561         }
1562     }
1564     if (sp_export_text_to_path) {
1565         (*i)->set_param_bool("textToPath", TRUE);
1566     } else {
1567         (*i)->set_param_bool("textToPath", FALSE);
1568     }
1570     if (sp_export_latex) {
1571         (*i)->set_param_bool("textToLaTeX", TRUE);
1572     } else {
1573         (*i)->set_param_bool("textToLaTeX", FALSE);
1574     }
1576     if (sp_export_ignore_filters) {
1577         (*i)->set_param_bool("blurToBitmap", FALSE);
1578     } else {
1579         (*i)->set_param_bool("blurToBitmap", TRUE);
1581         gdouble dpi = 90.0;
1582         if (sp_export_dpi) {
1583             dpi = atof(sp_export_dpi);
1584             if ((dpi < 1) || (dpi > 10000.0)) {
1585                 g_warning("DPI value %s out of range [1 - 10000]. Using 90 dpi instead.", sp_export_dpi);
1586                 dpi = 90;
1587             }
1588         }
1590         (*i)->set_param_int("resolution", (int) dpi);
1591     }
1593     (*i)->save(doc, uri);
1596 #ifdef WIN32
1597 /**
1598  *  Export a document to EMF
1599  *
1600  *  \param doc Document to export.
1601  *  \param uri URI to export to.
1602  *  \param mime MIME type to export as (should be "image/x-emf")
1603  */
1605 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1607     Inkscape::Extension::DB::OutputList o;
1608     Inkscape::Extension::db.get_output_list(o);
1609     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1610     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1611         i++;
1612     }
1614     if (i == o.end())
1615     {
1616         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1617         return;
1618     }
1620     (*i)->save(doc, uri);
1622 #endif //WIN32
1624 #ifdef WIN32
1625 bool replaceArgs( int& argc, char**& argv )
1627     bool worked = false;
1629 #ifdef REPLACEARGS_DEBUG
1630     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1631 #endif // REPLACEARGS_DEBUG
1633     wchar_t* line = GetCommandLineW();
1634     if ( line )
1635     {
1636 #ifdef REPLACEARGS_DEBUG
1637         {
1638             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1639             if ( utf8Line )
1640             {
1641                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1642                 {
1643                     char tmp[strlen(safe) + 32];
1644                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1645                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1646                 }
1647             }
1648         }
1649 #endif // REPLACEARGS_DEBUG
1651         int numArgs = 0;
1652         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1654 #ifdef REPLACEARGS_ANSI
1655 // test code for trying things on Win95/98/ME
1656         if ( !parsed )
1657         {
1658 #ifdef REPLACEARGS_DEBUG
1659             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1660 #endif // REPLACEARGS_DEBUG
1661             int lineLen = wcslen(line) + 1;
1662             wchar_t* lineDup = new wchar_t[lineLen];
1663             wcsncpy( lineDup, line, lineLen );
1665             int pos = 0;
1666             bool inQuotes = false;
1667             bool inWhitespace = true;
1668             std::vector<int> places;
1669             while ( lineDup[pos] )
1670             {
1671                 if ( inQuotes )
1672                 {
1673                     if ( lineDup[pos] == L'"' )
1674                     {
1675                         inQuotes = false;
1676                     }
1677                 }
1678                 else if ( lineDup[pos] == L'"' )
1679                 {
1680                     inQuotes = true;
1681                     inWhitespace = false;
1682                     places.push_back(pos);
1683                 }
1684                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1685                 {
1686                     if ( !inWhitespace )
1687                     {
1688                         inWhitespace = true;
1689                         lineDup[pos] = 0;
1690                     }
1691                 }
1692                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1693                 {
1694                     inWhitespace = false;
1695                     places.push_back(pos);
1696                 }
1697                 else
1698                 {
1699                     // consume
1700                 }
1701                 pos++;
1702             }
1703 #ifdef REPLACEARGS_DEBUG
1704             {
1705                 char tmp[256];
1706                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1707                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1708             }
1709 #endif // REPLACEARGS_DEBUG
1711             wchar_t** block = new wchar_t*[places.size()];
1712             int i = 0;
1713             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1714             {
1715                 block[i++] = &lineDup[*it];
1716             }
1717             parsed = block;
1718             numArgs = places.size();
1719         }
1720 #endif // REPLACEARGS_ANSI
1722         if ( parsed )
1723         {
1724             std::vector<wchar_t*>expandedArgs;
1725             if ( numArgs > 0 )
1726             {
1727                 expandedArgs.push_back( parsed[0] );
1728             }
1730             for ( int i1 = 1; i1 < numArgs; i1++ )
1731             {
1732                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1733                 wildcarded &= parsed[i1][0] != L'"';
1734                 wildcarded &= parsed[i1][0] != L'-';
1735                 if ( wildcarded )
1736                 {
1737 #ifdef REPLACEARGS_ANSI
1738                     WIN32_FIND_DATAA data;
1739 #else
1740                     WIN32_FIND_DATAW data;
1741 #endif // REPLACEARGS_ANSI
1743                     memset((void *)&data, 0, sizeof(data));
1745                     int baseLen = wcslen(parsed[i1]) + 2;
1746                     wchar_t* base = new wchar_t[baseLen];
1747                     wcsncpy( base, parsed[i1], baseLen );
1748                     wchar_t* last = wcsrchr( base, L'\\' );
1749                     if ( last )
1750                     {
1751                         last[1] = 0;
1752                     }
1753                     else
1754                     {
1755                         base[0] = 0;
1756                     }
1757                     baseLen = wcslen( base );
1759 #ifdef REPLACEARGS_ANSI
1760                     char target[MAX_PATH];
1761                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1762                     {
1763                         HANDLE hf = FindFirstFileA( target, &data );
1764 #else
1765                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1766 #endif // REPLACEARGS_ANSI
1767                         if ( hf != INVALID_HANDLE_VALUE )
1768                         {
1769                             BOOL found = TRUE;
1770                             do
1771                             {
1772 #ifdef REPLACEARGS_ANSI
1773                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1774                                 if ( howMany > 0 )
1775                                 {
1776                                     howMany += baseLen;
1777                                     wchar_t* tmp = new wchar_t[howMany + 1];
1778                                     wcsncpy( tmp, base, howMany + 1 );
1779                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1780                                     expandedArgs.push_back( tmp );
1781                                     found = FindNextFileA( hf, &data );
1782                                 }
1783 #else
1784                                 int howMany = wcslen(data.cFileName) + baseLen;
1785                                 wchar_t* tmp = new wchar_t[howMany + 1];
1786                                 wcsncpy( tmp, base, howMany + 1 );
1787                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1788                                 expandedArgs.push_back( tmp );
1789                                 found = FindNextFileW( hf, &data );
1790 #endif // REPLACEARGS_ANSI
1791                             } while ( found );
1793                             FindClose( hf );
1794                         }
1795                         else
1796                         {
1797                             expandedArgs.push_back( parsed[i1] );
1798                         }
1799 #ifdef REPLACEARGS_ANSI
1800                     }
1801 #endif // REPLACEARGS_ANSI
1803                     delete[] base;
1804                 }
1805                 else
1806                 {
1807                     expandedArgs.push_back( parsed[i1] );
1808                 }
1809             }
1811             {
1812                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1813                 int iz = 0;
1814                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1815                 {
1816                     block[iz++] = *it;
1817                 }
1818                 parsed = block;
1819                 numArgs = expandedArgs.size();
1820             }
1822             std::vector<gchar*> newArgs;
1823             for ( int i = 0; i < numArgs; i++ )
1824             {
1825                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1826                 if ( replacement )
1827                 {
1828 #ifdef REPLACEARGS_DEBUG
1829                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1831                     if ( safe2 )
1832                     {
1833                         {
1834                             char tmp[1024];
1835                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1836                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1837                         }
1838                         g_free( safe2 );
1839                     }
1840 #endif // REPLACEARGS_DEBUG
1842                     newArgs.push_back( replacement );
1843                 }
1844                 else
1845                 {
1846                     newArgs.push_back( blankParam );
1847                 }
1848             }
1850             // Now push our munged params to be the new argv and argc
1851             {
1852                 char** block = new char*[newArgs.size()];
1853                 int iz = 0;
1854                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1855                 {
1856                     block[iz++] = *it;
1857                 }
1858                 argv = block;
1859                 argc = newArgs.size();
1860                 worked = true;
1861             }
1862         }
1863 #ifdef REPLACEARGS_DEBUG
1864         else
1865         {
1866             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1867         }
1868 #endif // REPLACEARGS_DEBUG
1869     }
1870 #ifdef REPLACEARGS_DEBUG
1871     else
1872     {
1873         {
1874             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1875         }
1877         char* line2 = GetCommandLineA();
1878         if ( line2 )
1879         {
1880             gchar *safe = Inkscape::IO::sanitizeString(line2);
1881             {
1882                 {
1883                     char tmp[strlen(safe) + 32];
1884                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1885                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1886                 }
1887             }
1888         }
1889         else
1890         {
1891             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1892         }
1893     }
1894 #endif // REPLACEARGS_DEBUG
1896     return worked;
1898 #endif // WIN32
1900 static GSList *
1901 sp_process_args(poptContext ctx)
1903     GSList *fl = NULL;
1905     gint a;
1906     while ((a = poptGetNextOpt(ctx)) != -1) {
1907         switch (a) {
1908             case SP_ARG_FILE: {
1909                 gchar const *fn = poptGetOptArg(ctx);
1910                 if (fn != NULL) {
1911                     fl = g_slist_append(fl, g_strdup(fn));
1912                 }
1913                 break;
1914             }
1915             case SP_ARG_VERSION: {
1916                 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1917                 exit(0);
1918                 break;
1919             }
1920             case SP_ARG_EXTENSIONDIR: {
1921                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1922                 exit(0);
1923                 break;
1924             }
1925             case SP_ARG_VERB_LIST: {
1926                 // This really shouldn't go here, we should init the app.
1927                 // But, since we're just exiting in this path, there is
1928                 // no harm, and this is really a better place to put
1929                 // everything else.
1930                 Inkscape::Extension::init();
1931                 Inkscape::Verb::list();
1932                 exit(0);
1933                 break;
1934             }
1935             case SP_ARG_VERB:
1936             case SP_ARG_SELECT: {
1937                 gchar const *arg = poptGetOptArg(ctx);
1938                 if (arg != NULL) {
1939                     // printf("Adding in: %s\n", arg);
1940                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1941                 }
1942                 break;
1943             }
1944             case POPT_ERROR_BADOPT: {
1945                 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1946                 exit(1);
1947                 break;
1948             }
1949             default: {
1950                 break;
1951             }
1952         }
1953     }
1955     gchar const ** const args = poptGetArgs(ctx);
1956     if (args != NULL) {
1957         for (unsigned i = 0; args[i] != NULL; i++) {
1958             fl = g_slist_append(fl, g_strdup(args[i]));
1959         }
1960     }
1962     return fl;
1966 /*
1967   Local Variables:
1968   mode:c++
1969   c-file-style:"stroustrup"
1970   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1971   indent-tabs-mode:nil
1972   fill-column:99
1973   End:
1974 */
1975 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :