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