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