Code

Merge and cleanup of GSoC C++-ification project.
[inkscape.git] / src / main.cpp
1 /** \file
2  * Inkscape - an ambitious vector drawing program
3  *
4  * Authors:
5  *   Lauris Kaplinski <lauris@kaplinski.com>
6  *   Frank Felfe <innerspace@iname.com>
7  *   Davide Puricelli <evo@debian.org>
8  *   Mitsuru Oka <oka326@parkcity.ne.jp>
9  *   Masatake YAMATO  <jet@gyve.org>
10  *   F.J.Franklin <F.J.Franklin@sheffield.ac.uk>
11  *   Michael Meeks <michael@helixcode.com>
12  *   Chema Celorio <chema@celorio.com>
13  *   Pawel Palucha
14  *   Bryce Harrington <bryce@bryceharrington.com>
15  * ... and various people who have worked with various projects
16  *   Jon A. Cruz <jon@oncruz.org>
17  *   Abhishek Sharma
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 "main-cmdlineact.h"
118 #include "widgets/icon.h"
119 #include "ui/widget/panel.h"
121 #include <errno.h>
123 enum {
124     SP_ARG_NONE,
125     SP_ARG_NOGUI,
126     SP_ARG_GUI,
127     SP_ARG_FILE,
128     SP_ARG_PRINT,
129     SP_ARG_EXPORT_PNG,
130     SP_ARG_EXPORT_DPI,
131     SP_ARG_EXPORT_AREA,
132     SP_ARG_EXPORT_AREA_DRAWING,
133     SP_ARG_EXPORT_AREA_PAGE,
134     SP_ARG_EXPORT_AREA_SNAP,
135     SP_ARG_EXPORT_WIDTH,
136     SP_ARG_EXPORT_HEIGHT,
137     SP_ARG_EXPORT_ID,
138     SP_ARG_EXPORT_ID_ONLY,
139     SP_ARG_EXPORT_USE_HINTS,
140     SP_ARG_EXPORT_BACKGROUND,
141     SP_ARG_EXPORT_BACKGROUND_OPACITY,
142     SP_ARG_EXPORT_SVG,
143     SP_ARG_EXPORT_PS,
144     SP_ARG_EXPORT_EPS,
145     SP_ARG_EXPORT_PDF,
146     SP_ARG_EXPORT_LATEX,
147 #ifdef WIN32
148     SP_ARG_EXPORT_EMF,
149 #endif //WIN32
150     SP_ARG_EXPORT_TEXT_TO_PATH,
151     SP_ARG_EXPORT_IGNORE_FILTERS,
152     SP_ARG_EXTENSIONDIR,
153     SP_ARG_QUERY_X,
154     SP_ARG_QUERY_Y,
155     SP_ARG_QUERY_WIDTH,
156     SP_ARG_QUERY_HEIGHT,
157     SP_ARG_QUERY_ALL,
158     SP_ARG_QUERY_ID,
159     SP_ARG_SHELL,
160     SP_ARG_VERSION,
161     SP_ARG_VACUUM_DEFS,
162     SP_ARG_VERB_LIST,
163     SP_ARG_VERB,
164     SP_ARG_SELECT,
165     SP_ARG_LAST
166 };
168 int sp_main_gui(int argc, char const **argv);
169 int sp_main_console(int argc, char const **argv);
170 static void sp_do_export_png(SPDocument *doc);
171 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime);
172 #ifdef WIN32
173 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
174 #endif //WIN32
175 static void do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id);
176 static void do_query_all (SPDocument *doc);
177 static void do_query_all_recurse (SPObject *o);
179 static gchar *sp_global_printer = NULL;
180 static gchar *sp_export_png = NULL;
181 static gchar *sp_export_dpi = NULL;
182 static gchar *sp_export_area = NULL;
183 static gboolean sp_export_area_drawing = FALSE;
184 static gboolean sp_export_area_page = FALSE;
185 static gboolean sp_export_latex = FALSE;
186 static gchar *sp_export_width = NULL;
187 static gchar *sp_export_height = NULL;
188 static gchar *sp_export_id = NULL;
189 static gchar *sp_export_background = NULL;
190 static gchar *sp_export_background_opacity = NULL;
191 static gboolean sp_export_area_snap = FALSE;
192 static gboolean sp_export_use_hints = FALSE;
193 static gboolean sp_export_id_only = FALSE;
194 static gchar *sp_export_svg = NULL;
195 static gchar *sp_export_ps = NULL;
196 static gchar *sp_export_eps = NULL;
197 static gchar *sp_export_pdf = NULL;
198 #ifdef WIN32
199 static gchar *sp_export_emf = NULL;
200 #endif //WIN32
201 static gboolean sp_export_text_to_path = FALSE;
202 static gboolean sp_export_ignore_filters = FALSE;
203 static gboolean sp_export_font = FALSE;
204 static gboolean sp_query_x = FALSE;
205 static gboolean sp_query_y = FALSE;
206 static gboolean sp_query_width = FALSE;
207 static gboolean sp_query_height = FALSE;
208 static gboolean sp_query_all = FALSE;
209 static gchar *sp_query_id = NULL;
210 static gboolean sp_shell = FALSE;
211 static gboolean sp_vacuum_defs = FALSE;
213 static gchar *sp_export_png_utf8 = NULL;
214 static gchar *sp_export_svg_utf8 = NULL;
215 static gchar *sp_global_printer_utf8 = NULL;
218 /**
219  *  Reset variables to default values.
220  */
221 static void resetCommandlineGlobals() {
222         sp_global_printer = NULL;
223         sp_export_png = NULL;
224         sp_export_dpi = NULL;
225         sp_export_area = NULL;
226         sp_export_area_drawing = FALSE;
227         sp_export_area_page = FALSE;
228         sp_export_latex = FALSE;
229         sp_export_width = NULL;
230         sp_export_height = NULL;
231         sp_export_id = NULL;
232         sp_export_background = NULL;
233         sp_export_background_opacity = NULL;
234         sp_export_area_snap = FALSE;
235         sp_export_use_hints = FALSE;
236         sp_export_id_only = FALSE;
237         sp_export_svg = NULL;
238         sp_export_ps = NULL;
239         sp_export_eps = NULL;
240         sp_export_pdf = NULL;
241 #ifdef WIN32
242         sp_export_emf = NULL;
243 #endif //WIN32
244         sp_export_text_to_path = FALSE;
245         sp_export_ignore_filters = FALSE;
246         sp_export_font = FALSE;
247         sp_query_x = FALSE;
248         sp_query_y = FALSE;
249         sp_query_width = FALSE;
250         sp_query_height = FALSE;
251         sp_query_all = FALSE;
252         sp_query_id = NULL;
253         sp_vacuum_defs = FALSE;
255         sp_export_png_utf8 = NULL;
256         sp_export_svg_utf8 = NULL;
257         sp_global_printer_utf8 = NULL;
260 #ifdef WIN32
261 static bool replaceArgs( int& argc, char**& argv );
262 #endif
263 static GSList *sp_process_args(poptContext ctx);
264 struct poptOption options[] = {
265     {"version", 'V',
266      POPT_ARG_NONE, NULL, SP_ARG_VERSION,
267      N_("Print the Inkscape version number"),
268      NULL},
270     {"without-gui", 'z',
271      POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
272      N_("Do not use X server (only process files from console)"),
273      NULL},
275     {"with-gui", 'g',
276      POPT_ARG_NONE, NULL, SP_ARG_GUI,
277      N_("Try to use X server (even if $DISPLAY is not set)"),
278      NULL},
280     {"file", 'f',
281      POPT_ARG_STRING, NULL, SP_ARG_FILE,
282      N_("Open specified document(s) (option string may be excluded)"),
283      N_("FILENAME")},
285     {"print", 'p',
286      POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
287      N_("Print document(s) to specified output file (use '| program' for pipe)"),
288      N_("FILENAME")},
290     {"export-png", 'e',
291      POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
292      N_("Export document to a PNG file"),
293      N_("FILENAME")},
295     {"export-dpi", 'd',
296      POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
297      N_("Resolution for exporting to bitmap and for rasterization of filters in PS/EPS/PDF (default 90)"),
298      N_("DPI")},
300     {"export-area", 'a',
301      POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
302      N_("Exported area in SVG user units (default is the page; 0,0 is lower-left corner)"),
303      N_("x0:y0:x1:y1")},
305     {"export-area-drawing", 'D',
306      POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
307      N_("Exported area is the entire drawing (not page)"),
308      NULL},
310     {"export-area-page", 'C',
311      POPT_ARG_NONE, &sp_export_area_page, SP_ARG_EXPORT_AREA_PAGE,
312      N_("Exported area is the entire page"),
313      NULL},
315     {"export-area-snap", 0,
316      POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
317      N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
318      NULL},
320     {"export-width", 'w',
321      POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
322      N_("The width of exported bitmap in pixels (overrides export-dpi)"),
323      N_("WIDTH")},
325     {"export-height", 'h',
326      POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
327      N_("The height of exported bitmap in pixels (overrides export-dpi)"),
328      N_("HEIGHT")},
330     {"export-id", 'i',
331      POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
332      N_("The ID of the object to export"),
333      N_("ID")},
335     {"export-id-only", 'j',
336      POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
337      // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
338      //  See "man inkscape" for details.
339      N_("Export just the object with export-id, hide all others (only with export-id)"),
340      NULL},
342     {"export-use-hints", 't',
343      POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
344      N_("Use stored filename and DPI hints when exporting (only with export-id)"),
345      NULL},
347     {"export-background", 'b',
348      POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
349      N_("Background color of exported bitmap (any SVG-supported color string)"),
350      N_("COLOR")},
352     {"export-background-opacity", 'y',
353      POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
354      N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
355      N_("VALUE")},
357     {"export-plain-svg", 'l',
358      POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
359      N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
360      N_("FILENAME")},
362     {"export-ps", 'P',
363      POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
364      N_("Export document to a PS file"),
365      N_("FILENAME")},
367     {"export-eps", 'E',
368      POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
369      N_("Export document to an EPS file"),
370      N_("FILENAME")},
372     {"export-pdf", 'A',
373      POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
374      N_("Export document to a PDF file"),
375      N_("FILENAME")},
377     {"export-latex", 0,
378      POPT_ARG_NONE, &sp_export_latex, SP_ARG_EXPORT_LATEX,
379      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}"),
380      NULL},
382 #ifdef WIN32
383     {"export-emf", 'M',
384      POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
385      N_("Export document to an Enhanced Metafile (EMF) File"),
386      N_("FILENAME")},
387 #endif //WIN32
389     {"export-text-to-path", 'T',
390      POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
391      N_("Convert text object to paths on export (PS, EPS, PDF)"),
392      NULL},
394     {"export-ignore-filters", 0,
395      POPT_ARG_NONE, &sp_export_ignore_filters, SP_ARG_EXPORT_IGNORE_FILTERS,
396      N_("Render filtered objects without filters, instead of rasterizing (PS, EPS, PDF)"),
397      NULL},
399     {"query-x", 'X',
400      POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
401      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
402      N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
403      NULL},
405     {"query-y", 'Y',
406      POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
407      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
408      N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
409      NULL},
411     {"query-width", 'W',
412      POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
413      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
414      N_("Query the width of the drawing or, if specified, of the object with --query-id"),
415      NULL},
417     {"query-height", 'H',
418      POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
419      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
420      N_("Query the height of the drawing or, if specified, of the object with --query-id"),
421      NULL},
423     {"query-all", 'S',
424      POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
425      N_("List id,x,y,w,h for all objects"),
426      NULL},
428     {"query-id", 'I',
429      POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
430      N_("The ID of the object whose dimensions are queried"),
431      N_("ID")},
433     {"extension-directory", 'x',
434      POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
435      // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
436      N_("Print out the extension directory and exit"),
437      NULL},
439     {"vacuum-defs", 0,
440      POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
441      N_("Remove unused definitions from the defs section(s) of the document"),
442      NULL},
444     {"verb-list", 0,
445      POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
446      N_("List the IDs of all the verbs in Inkscape"),
447      NULL},
449     {"verb", 0,
450      POPT_ARG_STRING, NULL, SP_ARG_VERB,
451      N_("Verb to call when Inkscape opens."),
452      N_("VERB-ID")},
454     {"select", 0,
455      POPT_ARG_STRING, NULL, SP_ARG_SELECT,
456      N_("Object ID to select when Inkscape opens."),
457      N_("OBJECT-ID")},
459     {"shell", 0,
460      POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
461      N_("Start Inkscape in interactive shell mode."),
462      NULL},
464     POPT_AUTOHELP POPT_TABLEEND
465 };
467 static bool needToRecodeParams = true;
468 gchar * blankParam = g_strdup("");
472 #ifdef WIN32
474 /**
475  * Set up the PATH and PYTHONPATH environment variables on Windows
476  * @param exe Inkscape executable directory in UTF-8
477  */
478 static void _win32_set_inkscape_env(gchar const *exe)
480     gchar const *path = g_getenv("PATH");
481     gchar const *pythonpath = g_getenv("PYTHONPATH");
483     gchar *python = g_build_filename(exe, "python", NULL);
484     gchar *scripts = g_build_filename(exe, "python", "Scripts", NULL);
485     gchar *perl = g_build_filename(exe, "python", NULL);
486     gchar *pythonlib = g_build_filename(exe, "python", "Lib", NULL);
487     gchar *pythondll = g_build_filename(exe, "python", "DLLs", NULL);
488     
489     // Python 2.x needs short paths in PYTHONPATH.
490     // Otherwise it doesn't work when Inkscape is installed in Unicode directories.
491     // g_win32_locale_filename_from_utf8 is the GLib wrapper for GetShortPathName.
492     // Remove this once we move to Python 3.0.
493     gchar *python_s = g_win32_locale_filename_from_utf8(python);
494     gchar *pythonlib_s = g_win32_locale_filename_from_utf8(pythonlib);
495     gchar *pythondll_s = g_win32_locale_filename_from_utf8(pythondll);
497     gchar *new_path;
498     gchar *new_pythonpath;
499     if (path) {
500         new_path = g_strdup_printf("%s;%s;%s;%s;%s", exe, python, scripts, perl, path);
501     } else {
502         new_path = g_strdup_printf("%s;%s;%s;%s", exe, python, scripts, perl);
503     }
504     if (pythonpath) {
505         new_pythonpath = g_strdup_printf("%s;%s;%s;%s",
506              python_s, pythonlib_s, pythondll_s, pythonpath);
507     } else {
508         new_pythonpath = g_strdup_printf("%s;%s;%s",
509             python_s, pythonlib_s, pythondll_s);
510     }
512     g_setenv("PATH", new_path, TRUE);
513     g_setenv("PYTHONPATH", new_pythonpath, TRUE);
515     /*
516     printf("PATH = %s\n\n", g_getenv("PATH"));
517     printf("PYTHONPATH = %s\n\n", g_getenv("PYTHONPATH"));
519     gchar *p = g_find_program_in_path("python");
520     if (p) {
521         printf("python in %s\n\n", p);
522         g_free(p);
523     } else {
524         printf("python not found\n\n");
525     }*/
527     g_free(python);
528     g_free(scripts);
529     g_free(perl);
530     g_free(pythonlib);
531     g_free(pythondll);
532     
533     g_free(python_s);
534     g_free(pythonlib_s);
535     g_free(pythondll_s);
537     g_free(new_path);
538     g_free(new_pythonpath);
540 #endif
542 static void set_extensions_env()
544     gchar const *pythonpath = g_getenv("PYTHONPATH");
545     gchar *extdir;
546     gchar *new_pythonpath;
547     
548 #ifdef WIN32
549     extdir = g_win32_locale_filename_from_utf8(INKSCAPE_EXTENSIONDIR);
550 #else
551     extdir = g_strdup(INKSCAPE_EXTENSIONDIR);
552 #endif
554     // On some platforms, INKSCAPE_EXTENSIONDIR is not absolute,
555     // but relative to the directory that contains the Inkscape executable.
556     // Since we spawn Python chdir'ed into the script's directory,
557     // we need to obtain the absolute path here.
558     if (!g_path_is_absolute(extdir)) {
559         gchar *curdir = g_get_current_dir();
560         gchar *extdir_new = g_build_filename(curdir, extdir, NULL);
561         g_free(extdir);
562         g_free(curdir);
563         extdir = extdir_new;
564     }
566     if (pythonpath) {
567         new_pythonpath = g_strdup_printf("%s" G_SEARCHPATH_SEPARATOR_S "%s",
568                                          extdir, pythonpath);
569         g_free(extdir);
570     } else {
571         new_pythonpath = extdir;
572     }
574     g_setenv("PYTHONPATH", new_pythonpath, TRUE);
575     g_free(new_pythonpath);
576     //printf("PYTHONPATH = %s\n", g_getenv("PYTHONPATH"));
579 /**
580  * This is the classic main() entry point of the program, though on some
581  * architectures it might be called by something else.
582  */
583 int
584 main(int argc, char **argv)
586 #ifdef HAVE_FPSETMASK
587     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
588        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
589        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
590     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
591 #endif
593 #ifdef WIN32
594     gchar *exedir = g_strdup(win32_getExePath().data());
595     _win32_set_inkscape_env(exedir);
597 # ifdef ENABLE_NLS
598     // obtain short path to executable dir and pass it
599     // to bindtextdomain (it doesn't understand UTF-8)
600     gchar *shortexedir = g_win32_locale_filename_from_utf8(exedir);
601     gchar *localepath = g_build_filename(shortexedir, PACKAGE_LOCALE_DIR, NULL);
602     bindtextdomain(GETTEXT_PACKAGE, localepath);
603     g_free(shortexedir);
604     g_free(localepath);
605 # endif
606     g_free(exedir);
608     // Don't touch the registry (works fine without it) for Inkscape Portable
609     gchar const *val = g_getenv("INKSCAPE_PORTABLE_PROFILE_DIR");
610     if (!val) {
611         RegistryTool rt;
612         rt.setPathInfo();
613     }
614 #elif defined(ENABLE_NLS)
615 # ifdef ENABLE_BINRELOC
616     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
617 # else
618     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
619 # endif
620 #endif
622     // the bit below compiles regardless of platform
623 #ifdef ENABLE_NLS
624     // Allow the user to override the locale directory by setting
625     // the environment variable INKSCAPE_LOCALEDIR.
626     char const *inkscape_localedir = g_getenv("INKSCAPE_LOCALEDIR");
627     if (inkscape_localedir != NULL) {
628         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
629     }
631     // common setup
632     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
633     textdomain(GETTEXT_PACKAGE);
634 #endif
636     set_extensions_env();
638     // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
639     Gtk::Main::init_gtkmm_internals();
641     LIBXML_TEST_VERSION
643     Inkscape::GC::init();
645     Inkscape::Debug::Logger::init();
647     gboolean use_gui;
649 #ifndef WIN32
650     use_gui = (g_getenv("DISPLAY") != NULL);
651 #else
652     use_gui = TRUE;
653 #endif
654     /* Test whether with/without GUI is forced */
655     for (int i = 1; i < argc; i++) {
656         if (!strcmp(argv[i], "-z")
657             || !strcmp(argv[i], "--without-gui")
658             || !strcmp(argv[i], "-p")
659             || !strncmp(argv[i], "--print", 7)
660             || !strcmp(argv[i], "-e")
661             || !strncmp(argv[i], "--export-png", 12)
662             || !strcmp(argv[i], "-l")
663             || !strncmp(argv[i], "--export-plain-svg", 18)
664             || !strcmp(argv[i], "-i")
665             || !strncmp(argv[i], "--export-area-drawing", 21)
666             || !strcmp(argv[i], "-D")
667             || !strncmp(argv[i], "--export-area-page", 18)
668             || !strcmp(argv[i], "-C")
669             || !strncmp(argv[i], "--export-id", 11)
670             || !strcmp(argv[i], "-P")
671             || !strncmp(argv[i], "--export-ps", 11)
672             || !strcmp(argv[i], "-E")
673             || !strncmp(argv[i], "--export-eps", 12)
674             || !strcmp(argv[i], "-A")
675             || !strncmp(argv[i], "--export-pdf", 12)
676             || !strncmp(argv[i], "--export-latex", 14)
677 #ifdef WIN32
678             || !strcmp(argv[i], "-M")
679             || !strncmp(argv[i], "--export-emf", 12)
680 #endif //WIN32
681             || !strcmp(argv[i], "-W")
682             || !strncmp(argv[i], "--query-width", 13)
683             || !strcmp(argv[i], "-H")
684             || !strncmp(argv[i], "--query-height", 14)
685             || !strcmp(argv[i], "-S")
686             || !strncmp(argv[i], "--query-all", 11)
687             || !strcmp(argv[i], "-X")
688             || !strncmp(argv[i], "--query-x", 9)
689             || !strcmp(argv[i], "-Y")
690             || !strncmp(argv[i], "--query-y", 9)
691             || !strcmp(argv[i], "--vacuum-defs")
692             || !strcmp(argv[i], "--shell")
693            )
694         {
695             /* main_console handles any exports -- not the gui */
696             use_gui = FALSE;
697             break;
698         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
699             use_gui = TRUE;
700             break;
701         }
702     }
704 #ifdef WIN32
705 #ifndef REPLACEARGS_ANSI
706     if ( PrintWin32::is_os_wide() )
707 #endif // REPLACEARGS_ANSI
708     {
709         // If the call fails, we'll need to convert charsets
710         needToRecodeParams = !replaceArgs( argc, argv );
711     }
712 #endif // WIN32
714     int retcode;
716     if (use_gui) {
717         retcode = sp_main_gui(argc, (const char **) argv);
718     } else {
719         retcode = sp_main_console(argc, (const char **) argv);
720     }
722     return retcode;
728 void fixupSingleFilename( gchar **orig, gchar **spare )
730     if ( orig && *orig && **orig ) {
731         GError *error = NULL;
732         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
733         if ( newFileName )
734         {
735             *orig = newFileName;
736             if ( spare ) {
737                 *spare = newFileName;
738             }
739 //             g_message("Set a replacement fixup");
740         }
741     }
746 GSList *fixupFilenameEncoding( GSList* fl )
748     GSList *newFl = NULL;
749     while ( fl ) {
750         gchar *fn = static_cast<gchar*>(fl->data);
751         fl = g_slist_remove( fl, fl->data );
752         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
753         if ( newFileName ) {
755             if ( 0 )
756             {
757                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
758                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
759                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
760                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
761                 gtk_dialog_run (GTK_DIALOG (w));
762                 gtk_widget_destroy (w);
763                 g_free(safeNewFn);
764                 g_free(safeFn);
765             }
767             g_free( fn );
768             fn = newFileName;
769             newFileName = 0;
770         }
771         else
772             if ( 0 )
773         {
774             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
775             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
776             gtk_dialog_run (GTK_DIALOG (w));
777             gtk_widget_destroy (w);
778             g_free(safeFn);
779         }
780         newFl = g_slist_append( newFl, fn );
781     }
782     return newFl;
785 int sp_common_main( int argc, char const **argv, GSList **flDest )
787     /// \todo fixme: Move these to some centralized location (Lauris)
788     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
789     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
792     // temporarily switch gettext encoding to locale, so that help messages can be output properly
793     gchar const *charset;
794     g_get_charset(&charset);
796     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
798     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
799     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
800     g_return_val_if_fail(ctx != NULL, 1);
802     /* Collect own arguments */
803     GSList *fl = sp_process_args(ctx);
804     poptFreeContext(ctx);
806     // now switch gettext back to UTF-8 (for GUI)
807     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
809     // Now let's see if the file list still holds up
810     if ( needToRecodeParams )
811     {
812         fl = fixupFilenameEncoding( fl );
813     }
815     // Check the globals for filename-fixup
816     if ( needToRecodeParams )
817     {
818         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
819         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
820         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
821     }
822     else
823     {
824         if ( sp_export_png )
825             sp_export_png_utf8 = g_strdup( sp_export_png );
826         if ( sp_export_svg )
827             sp_export_svg_utf8 = g_strdup( sp_export_svg );
828         if ( sp_global_printer )
829             sp_global_printer_utf8 = g_strdup( sp_global_printer );
830     }
832     // Return the list if wanted, else free it up.
833     if ( flDest ) {
834         *flDest = fl;
835         fl = 0;
836     } else {
837         while ( fl ) {
838             g_free( fl->data );
839             fl = g_slist_remove( fl, fl->data );
840         }
841     }
842     return 0;
845 static void
846 snooper(GdkEvent *event, gpointer /*data*/) {
847     if (inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
848     {
849         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
850         switch (event->type) {
851             case GDK_MOTION_NOTIFY:
852                 if(event->motion.state & mapping) {
853                     event->motion.state|=GDK_MOD1_MASK;
854                 }
855                 break;
856             case GDK_BUTTON_PRESS:
857                 if(event->button.state & mapping) {
858                     event->button.state|=GDK_MOD1_MASK;
859                 }
860                 break;
861              case GDK_KEY_PRESS:
862                  if(event->key.state & mapping) {
863                      event->key.state|=GDK_MOD1_MASK;
864                  }
865                  break;
866         default:
867             break;
868         }
869     }
871     if (inkscape_trackalt()) {
872         // MacOS X with X11 has some problem with the default
873         // xmodmapping.  A ~/.xmodmap solution does not work reliably due
874         // to the way we package our executable in a .app that can launch
875         // X11 or use an already-running X11.  The same problem has been
876         // reported on Linux but there is no .app/X11 to get in the way
877         // of ~/.xmodmap fixes.  So we make this a preference.
878         //
879         // For some reason, Gdk senses changes in Alt (Mod1) state for
880         // many message types, but not for keystrokes!  So this ugly hack
881         // tracks what the state of Alt-pressing is, and ensures
882         // GDK_MOD1_MASK is in the event->key.state as appropriate.
883         //
884         static gboolean altL_pressed = FALSE;
885         static gboolean altR_pressed = FALSE;
886         static gboolean alt_pressed = FALSE;
887         guint get_group0_keyval(GdkEventKey* event);
888         guint keyval = 0;
889         switch (event->type) {
890         case GDK_MOTION_NOTIFY:
891             alt_pressed = TRUE && (event->motion.state & GDK_MOD1_MASK);
892             break;
893         case GDK_BUTTON_PRESS:
894             alt_pressed = TRUE && (event->button.state & GDK_MOD1_MASK);
895             break;
896         case GDK_KEY_PRESS:
897             keyval = get_group0_keyval(&event->key);
898             if (keyval == GDK_Alt_L) altL_pressed = TRUE;
899             if (keyval == GDK_Alt_R) altR_pressed = TRUE;
900             alt_pressed = alt_pressed || altL_pressed || altR_pressed;
901             alt_pressed = alt_pressed || (event->button.state & GDK_MOD1_MASK);
902             if (alt_pressed)
903                 event->key.state |= GDK_MOD1_MASK;
904             else
905                 event->key.state &= ~GDK_MOD1_MASK;
906             break;
907         case GDK_KEY_RELEASE:
908             keyval = get_group0_keyval(&event->key);
909             if (keyval == GDK_Alt_L) altL_pressed = FALSE;
910             if (keyval == GDK_Alt_R) altR_pressed = FALSE;
911             if (!altL_pressed && !altR_pressed)
912                 alt_pressed = FALSE;
913             break;
914         default:
915             break;
916         }
917         //printf("alt_pressed: %s\n", alt_pressed? "+" : "-");
918     }
920     gtk_main_do_event (event);
923 static std::vector<Glib::ustring> getDirectorySet(const gchar* userDir, const gchar* const * systemDirs) {
924     std::vector<Glib::ustring> listing;
925     listing.push_back(userDir);
926     for ( const char* const* cur = systemDirs; *cur; cur++ )
927     {
928         listing.push_back(*cur);
929     }
930     return listing;
933 int
934 sp_main_gui(int argc, char const **argv)
936     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
938     GSList *fl = NULL;
939     int retVal = sp_common_main( argc, argv, &fl );
940     g_return_val_if_fail(retVal == 0, 1);
942     // Add possible icon entry directories
943     std::vector<Glib::ustring> dataDirs = getDirectorySet( g_get_user_data_dir(),
944                                                            g_get_system_data_dirs() );
945     for (std::vector<Glib::ustring>::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
946     {
947         std::vector<Glib::ustring> listing;
948         listing.push_back(*it);
949         listing.push_back("inkscape");
950         listing.push_back("icons");
951         Glib::ustring dir = Glib::build_filename(listing);
952         gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir.c_str());
953     }
955     // Add our icon directory to the search path for icon theme lookups.
956     gchar *usericondir = profile_path("icons");
957     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), usericondir);
958     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
959     g_free(usericondir);
961     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
962     Inkscape::Debug::log_display_config();
964     // Set default window icon. Obeys the theme.
965     gtk_window_set_default_icon_name("inkscape");
966     // Do things that were previously in inkscape_gtk_stock_init().
967     sp_icon_get_phys_size(GTK_ICON_SIZE_MENU);
968     Inkscape::UI::Widget::Panel::prep();
970     gboolean create_new = TRUE;
972     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
973     inkscape_application_init(argv[0], true);
975     while (fl) {
976         if (sp_file_open((gchar *)fl->data,NULL)) {
977             create_new=FALSE;
978         }
979         fl = g_slist_remove(fl, fl->data);
980     }
981     if (create_new) {
982         sp_file_new_default();
983     }
985     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
986     main_instance.run();
988 #ifdef WIN32
989     //We might not need anything here
990     //sp_win32_finish(); <-- this is a NOP func
991 #endif
993     return 0;
996 /**
997  * Process file list
998  */
999 void sp_process_file_list(GSList *fl)
1001     while (fl) {
1002         const gchar *filename = (gchar *)fl->data;
1004         SPDocument *doc = NULL;
1005         try {
1006             doc = Inkscape::Extension::open(NULL, filename);
1007         } catch (Inkscape::Extension::Input::no_extension_found &e) {
1008             doc = NULL;
1009         } catch (Inkscape::Extension::Input::open_failed &e) {
1010             doc = NULL;
1011         }
1013         if (doc == NULL) {
1014             try {
1015                 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
1016             } catch (Inkscape::Extension::Input::no_extension_found &e) {
1017                 doc = NULL;
1018             } catch (Inkscape::Extension::Input::open_failed &e) {
1019                 doc = NULL;
1020             }
1021         }
1022         if (doc == NULL) {
1023             g_warning("Specified document %s cannot be opened (does not exist or not a valid SVG file)", filename);
1024         } else {
1025             if (sp_vacuum_defs) {
1026                 doc->vacuumDocument();
1027             }
1028             if (sp_vacuum_defs && !sp_export_svg) {
1029                 // save under the name given in the command line
1030                 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
1031             }
1032             if (sp_global_printer) {
1033                 sp_print_document_to_file(doc, sp_global_printer);
1034             }
1035             if (sp_export_png || (sp_export_id && sp_export_use_hints)) {
1036                 sp_do_export_png(doc);
1037             }
1038             if (sp_export_svg) {
1039                 Inkscape::XML::Document *rdoc;
1040                 Inkscape::XML::Node *repr;
1041                 rdoc = sp_repr_document_new("svg:svg");
1042                 repr = rdoc->root();
1043                 repr = doc->getRoot()->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
1044                 sp_repr_save_rebased_file(repr->document(), sp_export_svg, SP_SVG_NS_URI,
1045                                           doc->getBase(), sp_export_svg);
1046             }
1047             if (sp_export_ps) {
1048                 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
1049             }
1050             if (sp_export_eps) {
1051                 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
1052             }
1053             if (sp_export_pdf) {
1054                 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
1055             }
1056 #ifdef WIN32
1057             if (sp_export_emf) {
1058                 do_export_emf(doc, sp_export_emf, "image/x-emf");
1059             }
1060 #endif //WIN32
1061             if (sp_query_all) {
1062                 do_query_all (doc);
1063             } else if (sp_query_width || sp_query_height) {
1064                 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
1065             } else if (sp_query_x || sp_query_y) {
1066                 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
1067             }
1069             delete doc;
1070         }
1071         fl = g_slist_remove(fl, fl->data);
1072     }
1075 /**
1076  * Run the application as an interactive shell, parsing command lines from stdin
1077  * Returns -1 on error.
1078  */
1079 int sp_main_shell(char const* command_name)
1081     int retval = 0;
1083     const unsigned int buffer_size = 4096;
1084     gchar *command_line = g_strnfill(buffer_size, 0);
1085     g_strlcpy(command_line, command_name, buffer_size);
1086     gsize offset = g_strlcat(command_line, " ", buffer_size);
1087     gsize sizeLeft = buffer_size - offset;
1088     gchar *useme = command_line + offset;
1090     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
1091     fflush(stdout);
1092     char* linedata = 0;
1093     do {
1094         fprintf(stdout, ">");
1095         fflush(stdout);
1096         if ((linedata = fgets(useme, sizeLeft, stdin))) {
1097             size_t len = strlen(useme);
1098             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
1099                 fprintf(stdout, "ERROR: Command line too long\n");
1100                 // Consume rest of line
1101                 retval = -1; // If the while loop completes, this remains -1
1102                 while (fgets(useme, sizeLeft, stdin) && retval) {
1103                     len = strlen(command_line);
1104                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
1105                         retval = 0;
1106                     }
1107                 }
1108             } else {
1109                 useme[--len] = '\0';  // Strip newline
1110                 if (useme[len - 1] == '\r') {
1111                     useme[--len] = '\0';
1112                 }
1113                 if ( strcmp(useme, "quit") == 0 ) {
1114                     // Time to quit
1115                     fflush(stdout);
1116                     linedata = 0; // mark for exit
1117                 } else if ( len < 1 ) {
1118                     // blank string. Do nothing.
1119                 } else {
1120                     GError* parseError = 0;
1121                     gchar** argv = 0;
1122                     gint argc = 0;
1123                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
1124                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
1125                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
1126                         if ( ctx ) {
1127                             GSList *fl = sp_process_args(ctx);
1128                             sp_process_file_list(fl);
1129                             poptFreeContext(ctx);
1130                         } else {
1131                             retval = 1; // not sure why. But this was the previous return value
1132                         }
1133                         resetCommandlineGlobals();
1134                         g_strfreev(argv);
1135                     } else {
1136                         g_warning("Cannot parse commandline: %s", useme);
1137                     }
1138                 }
1139             }
1140         } // if (linedata...
1141     } while (linedata && (retval == 0));
1143     g_free(command_line);
1144     return retval;
1147 int sp_main_console(int argc, char const **argv)
1149     /* We are started in text mode */
1151     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1152      * in a non-Gtk environment.  Used in libnrtype's
1153      * FontInstance.cpp and FontFactory.cpp.
1154      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1155      */
1156     g_type_init();
1157     char **argv2 = const_cast<char **>(argv);
1158     gtk_init_check( &argc, &argv2 );
1159     //setlocale(LC_ALL, "");
1161     GSList *fl = NULL;
1162     int retVal = sp_common_main( argc, argv, &fl );
1163     g_return_val_if_fail(retVal == 0, 1);
1165     if (fl == NULL && !sp_shell) {
1166         g_print("Nothing to do!\n");
1167         exit(0);
1168     }
1170     inkscape_application_init(argv[0], false);
1172     if (sp_shell) {
1173         sp_main_shell(argv[0]); // Run as interactive shell
1174         exit(0);
1175     } else {
1176         sp_process_file_list(fl); // Normal command line invokation
1177     }
1179     return 0;
1182 static void
1183 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1185     SPObject *o = NULL;
1187     if (id) {
1188         o = doc->getObjectById(id);
1189         if (o) {
1190             if (!SP_IS_ITEM (o)) {
1191                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1192                 return;
1193             }
1194         } else {
1195             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1196             return;
1197         }
1198     } else {
1199         o = doc->getRoot();
1200     }
1202     if (o) {
1203         doc->ensureUpToDate();
1204         SPItem *item = ((SPItem *) o);
1206         // "true" SVG bbox for scripting
1207         Geom::OptRect area = item->getBounds(item->i2doc_affine());
1208         if (area) {
1209             Inkscape::SVGOStringStream os;
1210             if (extent) {
1211                 os << area->dimensions()[axis];
1212             } else {
1213                 os << area->min()[axis];
1214             }
1215             g_print ("%s", os.str().c_str());
1216         } else {
1217             g_print("0");
1218         }
1219     }
1222 static void do_query_all(SPDocument *doc)
1224     SPObject *o = doc->getRoot();
1226     if (o) {
1227         doc->ensureUpToDate();
1228         do_query_all_recurse(o);
1229     }
1232 static void
1233 do_query_all_recurse (SPObject *o)
1235     SPItem *item = ((SPItem *) o);
1236     if (o->getId() && SP_IS_ITEM(item)) {
1237         Geom::OptRect area = item->getBounds(item->i2doc_affine());
1238         if (area) {
1239             Inkscape::SVGOStringStream os;
1240             os << o->getId();
1241             os << "," << area->min()[Geom::X];
1242             os << "," << area->min()[Geom::Y];
1243             os << "," << area->dimensions()[Geom::X];
1244             os << "," << area->dimensions()[Geom::Y];
1245             g_print ("%s\n", os.str().c_str());
1246         }
1247     }
1249     SPObject *child = o->children;
1250     while (child) {
1251         do_query_all_recurse (child);
1252         child = child->next;
1253     }
1257 static void
1258 sp_do_export_png(SPDocument *doc)
1260     const gchar *filename = NULL;
1261     bool filename_from_hint = false;
1262     gdouble dpi = 0.0;
1264     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1265         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1266     }
1268     GSList *items = NULL;
1270     Geom::Rect area;
1271     if (sp_export_id || sp_export_area_drawing) {
1273         SPObject *o = NULL;
1274         SPObject *o_area = NULL;
1275         if (sp_export_id && sp_export_area_drawing) {
1276             o = doc->getObjectById(sp_export_id);
1277             o_area = doc->getRoot();
1278         } else if (sp_export_id) {
1279             o = doc->getObjectById(sp_export_id);
1280             o_area = o;
1281         } else if (sp_export_area_drawing) {
1282             o = doc->getRoot();
1283             o_area = o;
1284         }
1286         if (o) {
1287             if (!SP_IS_ITEM (o)) {
1288                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1289                 return;
1290             }
1292             items = g_slist_prepend (items, SP_ITEM(o));
1294             if (sp_export_id_only) {
1295                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1296             }
1298             if (sp_export_use_hints) {
1300                 // retrieve export filename hint
1301                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1302                 if (fn_hint) {
1303                     if (sp_export_png) {
1304                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1305                         filename = sp_export_png;
1306                     } else {
1307                         filename = fn_hint;
1308                         filename_from_hint = true;
1309                     }
1310                 } else {
1311                     g_warning ("Export filename hint not found for the object.");
1312                     filename = sp_export_png;
1313                 }
1315                 // retrieve export dpi hints
1316                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1317                 if (dpi_hint) {
1318                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1319                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1320                     } else {
1321                         dpi = atof(dpi_hint);
1322                     }
1323                 } else {
1324                     g_warning ("Export DPI hint not found for the object.");
1325                 }
1327             }
1329             // write object bbox to area
1330             doc->ensureUpToDate();
1331             Geom::OptRect areaMaybe;
1332             static_cast<SPItem *>(o_area)->invoke_bbox( areaMaybe, static_cast<SPItem *>(o_area)->i2d_affine(), TRUE);
1333             if (areaMaybe) {
1334                 area = *areaMaybe;
1335             } else {
1336                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1337                 return;
1338             }
1339         } else {
1340             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1341             return;
1342         }
1343     }
1345     if (sp_export_area) {
1346         /* Try to parse area (given in SVG pixels) */
1347         gdouble x0,y0,x1,y1;
1348         if (sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) != 4) {
1349             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1350             return;
1351         }
1352         area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1353     } else if (sp_export_area_page || !(sp_export_id || sp_export_area_drawing)) {
1354         /* Export the whole page: note: Inkscape uses 'page' in all menus and dialogs, not 'canvas' */
1355         doc->ensureUpToDate();
1356         Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1357         area = Geom::Rect(origin, origin + doc->getDimensions());
1358     }
1360     // set filename and dpi from options, if not yet set from the hints
1361     if (!filename) {
1362         if (!sp_export_png) {
1363             g_warning ("No export filename given and no filename hint. Nothing exported.");
1364             return;
1365         }
1366         filename = sp_export_png;
1367     }
1369     if (sp_export_dpi && dpi == 0.0) {
1370         dpi = atof(sp_export_dpi);
1371         if ((dpi < 0.1) || (dpi > 10000.0)) {
1372             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1373             return;
1374         }
1375         g_print("DPI: %g\n", dpi);
1376     }
1378     if (sp_export_area_snap) {
1379         round_rectangle_outwards(area);
1380     }
1382     // default dpi
1383     if (dpi == 0.0) {
1384         dpi = PX_PER_IN;
1385     }
1387     unsigned long int width = 0;
1388     unsigned long int height = 0;
1390     if (sp_export_width) {
1391         errno=0;
1392         width = strtoul(sp_export_width, NULL, 0);
1393         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1394             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1395             return;
1396         }
1397         dpi = (gdouble) width * PX_PER_IN / area.width();
1398     }
1400     if (sp_export_height) {
1401         errno=0;
1402         height = strtoul(sp_export_height, NULL, 0);
1403         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1404             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1405             return;
1406         }
1407         dpi = (gdouble) height * PX_PER_IN / area.height();
1408     }
1410     if (!sp_export_width) {
1411         width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1412     }
1414     if (!sp_export_height) {
1415         height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1416     }
1418     guint32 bgcolor = 0x00000000;
1419     if (sp_export_background) {
1420         // override the page color
1421         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1422         bgcolor |= 0xff; // default is no opacity
1423     } else {
1424         // read from namedview
1425         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1426         if (nv && nv->attribute("pagecolor"))
1427             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1428         if (nv && nv->attribute("inkscape:pageopacity"))
1429             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1430     }
1432     if (sp_export_background_opacity) {
1433         // override opacity
1434         gfloat value;
1435         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1436             if (value > 1.0) {
1437                 value = CLAMP (value, 1.0f, 255.0f);
1438                 bgcolor &= (guint32) 0xffffff00;
1439                 bgcolor |= (guint32) floor(value);
1440             } else {
1441                 value = CLAMP (value, 0.0f, 1.0f);
1442                 bgcolor &= (guint32) 0xffffff00;
1443                 bgcolor |= SP_COLOR_F_TO_U(value);
1444             }
1445         }
1446     }
1448     gchar *path = 0;
1449     if (filename_from_hint) {
1450         //Make relative paths go from the document location, if possible:
1451         if (!g_path_is_absolute(filename) && doc->getURI()) {
1452             gchar *dirname = g_path_get_dirname(doc->getURI());
1453             if (dirname) {
1454                 path = g_build_filename(dirname, filename, NULL);
1455                 g_free(dirname);
1456             }
1457         }
1458         if (!path) {
1459             path = g_strdup(filename);
1460         }
1461     } else {
1462         path = g_strdup(filename);
1463     }
1465     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1467     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);
1469     g_print("Bitmap saved as: %s\n", filename);
1471     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1472         sp_export_png_file(doc, path, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1473     } else {
1474         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1475     }
1477     g_free (path);
1478     g_slist_free (items);
1482 /**
1483  *  Perform a PDF/PS/EPS export
1484  *
1485  *  \param doc Document to export.
1486  *  \param uri URI to export to.
1487  *  \param mime MIME type to export as.
1488  */
1490 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1492     Inkscape::Extension::DB::OutputList o;
1493     Inkscape::Extension::db.get_output_list(o);
1494     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1495     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1496         i++;
1497     }
1499     if (i == o.end())
1500     {
1501         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1502         return;
1503     }
1505     if (sp_export_id) {
1506         SPObject *o = doc->getObjectById(sp_export_id);
1507         if (o == NULL) {
1508             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1509             return;
1510         }
1511         (*i)->set_param_string ("exportId", sp_export_id);
1512     } else {
1513         (*i)->set_param_string ("exportId", "");
1514     }
1516     if (sp_export_area_page && sp_export_area_drawing) {
1517         g_warning ("You cannot use --export-area-page and --export-area-drawing at the same time; only the former will take effect.");
1518         sp_export_area_drawing = false;
1519     }
1521     if (sp_export_area_drawing) {
1522         (*i)->set_param_bool ("areaDrawing", TRUE);
1523     } else {
1524         (*i)->set_param_bool ("areaDrawing", FALSE);
1525     }
1527     if (sp_export_area_page) {
1528         if (sp_export_eps) {
1529             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.");
1530         }
1531         (*i)->set_param_bool ("areaPage", TRUE);
1532     } else {
1533         (*i)->set_param_bool ("areaPage", FALSE);
1534     }
1536     if (!sp_export_area_drawing && !sp_export_area_page && !sp_export_id) {
1537         // neither is set, set page as default for ps/pdf and drawing for eps
1538         if (sp_export_eps) {
1539             try {
1540                (*i)->set_param_bool("areaDrawing", TRUE);
1541             } catch (...) {}
1542         }
1543     }
1545     if (sp_export_text_to_path) {
1546         (*i)->set_param_bool("textToPath", TRUE);
1547     } else {
1548         (*i)->set_param_bool("textToPath", FALSE);
1549     }
1551     if (sp_export_latex) {
1552         (*i)->set_param_bool("textToLaTeX", TRUE);
1553     } else {
1554         (*i)->set_param_bool("textToLaTeX", FALSE);
1555     }
1557     if (sp_export_ignore_filters) {
1558         (*i)->set_param_bool("blurToBitmap", FALSE);
1559     } else {
1560         (*i)->set_param_bool("blurToBitmap", TRUE);
1562         gdouble dpi = 90.0;
1563         if (sp_export_dpi) {
1564             dpi = atof(sp_export_dpi);
1565             if ((dpi < 1) || (dpi > 10000.0)) {
1566                 g_warning("DPI value %s out of range [1 - 10000]. Using 90 dpi instead.", sp_export_dpi);
1567                 dpi = 90;
1568             }
1569         }
1571         (*i)->set_param_int("resolution", (int) dpi);
1572     }
1574     (*i)->save(doc, uri);
1577 #ifdef WIN32
1578 /**
1579  *  Export a document to EMF
1580  *
1581  *  \param doc Document to export.
1582  *  \param uri URI to export to.
1583  *  \param mime MIME type to export as (should be "image/x-emf")
1584  */
1586 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1588     Inkscape::Extension::DB::OutputList o;
1589     Inkscape::Extension::db.get_output_list(o);
1590     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1591     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1592         i++;
1593     }
1595     if (i == o.end())
1596     {
1597         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1598         return;
1599     }
1601     (*i)->save(doc, uri);
1603 #endif //WIN32
1605 #ifdef WIN32
1606 bool replaceArgs( int& argc, char**& argv )
1608     bool worked = false;
1610 #ifdef REPLACEARGS_DEBUG
1611     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1612 #endif // REPLACEARGS_DEBUG
1614     wchar_t* line = GetCommandLineW();
1615     if ( line )
1616     {
1617 #ifdef REPLACEARGS_DEBUG
1618         {
1619             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1620             if ( utf8Line )
1621             {
1622                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1623                 {
1624                     char tmp[strlen(safe) + 32];
1625                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1626                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1627                 }
1628             }
1629         }
1630 #endif // REPLACEARGS_DEBUG
1632         int numArgs = 0;
1633         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1635 #ifdef REPLACEARGS_ANSI
1636 // test code for trying things on Win95/98/ME
1637         if ( !parsed )
1638         {
1639 #ifdef REPLACEARGS_DEBUG
1640             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1641 #endif // REPLACEARGS_DEBUG
1642             int lineLen = wcslen(line) + 1;
1643             wchar_t* lineDup = new wchar_t[lineLen];
1644             wcsncpy( lineDup, line, lineLen );
1646             int pos = 0;
1647             bool inQuotes = false;
1648             bool inWhitespace = true;
1649             std::vector<int> places;
1650             while ( lineDup[pos] )
1651             {
1652                 if ( inQuotes )
1653                 {
1654                     if ( lineDup[pos] == L'"' )
1655                     {
1656                         inQuotes = false;
1657                     }
1658                 }
1659                 else if ( lineDup[pos] == L'"' )
1660                 {
1661                     inQuotes = true;
1662                     inWhitespace = false;
1663                     places.push_back(pos);
1664                 }
1665                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1666                 {
1667                     if ( !inWhitespace )
1668                     {
1669                         inWhitespace = true;
1670                         lineDup[pos] = 0;
1671                     }
1672                 }
1673                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1674                 {
1675                     inWhitespace = false;
1676                     places.push_back(pos);
1677                 }
1678                 else
1679                 {
1680                     // consume
1681                 }
1682                 pos++;
1683             }
1684 #ifdef REPLACEARGS_DEBUG
1685             {
1686                 char tmp[256];
1687                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1688                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1689             }
1690 #endif // REPLACEARGS_DEBUG
1692             wchar_t** block = new wchar_t*[places.size()];
1693             int i = 0;
1694             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1695             {
1696                 block[i++] = &lineDup[*it];
1697             }
1698             parsed = block;
1699             numArgs = places.size();
1700         }
1701 #endif // REPLACEARGS_ANSI
1703         if ( parsed )
1704         {
1705             std::vector<wchar_t*>expandedArgs;
1706             if ( numArgs > 0 )
1707             {
1708                 expandedArgs.push_back( parsed[0] );
1709             }
1711             for ( int i1 = 1; i1 < numArgs; i1++ )
1712             {
1713                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1714                 wildcarded &= parsed[i1][0] != L'"';
1715                 wildcarded &= parsed[i1][0] != L'-';
1716                 if ( wildcarded )
1717                 {
1718 #ifdef REPLACEARGS_ANSI
1719                     WIN32_FIND_DATAA data;
1720 #else
1721                     WIN32_FIND_DATAW data;
1722 #endif // REPLACEARGS_ANSI
1724                     memset((void *)&data, 0, sizeof(data));
1726                     int baseLen = wcslen(parsed[i1]) + 2;
1727                     wchar_t* base = new wchar_t[baseLen];
1728                     wcsncpy( base, parsed[i1], baseLen );
1729                     wchar_t* last = wcsrchr( base, L'\\' );
1730                     if ( last )
1731                     {
1732                         last[1] = 0;
1733                     }
1734                     else
1735                     {
1736                         base[0] = 0;
1737                     }
1738                     baseLen = wcslen( base );
1740 #ifdef REPLACEARGS_ANSI
1741                     char target[MAX_PATH];
1742                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1743                     {
1744                         HANDLE hf = FindFirstFileA( target, &data );
1745 #else
1746                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1747 #endif // REPLACEARGS_ANSI
1748                         if ( hf != INVALID_HANDLE_VALUE )
1749                         {
1750                             BOOL found = TRUE;
1751                             do
1752                             {
1753 #ifdef REPLACEARGS_ANSI
1754                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1755                                 if ( howMany > 0 )
1756                                 {
1757                                     howMany += baseLen;
1758                                     wchar_t* tmp = new wchar_t[howMany + 1];
1759                                     wcsncpy( tmp, base, howMany + 1 );
1760                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1761                                     expandedArgs.push_back( tmp );
1762                                     found = FindNextFileA( hf, &data );
1763                                 }
1764 #else
1765                                 int howMany = wcslen(data.cFileName) + baseLen;
1766                                 wchar_t* tmp = new wchar_t[howMany + 1];
1767                                 wcsncpy( tmp, base, howMany + 1 );
1768                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1769                                 expandedArgs.push_back( tmp );
1770                                 found = FindNextFileW( hf, &data );
1771 #endif // REPLACEARGS_ANSI
1772                             } while ( found );
1774                             FindClose( hf );
1775                         }
1776                         else
1777                         {
1778                             expandedArgs.push_back( parsed[i1] );
1779                         }
1780 #ifdef REPLACEARGS_ANSI
1781                     }
1782 #endif // REPLACEARGS_ANSI
1784                     delete[] base;
1785                 }
1786                 else
1787                 {
1788                     expandedArgs.push_back( parsed[i1] );
1789                 }
1790             }
1792             {
1793                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1794                 int iz = 0;
1795                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1796                 {
1797                     block[iz++] = *it;
1798                 }
1799                 parsed = block;
1800                 numArgs = expandedArgs.size();
1801             }
1803             std::vector<gchar*> newArgs;
1804             for ( int i = 0; i < numArgs; i++ )
1805             {
1806                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1807                 if ( replacement )
1808                 {
1809 #ifdef REPLACEARGS_DEBUG
1810                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1812                     if ( safe2 )
1813                     {
1814                         {
1815                             char tmp[1024];
1816                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1817                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1818                         }
1819                         g_free( safe2 );
1820                     }
1821 #endif // REPLACEARGS_DEBUG
1823                     newArgs.push_back( replacement );
1824                 }
1825                 else
1826                 {
1827                     newArgs.push_back( blankParam );
1828                 }
1829             }
1831             // Now push our munged params to be the new argv and argc
1832             {
1833                 char** block = new char*[newArgs.size()];
1834                 int iz = 0;
1835                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1836                 {
1837                     block[iz++] = *it;
1838                 }
1839                 argv = block;
1840                 argc = newArgs.size();
1841                 worked = true;
1842             }
1843         }
1844 #ifdef REPLACEARGS_DEBUG
1845         else
1846         {
1847             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1848         }
1849 #endif // REPLACEARGS_DEBUG
1850     }
1851 #ifdef REPLACEARGS_DEBUG
1852     else
1853     {
1854         {
1855             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1856         }
1858         char* line2 = GetCommandLineA();
1859         if ( line2 )
1860         {
1861             gchar *safe = Inkscape::IO::sanitizeString(line2);
1862             {
1863                 {
1864                     char tmp[strlen(safe) + 32];
1865                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1866                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1867                 }
1868             }
1869         }
1870         else
1871         {
1872             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1873         }
1874     }
1875 #endif // REPLACEARGS_DEBUG
1877     return worked;
1879 #endif // WIN32
1881 static GSList *
1882 sp_process_args(poptContext ctx)
1884     GSList *fl = NULL;
1886     gint a;
1887     while ((a = poptGetNextOpt(ctx)) != -1) {
1888         switch (a) {
1889             case SP_ARG_FILE: {
1890                 gchar const *fn = poptGetOptArg(ctx);
1891                 if (fn != NULL) {
1892                     fl = g_slist_append(fl, g_strdup(fn));
1893                 }
1894                 break;
1895             }
1896             case SP_ARG_VERSION: {
1897                 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1898                 exit(0);
1899                 break;
1900             }
1901             case SP_ARG_EXTENSIONDIR: {
1902                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1903                 exit(0);
1904                 break;
1905             }
1906             case SP_ARG_VERB_LIST: {
1907                 // This really shouldn't go here, we should init the app.
1908                 // But, since we're just exiting in this path, there is
1909                 // no harm, and this is really a better place to put
1910                 // everything else.
1911                 Inkscape::Extension::init();
1912                 Inkscape::Verb::list();
1913                 exit(0);
1914                 break;
1915             }
1916             case SP_ARG_VERB:
1917             case SP_ARG_SELECT: {
1918                 gchar const *arg = poptGetOptArg(ctx);
1919                 if (arg != NULL) {
1920                     // printf("Adding in: %s\n", arg);
1921                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1922                 }
1923                 break;
1924             }
1925             case POPT_ERROR_BADOPT: {
1926                 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1927                 exit(1);
1928                 break;
1929             }
1930             default: {
1931                 break;
1932             }
1933         }
1934     }
1936     gchar const ** const args = poptGetArgs(ctx);
1937     if (args != NULL) {
1938         for (unsigned i = 0; args[i] != NULL; i++) {
1939             fl = g_slist_append(fl, g_strdup(args[i]));
1940         }
1941     }
1943     return fl;
1947 /*
1948   Local Variables:
1949   mode:c++
1950   c-file-style:"stroustrup"
1951   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1952   indent-tabs-mode:nil
1953   fill-column:99
1954   End:
1955 */
1956 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :