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