Code

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