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