Code

Merge from trunk
[inkscape.git] / src / main.cpp
1 #define __MAIN_C__
3 /** \file
4  * Inkscape - an ambitious vector drawing program
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Frank Felfe <innerspace@iname.com>
9  *   Davide Puricelli <evo@debian.org>
10  *   Mitsuru Oka <oka326@parkcity.ne.jp>
11  *   Masatake YAMATO  <jet@gyve.org>
12  *   F.J.Franklin <F.J.Franklin@sheffield.ac.uk>
13  *   Michael Meeks <michael@helixcode.com>
14  *   Chema Celorio <chema@celorio.com>
15  *   Pawel Palucha
16  *   Bryce Harrington <bryce@bryceharrington.com>
17  * ... and various people who have worked with various projects
18  *
19  * Copyright (C) 1999-2004 authors
20  * Copyright (C) 2001-2002 Ximian, Inc.
21  *
22  * Released under GNU GPL, read the file 'COPYING' for more information
23  */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 #include "path-prefix.h"
31 // This has to be included prior to anything that includes setjmp.h, it croaks otherwise
32 #include <png.h>
34 #include <gtk/gtkmessagedialog.h>
36 #ifdef HAVE_IEEEFP_H
37 #include <ieeefp.h>
38 #endif
39 #include <cstring>
40 #include <string>
41 #include <locale.h>
42 #include <stdlib.h>
44 #include <popt.h>
45 #ifndef POPT_TABLEEND
46 #define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
47 #endif /* Not def: POPT_TABLEEND */
49 #include <libxml/tree.h>
50 #include <glib-object.h>
51 #include <gtk/gtk.h>
52 #include <gtk/gtkmain.h>
53 #include <gtk/gtksignal.h>
54 #include <gtk/gtkwindow.h>
55 #include <gtk/gtkbox.h>
57 #include "gc-core.h"
59 #ifdef AND
60 #undef AND
61 #endif
63 #include "macros.h"
64 #include "file.h"
65 #include "document.h"
66 #include "sp-object.h"
67 #include "interface.h"
68 #include "print.h"
69 #include "color.h"
70 #include "sp-item.h"
71 #include "sp-root.h"
72 #include "unit-constants.h"
74 #include "svg/svg.h"
75 #include "svg/svg-color.h"
76 #include "svg/stringstream.h"
78 #include "inkscape-private.h"
79 #include "inkscape-version.h"
81 #include "sp-namedview.h"
82 #include "sp-guide.h"
83 #include "sp-object-repr.h"
84 #include "xml/repr.h"
86 #include "io/sys.h"
88 #include "debug/logger.h"
89 #include "debug/log-display-config.h"
91 #include "helper/png-write.h"
92 #include "helper/geom.h"
94 #include <extension/extension.h>
95 #include <extension/system.h>
96 #include <extension/db.h>
97 #include <extension/output.h>
98 #include <extension/input.h>
100 #ifdef WIN32
101 #include "registrytool.h"
102 #include "extension/internal/win32.h"
103 using Inkscape::Extension::Internal::PrintWin32;
104 #endif // WIN32
106 #include "extension/init.h"
108 #include <glibmm/i18n.h>
109 #include <gtkmm/main.h>
111 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
112 #define bind_textdomain_codeset(p,c)
113 #endif
115 #include "application/application.h"
116 #include "main-cmdlineact.h"
117 #include "widgets/icon.h"
118 #include "ui/widget/panel.h"
120 #include <errno.h>
122 enum {
123     SP_ARG_NONE,
124     SP_ARG_NOGUI,
125     SP_ARG_GUI,
126     SP_ARG_FILE,
127     SP_ARG_PRINT,
128     SP_ARG_EXPORT_PNG,
129     SP_ARG_EXPORT_DPI,
130     SP_ARG_EXPORT_AREA,
131     SP_ARG_EXPORT_AREA_DRAWING,
132     SP_ARG_EXPORT_AREA_PAGE,
133     SP_ARG_EXPORT_AREA_SNAP,
134     SP_ARG_EXPORT_WIDTH,
135     SP_ARG_EXPORT_HEIGHT,
136     SP_ARG_EXPORT_ID,
137     SP_ARG_EXPORT_ID_ONLY,
138     SP_ARG_EXPORT_USE_HINTS,
139     SP_ARG_EXPORT_BACKGROUND,
140     SP_ARG_EXPORT_BACKGROUND_OPACITY,
141     SP_ARG_EXPORT_SVG,
142     SP_ARG_EXPORT_PS,
143     SP_ARG_EXPORT_EPS,
144     SP_ARG_EXPORT_PDF,
145     SP_ARG_EXPORT_LATEX,
146 #ifdef WIN32
147     SP_ARG_EXPORT_EMF,
148 #endif //WIN32
149     SP_ARG_EXPORT_TEXT_TO_PATH,
150     SP_ARG_EXPORT_IGNORE_FILTERS,
151     SP_ARG_EXTENSIONDIR,
152     SP_ARG_QUERY_X,
153     SP_ARG_QUERY_Y,
154     SP_ARG_QUERY_WIDTH,
155     SP_ARG_QUERY_HEIGHT,
156     SP_ARG_QUERY_ALL,
157     SP_ARG_QUERY_ID,
158     SP_ARG_SHELL,
159     SP_ARG_VERSION,
160     SP_ARG_VACUUM_DEFS,
161     SP_ARG_VERB_LIST,
162     SP_ARG_VERB,
163     SP_ARG_SELECT,
164     SP_ARG_LAST
165 };
167 int sp_main_gui(int argc, char const **argv);
168 int sp_main_console(int argc, char const **argv);
169 static void sp_do_export_png(SPDocument *doc);
170 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime);
171 #ifdef WIN32
172 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
173 #endif //WIN32
174 static void do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id);
175 static void do_query_all (SPDocument *doc);
176 static void do_query_all_recurse (SPObject *o);
178 static gchar *sp_global_printer = NULL;
179 static gchar *sp_export_png = NULL;
180 static gchar *sp_export_dpi = NULL;
181 static gchar *sp_export_area = NULL;
182 static gboolean sp_export_area_drawing = FALSE;
183 static gboolean sp_export_area_page = FALSE;
184 static gboolean sp_export_latex = FALSE;
185 static gchar *sp_export_width = NULL;
186 static gchar *sp_export_height = NULL;
187 static gchar *sp_export_id = NULL;
188 static gchar *sp_export_background = NULL;
189 static gchar *sp_export_background_opacity = NULL;
190 static gboolean sp_export_area_snap = FALSE;
191 static gboolean sp_export_use_hints = FALSE;
192 static gboolean sp_export_id_only = FALSE;
193 static gchar *sp_export_svg = NULL;
194 static gchar *sp_export_ps = NULL;
195 static gchar *sp_export_eps = NULL;
196 static gchar *sp_export_pdf = NULL;
197 #ifdef WIN32
198 static gchar *sp_export_emf = NULL;
199 #endif //WIN32
200 static gboolean sp_export_text_to_path = FALSE;
201 static gboolean sp_export_ignore_filters = FALSE;
202 static gboolean sp_export_font = FALSE;
203 static gboolean sp_query_x = FALSE;
204 static gboolean sp_query_y = FALSE;
205 static gboolean sp_query_width = FALSE;
206 static gboolean sp_query_height = FALSE;
207 static gboolean sp_query_all = FALSE;
208 static gchar *sp_query_id = NULL;
209 static int sp_new_gui = FALSE;
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;
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  * Return the directory of the .exe that is currently running
476  */
477 static Glib::ustring _win32_getExePath()
479     char exeName[MAX_PATH+1];
480     // TODO these should use xxxW() calls explicitly and convert UTF-16 <--> UTF-8
481     GetModuleFileName(NULL, exeName, MAX_PATH);
482     char *slashPos = strrchr(exeName, '\\');
483     if (slashPos) {
484         *slashPos = '\0';
485     }
486     Glib::ustring s = exeName;
487     return s;
490 /**
491  * Set up the PATH and PYTHONPATH environment variables on
492  * win32
493  */
494 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
496     // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
498     char *oldenv = getenv("PATH");
499     Glib::ustring tmp = "PATH=";
500     tmp += exePath;
501     tmp += ";";
502     tmp += exePath;
503     tmp += "\\python;";
504     tmp += exePath;
505     tmp += "\\python\\Scripts;";  // for uniconv.cmd
506     tmp += exePath;
507     tmp += "\\perl";
508     if(oldenv != NULL) {
509         tmp += ";";
510         tmp += oldenv;
511     }
512     _putenv(tmp.c_str());
514     oldenv = getenv("PYTHONPATH");
515     tmp = "PYTHONPATH=";
516     tmp += exePath;
517     tmp += "\\python;";
518     tmp += exePath;
519     tmp += "\\python\\Lib;";
520     tmp += exePath;
521     tmp += "\\python\\DLLs";
522     if(oldenv != NULL) {
523         tmp += ";";
524         tmp += oldenv;
525     }
526     _putenv(tmp.c_str());
528     return 0;
530 #endif
532 /**
533  * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
534  * can find inkex.py et al. (Bug #197475)
535  */
536 static int set_extensions_env()
538     char *oldenv = getenv("PYTHONPATH");
539     Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
540     if (oldenv != NULL) {
541         tmp += G_SEARCHPATH_SEPARATOR;
542         tmp += oldenv;
543     }
544     g_setenv("PYTHONPATH", tmp.c_str(), TRUE);
546     return 0;
550 /**
551  * This is the classic main() entry point of the program, though on some
552  * architectures it might be called by something else.
553  */
554 int
555 main(int argc, char **argv)
557 #ifdef HAVE_FPSETMASK
558     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
559        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
560        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
561     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
562 #endif
564 #ifdef WIN32
565     /*
566       Set the current directory to the directory of the
567       executable.  This seems redundant, but is needed for
568       when inkscape.exe is executed from another directory.
569       We use relative paths on win32.
570       HKCR\svgfile\shell\open\command is a good example
571     */
572     Glib::ustring homedir = _win32_getExePath();
573     // TODO these should use xxxW() calls explicitly and convert UTF-16 <--> UTF-8
574     SetCurrentDirectory(homedir.c_str());
575     _win32_set_inkscape_env(homedir);
576     // Don't touch the registry (works fine without it) for Inkscape Portable
577     gchar const *val = g_getenv("INKSCAPE_PORTABLE_PROFILE_DIR");
578     if (!val) {
579         RegistryTool rt;
580         rt.setPathInfo();
581     }
582 #endif
584     // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
585     Gtk::Main::init_gtkmm_internals();
587     // Bug #197475
588     set_extensions_env();
590    /**
591     * Call bindtextdomain() for various machines's paths
592     */
593 #ifdef ENABLE_NLS
594 #ifdef WIN32
595     Glib::ustring localePath = homedir;
596     localePath += "\\";
597     localePath += PACKAGE_LOCALE_DIR;
598     bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
599 #else
600 #ifdef ENABLE_BINRELOC
601     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
602 #else
603     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
604 #endif
605 #endif
606     // Allow the user to override the locale directory by setting
607     // the environment variable INKSCAPE_LOCALEDIR.
608     char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
609     if (inkscape_localedir != NULL) {
610         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
611     }
612 #endif
614     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
616 #ifdef ENABLE_NLS
617     textdomain(GETTEXT_PACKAGE);
618 #endif
620     LIBXML_TEST_VERSION
622     Inkscape::GC::init();
624     Inkscape::Debug::Logger::init();
626     gboolean use_gui;
628 #ifndef WIN32
629     // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
630     use_gui = (getenv("DISPLAY") != NULL);
631 #else
632     use_gui = TRUE;
633 #endif
634     /* Test whether with/without GUI is forced */
635     for (int i = 1; i < argc; i++) {
636         if (!strcmp(argv[i], "-z")
637             || !strcmp(argv[i], "--without-gui")
638             || !strcmp(argv[i], "-p")
639             || !strncmp(argv[i], "--print", 7)
640             || !strcmp(argv[i], "-e")
641             || !strncmp(argv[i], "--export-png", 12)
642             || !strcmp(argv[i], "-l")
643             || !strncmp(argv[i], "--export-plain-svg", 18)
644             || !strcmp(argv[i], "-i")
645             || !strncmp(argv[i], "--export-area-drawing", 21)
646             || !strcmp(argv[i], "-D")
647             || !strncmp(argv[i], "--export-area-page", 18)
648             || !strcmp(argv[i], "-C")
649             || !strncmp(argv[i], "--export-id", 11)
650             || !strcmp(argv[i], "-P")
651             || !strncmp(argv[i], "--export-ps", 11)
652             || !strcmp(argv[i], "-E")
653             || !strncmp(argv[i], "--export-eps", 12)
654             || !strcmp(argv[i], "-A")
655             || !strncmp(argv[i], "--export-pdf", 12)
656             || !strncmp(argv[i], "--export-latex", 14)
657 #ifdef WIN32
658             || !strcmp(argv[i], "-M")
659             || !strncmp(argv[i], "--export-emf", 12)
660 #endif //WIN32
661             || !strcmp(argv[i], "-W")
662             || !strncmp(argv[i], "--query-width", 13)
663             || !strcmp(argv[i], "-H")
664             || !strncmp(argv[i], "--query-height", 14)
665             || !strcmp(argv[i], "-S")
666             || !strncmp(argv[i], "--query-all", 11)
667             || !strcmp(argv[i], "-X")
668             || !strncmp(argv[i], "--query-x", 9)
669             || !strcmp(argv[i], "-Y")
670             || !strncmp(argv[i], "--query-y", 9)
671             || !strcmp(argv[i], "--vacuum-defs")
672             || !strcmp(argv[i], "--shell")
673            )
674         {
675             /* main_console handles any exports -- not the gui */
676             use_gui = FALSE;
677             break;
678         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
679             use_gui = TRUE;
680             break;
681         }
682     }
684 #ifdef WIN32
685 #ifndef REPLACEARGS_ANSI
686     if ( PrintWin32::is_os_wide() )
687 #endif // REPLACEARGS_ANSI
688     {
689         // If the call fails, we'll need to convert charsets
690         needToRecodeParams = !replaceArgs( argc, argv );
691     }
692 #endif // WIN32
694     /// \todo  Should this be a static object (see inkscape.cpp)?
695     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
697     return app.run();
703 void fixupSingleFilename( gchar **orig, gchar **spare )
705     if ( orig && *orig && **orig ) {
706         GError *error = NULL;
707         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
708         if ( newFileName )
709         {
710             *orig = newFileName;
711             if ( spare ) {
712                 *spare = newFileName;
713             }
714 //             g_message("Set a replacement fixup");
715         }
716     }
721 GSList *fixupFilenameEncoding( GSList* fl )
723     GSList *newFl = NULL;
724     while ( fl ) {
725         gchar *fn = static_cast<gchar*>(fl->data);
726         fl = g_slist_remove( fl, fl->data );
727         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
728         if ( newFileName ) {
730             if ( 0 )
731             {
732                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
733                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
734                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
735                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
736                 gtk_dialog_run (GTK_DIALOG (w));
737                 gtk_widget_destroy (w);
738                 g_free(safeNewFn);
739                 g_free(safeFn);
740             }
742             g_free( fn );
743             fn = newFileName;
744             newFileName = 0;
745         }
746         else
747             if ( 0 )
748         {
749             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
750             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
751             gtk_dialog_run (GTK_DIALOG (w));
752             gtk_widget_destroy (w);
753             g_free(safeFn);
754         }
755         newFl = g_slist_append( newFl, fn );
756     }
757     return newFl;
760 int sp_common_main( int argc, char const **argv, GSList **flDest )
762     /// \todo fixme: Move these to some centralized location (Lauris)
763     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
764     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
767     // temporarily switch gettext encoding to locale, so that help messages can be output properly
768     gchar const *charset;
769     g_get_charset(&charset);
771     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
773     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
774     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
775     g_return_val_if_fail(ctx != NULL, 1);
777     /* Collect own arguments */
778     GSList *fl = sp_process_args(ctx);
779     poptFreeContext(ctx);
781     // now switch gettext back to UTF-8 (for GUI)
782     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
784     // Now let's see if the file list still holds up
785     if ( needToRecodeParams )
786     {
787         fl = fixupFilenameEncoding( fl );
788     }
790     // Check the globals for filename-fixup
791     if ( needToRecodeParams )
792     {
793         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
794         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
795         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
796     }
797     else
798     {
799         if ( sp_export_png )
800             sp_export_png_utf8 = g_strdup( sp_export_png );
801         if ( sp_export_svg )
802             sp_export_svg_utf8 = g_strdup( sp_export_svg );
803         if ( sp_global_printer )
804             sp_global_printer_utf8 = g_strdup( sp_global_printer );
805     }
807     // Return the list if wanted, else free it up.
808     if ( flDest ) {
809         *flDest = fl;
810         fl = 0;
811     } else {
812         while ( fl ) {
813             g_free( fl->data );
814             fl = g_slist_remove( fl, fl->data );
815         }
816     }
817     return 0;
820 static void
821 snooper(GdkEvent *event, gpointer /*data*/) {
822     if (inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
823     {
824         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
825         switch (event->type) {
826             case GDK_MOTION_NOTIFY:
827                 if(event->motion.state & mapping) {
828                     event->motion.state|=GDK_MOD1_MASK;
829                 }
830                 break;
831             case GDK_BUTTON_PRESS:
832                 if(event->button.state & mapping) {
833                     event->button.state|=GDK_MOD1_MASK;
834                 }
835                 break;
836              case GDK_KEY_PRESS:
837                  if(event->key.state & mapping) {
838                      event->key.state|=GDK_MOD1_MASK;
839                  }
840                  break;
841         default:
842             break;
843         }
844     }
846     if (inkscape_trackalt()) {
847         // MacOS X with X11 has some problem with the default
848         // xmodmapping.  A ~/.xmodmap solution does not work reliably due
849         // to the way we package our executable in a .app that can launch
850         // X11 or use an already-running X11.  The same problem has been
851         // reported on Linux but there is no .app/X11 to get in the way
852         // of ~/.xmodmap fixes.  So we make this a preference.
853         //
854         // For some reason, Gdk senses changes in Alt (Mod1) state for
855         // many message types, but not for keystrokes!  So this ugly hack
856         // tracks what the state of Alt-pressing is, and ensures
857         // GDK_MOD1_MASK is in the event->key.state as appropriate.
858         //
859         static gboolean altL_pressed = FALSE;
860         static gboolean altR_pressed = FALSE;
861         static gboolean alt_pressed = FALSE;
862         guint get_group0_keyval(GdkEventKey* event);
863         guint keyval = 0;
864         switch (event->type) {
865         case GDK_MOTION_NOTIFY:
866             alt_pressed = TRUE && (event->motion.state & GDK_MOD1_MASK);
867             break;
868         case GDK_BUTTON_PRESS:
869             alt_pressed = TRUE && (event->button.state & GDK_MOD1_MASK);
870             break;
871         case GDK_KEY_PRESS:
872             keyval = get_group0_keyval(&event->key);
873             if (keyval == GDK_Alt_L) altL_pressed = TRUE;
874             if (keyval == GDK_Alt_R) altR_pressed = TRUE;
875             alt_pressed = alt_pressed || altL_pressed || altR_pressed;
876             alt_pressed = alt_pressed || (event->button.state & GDK_MOD1_MASK);
877             if (alt_pressed)
878                 event->key.state |= GDK_MOD1_MASK;
879             else
880                 event->key.state &= ~GDK_MOD1_MASK;
881             break;
882         case GDK_KEY_RELEASE:
883             keyval = get_group0_keyval(&event->key);
884             if (keyval == GDK_Alt_L) altL_pressed = FALSE;
885             if (keyval == GDK_Alt_R) altR_pressed = FALSE;
886             if (!altL_pressed && !altR_pressed)
887                 alt_pressed = FALSE;
888             break;
889         default:
890             break;
891         }
892         //printf("alt_pressed: %s\n", alt_pressed? "+" : "-");
893     }
895     gtk_main_do_event (event);
898 static std::vector<Glib::ustring> getDirectorySet(const gchar* userDir, const gchar* const * systemDirs) {
899     std::vector<Glib::ustring> listing;
900     listing.push_back(userDir);
901     for ( const char* const* cur = systemDirs; *cur; cur++ )
902     {
903         listing.push_back(*cur);
904     }
905     return listing;
908 int
909 sp_main_gui(int argc, char const **argv)
911     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
913     GSList *fl = NULL;
914     int retVal = sp_common_main( argc, argv, &fl );
915     g_return_val_if_fail(retVal == 0, 1);
917     // Add possible icon entry directories
918     std::vector<Glib::ustring> dataDirs = getDirectorySet( g_get_user_data_dir(),
919                                                            g_get_system_data_dirs() );
920     for (std::vector<Glib::ustring>::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
921     {
922         std::vector<Glib::ustring> listing;
923         listing.push_back(*it);
924         listing.push_back("inkscape");
925         listing.push_back("icons");
926         Glib::ustring dir = Glib::build_filename(listing);
927         gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir.c_str());
928     }
930     // Add our icon directory to the search path for icon theme lookups.
931     gchar *usericondir = profile_path("icons");
932     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), usericondir);
933     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
934     g_free(usericondir);
936     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
937     Inkscape::Debug::log_display_config();
939     // Set default window icon. Obeys the theme.
940     gtk_window_set_default_icon_name("inkscape");
941     // Do things that were previously in inkscape_gtk_stock_init().
942     sp_icon_get_phys_size(GTK_ICON_SIZE_MENU);
943     Inkscape::UI::Widget::Panel::prep();
945     gboolean create_new = TRUE;
947     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
948     inkscape_application_init(argv[0], true);
950     while (fl) {
951         if (sp_file_open((gchar *)fl->data,NULL)) {
952             create_new=FALSE;
953         }
954         fl = g_slist_remove(fl, fl->data);
955     }
956     if (create_new) {
957         sp_file_new_default();
958     }
960     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
961     main_instance.run();
963 #ifdef WIN32
964     //We might not need anything here
965     //sp_win32_finish(); <-- this is a NOP func
966 #endif
968     return 0;
971 /**
972  * Process file list
973  */
974 void sp_process_file_list(GSList *fl)
976     while (fl) {
977         const gchar *filename = (gchar *)fl->data;
979         SPDocument *doc = NULL;
980         try {
981             doc = Inkscape::Extension::open(NULL, filename);
982         } catch (Inkscape::Extension::Input::no_extension_found &e) {
983             doc = NULL;
984         } catch (Inkscape::Extension::Input::open_failed &e) {
985             doc = NULL;
986         }
988         if (doc == NULL) {
989             try {
990                 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
991             } catch (Inkscape::Extension::Input::no_extension_found &e) {
992                 doc = NULL;
993             } catch (Inkscape::Extension::Input::open_failed &e) {
994                 doc = NULL;
995             }
996         }
997         if (doc == NULL) {
998             g_warning("Specified document %s cannot be opened (does not exist or not a valid SVG file)", filename);
999         } else {
1000             if (sp_vacuum_defs) {
1001                 vacuum_document(doc);
1002             }
1003             if (sp_vacuum_defs && !sp_export_svg) {
1004                 // save under the name given in the command line
1005                 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
1006             }
1007             if (sp_global_printer) {
1008                 sp_print_document_to_file(doc, sp_global_printer);
1009             }
1010             if (sp_export_png || (sp_export_id && sp_export_use_hints)) {
1011                 sp_do_export_png(doc);
1012             }
1013             if (sp_export_svg) {
1014                 Inkscape::XML::Document *rdoc;
1015                 Inkscape::XML::Node *repr;
1016                 rdoc = sp_repr_document_new("svg:svg");
1017                 repr = rdoc->root();
1018                 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
1019                 sp_repr_save_rebased_file(repr->document(), sp_export_svg, SP_SVG_NS_URI,
1020                                           doc->base, sp_export_svg);
1021             }
1022             if (sp_export_ps) {
1023                 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
1024             }
1025             if (sp_export_eps) {
1026                 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
1027             }
1028             if (sp_export_pdf) {
1029                 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
1030             }
1031 #ifdef WIN32
1032             if (sp_export_emf) {
1033                 do_export_emf(doc, sp_export_emf, "image/x-emf");
1034             }
1035 #endif //WIN32
1036             if (sp_query_all) {
1037                 do_query_all (doc);
1038             } else if (sp_query_width || sp_query_height) {
1039                 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
1040             } else if (sp_query_x || sp_query_y) {
1041                 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
1042             }
1044             delete doc;
1045         }
1046         fl = g_slist_remove(fl, fl->data);
1047     }
1050 /**
1051  * Run the application as an interactive shell, parsing command lines from stdin
1052  * Returns -1 on error.
1053  */
1054 int sp_main_shell(char const* command_name)
1056     int retval = 0;
1058     const unsigned int buffer_size = 4096;
1059     gchar *command_line = g_strnfill(buffer_size, 0);
1060     g_strlcpy(command_line, command_name, buffer_size);
1061     gsize offset = g_strlcat(command_line, " ", buffer_size);
1062     gsize sizeLeft = buffer_size - offset;
1063     gchar *useme = command_line + offset;
1065     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
1066     fflush(stdout);
1067     char* linedata = 0;
1068     do {
1069         fprintf(stdout, ">");
1070         fflush(stdout);
1071         if ((linedata = fgets(useme, sizeLeft, stdin))) {
1072             size_t len = strlen(useme);
1073             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
1074                 fprintf(stdout, "ERROR: Command line too long\n");
1075                 // Consume rest of line
1076                 retval = -1; // If the while loop completes, this remains -1
1077                 while (fgets(useme, sizeLeft, stdin) && retval) {
1078                     len = strlen(command_line);
1079                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
1080                         retval = 0;
1081                     }
1082                 }
1083             } else {
1084                 useme[--len] = '\0';  // Strip newline
1085                 if (useme[len - 1] == '\r') {
1086                     useme[--len] = '\0';
1087                 }
1088                 if ( strcmp(useme, "quit") == 0 ) {
1089                     // Time to quit
1090                     fflush(stdout);
1091                     linedata = 0; // mark for exit
1092                 } else if ( len < 1 ) {
1093                     // blank string. Do nothing.
1094                 } else {
1095                     GError* parseError = 0;
1096                     gchar** argv = 0;
1097                     gint argc = 0;
1098                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
1099                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
1100                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
1101                         if ( ctx ) {
1102                             GSList *fl = sp_process_args(ctx);
1103                             sp_process_file_list(fl);
1104                             poptFreeContext(ctx);
1105                         } else {
1106                             retval = 1; // not sure why. But this was the previous return value
1107                         }
1108                         resetCommandlineGlobals();
1109                         g_strfreev(argv);
1110                     } else {
1111                         g_warning("Cannot parse commandline: %s", useme);
1112                     }
1113                 }
1114             }
1115         } // if (linedata...
1116     } while (linedata && (retval == 0));
1118     g_free(command_line);
1119     return retval;
1122 int sp_main_console(int argc, char const **argv)
1124     /* We are started in text mode */
1126     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1127      * in a non-Gtk environment.  Used in libnrtype's
1128      * FontInstance.cpp and FontFactory.cpp.
1129      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1130      */
1131     g_type_init();
1132     char **argv2 = const_cast<char **>(argv);
1133     gtk_init_check( &argc, &argv2 );
1134     //setlocale(LC_ALL, "");
1136     GSList *fl = NULL;
1137     int retVal = sp_common_main( argc, argv, &fl );
1138     g_return_val_if_fail(retVal == 0, 1);
1140     if (fl == NULL && !sp_shell) {
1141         g_print("Nothing to do!\n");
1142         exit(0);
1143     }
1145     inkscape_application_init(argv[0], false);
1147     if (sp_shell) {
1148         sp_main_shell(argv[0]); // Run as interactive shell
1149         exit(0);
1150     } else {
1151         sp_process_file_list(fl); // Normal command line invokation
1152     }
1154     return 0;
1157 static void
1158 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1160     SPObject *o = NULL;
1162     if (id) {
1163         o = doc->getObjectById(id);
1164         if (o) {
1165             if (!SP_IS_ITEM (o)) {
1166                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1167                 return;
1168             }
1169         } else {
1170             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1171             return;
1172         }
1173     } else {
1174         o = SP_DOCUMENT_ROOT(doc);
1175     }
1177     if (o) {
1178         sp_document_ensure_up_to_date (doc);
1179         SPItem *item = ((SPItem *) o);
1181         // "true" SVG bbox for scripting
1182         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1183         if (area) {
1184             Inkscape::SVGOStringStream os;
1185             if (extent) {
1186                 os << area->dimensions()[axis];
1187             } else {
1188                 os << area->min()[axis];
1189             }
1190             g_print ("%s", os.str().c_str());
1191         } else {
1192             g_print("0");
1193         }
1194     }
1197 static void
1198 do_query_all (SPDocument *doc)
1200     SPObject *o = NULL;
1202     o = SP_DOCUMENT_ROOT(doc);
1204     if (o) {
1205         sp_document_ensure_up_to_date (doc);
1206         do_query_all_recurse(o);
1207     }
1210 static void
1211 do_query_all_recurse (SPObject *o)
1213     SPItem *item = ((SPItem *) o);
1214     if (o->getId() && SP_IS_ITEM(item)) {
1215         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1216         if (area) {
1217             Inkscape::SVGOStringStream os;
1218             os << o->getId();
1219             os << "," << area->min()[Geom::X];
1220             os << "," << area->min()[Geom::Y];
1221             os << "," << area->dimensions()[Geom::X];
1222             os << "," << area->dimensions()[Geom::Y];
1223             g_print ("%s\n", os.str().c_str());
1224         }
1225     }
1227     SPObject *child = o->children;
1228     while (child) {
1229         do_query_all_recurse (child);
1230         child = child->next;
1231     }
1235 static void
1236 sp_do_export_png(SPDocument *doc)
1238     const gchar *filename = NULL;
1239     bool filename_from_hint = false;
1240     gdouble dpi = 0.0;
1242     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1243         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1244     }
1246     GSList *items = NULL;
1248     Geom::Rect area;
1249     if (sp_export_id || sp_export_area_drawing) {
1251         SPObject *o = NULL;
1252         SPObject *o_area = NULL;
1253         if (sp_export_id && sp_export_area_drawing) {
1254             o = doc->getObjectById(sp_export_id);
1255             o_area = SP_DOCUMENT_ROOT (doc);
1256         } else if (sp_export_id) {
1257             o = doc->getObjectById(sp_export_id);
1258             o_area = o;
1259         } else if (sp_export_area_drawing) {
1260             o = SP_DOCUMENT_ROOT (doc);
1261             o_area = o;
1262         }
1264         if (o) {
1265             if (!SP_IS_ITEM (o)) {
1266                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1267                 return;
1268             }
1270             items = g_slist_prepend (items, SP_ITEM(o));
1272             if (sp_export_id_only) {
1273                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1274             }
1276             if (sp_export_use_hints) {
1278                 // retrieve export filename hint
1279                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1280                 if (fn_hint) {
1281                     if (sp_export_png) {
1282                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1283                         filename = sp_export_png;
1284                     } else {
1285                         filename = fn_hint;
1286                         filename_from_hint = true;
1287                     }
1288                 } else {
1289                     g_warning ("Export filename hint not found for the object.");
1290                     filename = sp_export_png;
1291                 }
1293                 // retrieve export dpi hints
1294                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1295                 if (dpi_hint) {
1296                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1297                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1298                     } else {
1299                         dpi = atof(dpi_hint);
1300                     }
1301                 } else {
1302                     g_warning ("Export DPI hint not found for the object.");
1303                 }
1305             }
1307             // write object bbox to area
1308             sp_document_ensure_up_to_date (doc);
1309             Geom::OptRect areaMaybe;
1310             sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1311             if (areaMaybe) {
1312                 area = *areaMaybe;
1313             } else {
1314                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1315                 return;
1316             }
1317         } else {
1318             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1319             return;
1320         }
1321     }
1323     if (sp_export_area) {
1324         /* Try to parse area (given in SVG pixels) */
1325         gdouble x0,y0,x1,y1;
1326         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1327             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1328             return;
1329         }
1330         area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1331     } else if (sp_export_area_page || !(sp_export_id || sp_export_area_drawing)) {
1332         /* Export the whole page: note: Inkscape uses 'page' in all menus and dialogs, not 'canvas' */
1333         sp_document_ensure_up_to_date (doc);
1334         Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1335         area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1336     }
1338     // set filename and dpi from options, if not yet set from the hints
1339     if (!filename) {
1340         if (!sp_export_png) {
1341             g_warning ("No export filename given and no filename hint. Nothing exported.");
1342             return;
1343         }
1344         filename = sp_export_png;
1345     }
1347     if (sp_export_dpi && dpi == 0.0) {
1348         dpi = atof(sp_export_dpi);
1349         if ((dpi < 0.1) || (dpi > 10000.0)) {
1350             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1351             return;
1352         }
1353         g_print("DPI: %g\n", dpi);
1354     }
1356     if (sp_export_area_snap) {
1357         round_rectangle_outwards(area);
1358     }
1360     // default dpi
1361     if (dpi == 0.0) {
1362         dpi = PX_PER_IN;
1363     }
1365     unsigned long int width = 0;
1366     unsigned long int height = 0;
1368     if (sp_export_width) {
1369         errno=0;
1370         width = strtoul(sp_export_width, NULL, 0);
1371         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1372             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1373             return;
1374         }
1375         dpi = (gdouble) width * PX_PER_IN / area.width();
1376     }
1378     if (sp_export_height) {
1379         errno=0;
1380         height = strtoul(sp_export_height, NULL, 0);
1381         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1382             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1383             return;
1384         }
1385         dpi = (gdouble) height * PX_PER_IN / area.height();
1386     }
1388     if (!sp_export_width) {
1389         width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1390     }
1392     if (!sp_export_height) {
1393         height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1394     }
1396     guint32 bgcolor = 0x00000000;
1397     if (sp_export_background) {
1398         // override the page color
1399         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1400         bgcolor |= 0xff; // default is no opacity
1401     } else {
1402         // read from namedview
1403         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1404         if (nv && nv->attribute("pagecolor"))
1405             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1406         if (nv && nv->attribute("inkscape:pageopacity"))
1407             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1408     }
1410     if (sp_export_background_opacity) {
1411         // override opacity
1412         gfloat value;
1413         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1414             if (value > 1.0) {
1415                 value = CLAMP (value, 1.0f, 255.0f);
1416                 bgcolor &= (guint32) 0xffffff00;
1417                 bgcolor |= (guint32) floor(value);
1418             } else {
1419                 value = CLAMP (value, 0.0f, 1.0f);
1420                 bgcolor &= (guint32) 0xffffff00;
1421                 bgcolor |= SP_COLOR_F_TO_U(value);
1422             }
1423         }
1424     }
1426     gchar *path = 0;
1427     if (filename_from_hint) {
1428         //Make relative paths go from the document location, if possible:
1429         if (!g_path_is_absolute(filename) && doc->uri) {
1430             gchar *dirname = g_path_get_dirname(doc->uri);
1431             if (dirname) {
1432                 path = g_build_filename(dirname, filename, NULL);
1433                 g_free(dirname);
1434             }
1435         }
1436         if (!path) {
1437             path = g_strdup(filename);
1438         }
1439     } else {
1440         path = g_strdup(filename);
1441     }
1443     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1445     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);
1447     g_print("Bitmap saved as: %s\n", filename);
1449     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1450         sp_export_png_file(doc, path, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1451     } else {
1452         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1453     }
1455     g_free (path);
1456     g_slist_free (items);
1460 /**
1461  *  Perform a PDF/PS/EPS export
1462  *
1463  *  \param doc Document to export.
1464  *  \param uri URI to export to.
1465  *  \param mime MIME type to export as.
1466  */
1468 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1470     Inkscape::Extension::DB::OutputList o;
1471     Inkscape::Extension::db.get_output_list(o);
1472     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1473     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1474         i++;
1475     }
1477     if (i == o.end())
1478     {
1479         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1480         return;
1481     }
1483     if (sp_export_id) {
1484         SPObject *o = doc->getObjectById(sp_export_id);
1485         if (o == NULL) {
1486             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1487             return;
1488         }
1489         (*i)->set_param_string ("exportId", sp_export_id);
1490     } else {
1491         (*i)->set_param_string ("exportId", "");
1492     }
1494     if (sp_export_area_page && sp_export_area_drawing) {
1495         g_warning ("You cannot use --export-area-page and --export-area-drawing at the same time; only the former will take effect.");
1496         sp_export_area_drawing = false;
1497     }
1499     if (sp_export_area_drawing) {
1500         (*i)->set_param_bool ("areaDrawing", TRUE);
1501     } else {
1502         (*i)->set_param_bool ("areaDrawing", FALSE);
1503     }
1505     if (sp_export_area_page) {
1506         if (sp_export_eps) {
1507             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.");
1508         }
1509         (*i)->set_param_bool ("areaPage", TRUE);
1510     } else {
1511         (*i)->set_param_bool ("areaPage", FALSE);
1512     }
1514     if (!sp_export_area_drawing && !sp_export_area_page && !sp_export_id) {
1515         // neither is set, set page as default for ps/pdf and drawing for eps
1516         if (sp_export_eps) {
1517             try {
1518                (*i)->set_param_bool("areaDrawing", TRUE);
1519             } catch (...) {}
1520         }
1521     }
1523     if (sp_export_text_to_path) {
1524         (*i)->set_param_bool("textToPath", TRUE);
1525     } else {
1526         (*i)->set_param_bool("textToPath", FALSE);
1527     }
1529     if (sp_export_latex) {
1530         (*i)->set_param_bool("textToLaTeX", TRUE);
1531     } else {
1532         (*i)->set_param_bool("textToLaTeX", FALSE);
1533     }
1535     if (sp_export_ignore_filters) {
1536         (*i)->set_param_bool("blurToBitmap", FALSE);
1537     } else {
1538         (*i)->set_param_bool("blurToBitmap", TRUE);
1540         gdouble dpi = 90.0;
1541         if (sp_export_dpi) {
1542             dpi = atof(sp_export_dpi);
1543             if ((dpi < 1) || (dpi > 10000.0)) {
1544                 g_warning("DPI value %s out of range [1 - 10000]. Using 90 dpi instead.", sp_export_dpi);
1545                 dpi = 90;
1546             }
1547         }
1549         (*i)->set_param_int("resolution", (int) dpi);
1550     }
1552     (*i)->save(doc, uri);
1555 #ifdef WIN32
1556 /**
1557  *  Export a document to EMF
1558  *
1559  *  \param doc Document to export.
1560  *  \param uri URI to export to.
1561  *  \param mime MIME type to export as (should be "image/x-emf")
1562  */
1564 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1566     Inkscape::Extension::DB::OutputList o;
1567     Inkscape::Extension::db.get_output_list(o);
1568     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1569     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1570         i++;
1571     }
1573     if (i == o.end())
1574     {
1575         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1576         return;
1577     }
1579     (*i)->save(doc, uri);
1581 #endif //WIN32
1583 #ifdef WIN32
1584 bool replaceArgs( int& argc, char**& argv )
1586     bool worked = false;
1588 #ifdef REPLACEARGS_DEBUG
1589     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1590 #endif // REPLACEARGS_DEBUG
1592     wchar_t* line = GetCommandLineW();
1593     if ( line )
1594     {
1595 #ifdef REPLACEARGS_DEBUG
1596         {
1597             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1598             if ( utf8Line )
1599             {
1600                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1601                 {
1602                     char tmp[strlen(safe) + 32];
1603                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1604                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1605                 }
1606             }
1607         }
1608 #endif // REPLACEARGS_DEBUG
1610         int numArgs = 0;
1611         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1613 #ifdef REPLACEARGS_ANSI
1614 // test code for trying things on Win95/98/ME
1615         if ( !parsed )
1616         {
1617 #ifdef REPLACEARGS_DEBUG
1618             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1619 #endif // REPLACEARGS_DEBUG
1620             int lineLen = wcslen(line) + 1;
1621             wchar_t* lineDup = new wchar_t[lineLen];
1622             wcsncpy( lineDup, line, lineLen );
1624             int pos = 0;
1625             bool inQuotes = false;
1626             bool inWhitespace = true;
1627             std::vector<int> places;
1628             while ( lineDup[pos] )
1629             {
1630                 if ( inQuotes )
1631                 {
1632                     if ( lineDup[pos] == L'"' )
1633                     {
1634                         inQuotes = false;
1635                     }
1636                 }
1637                 else if ( lineDup[pos] == L'"' )
1638                 {
1639                     inQuotes = true;
1640                     inWhitespace = false;
1641                     places.push_back(pos);
1642                 }
1643                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1644                 {
1645                     if ( !inWhitespace )
1646                     {
1647                         inWhitespace = true;
1648                         lineDup[pos] = 0;
1649                     }
1650                 }
1651                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1652                 {
1653                     inWhitespace = false;
1654                     places.push_back(pos);
1655                 }
1656                 else
1657                 {
1658                     // consume
1659                 }
1660                 pos++;
1661             }
1662 #ifdef REPLACEARGS_DEBUG
1663             {
1664                 char tmp[256];
1665                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1666                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1667             }
1668 #endif // REPLACEARGS_DEBUG
1670             wchar_t** block = new wchar_t*[places.size()];
1671             int i = 0;
1672             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1673             {
1674                 block[i++] = &lineDup[*it];
1675             }
1676             parsed = block;
1677             numArgs = places.size();
1678         }
1679 #endif // REPLACEARGS_ANSI
1681         if ( parsed )
1682         {
1683             std::vector<wchar_t*>expandedArgs;
1684             if ( numArgs > 0 )
1685             {
1686                 expandedArgs.push_back( parsed[0] );
1687             }
1689             for ( int i1 = 1; i1 < numArgs; i1++ )
1690             {
1691                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1692                 wildcarded &= parsed[i1][0] != L'"';
1693                 wildcarded &= parsed[i1][0] != L'-';
1694                 if ( wildcarded )
1695                 {
1696 #ifdef REPLACEARGS_ANSI
1697                     WIN32_FIND_DATAA data;
1698 #else
1699                     WIN32_FIND_DATAW data;
1700 #endif // REPLACEARGS_ANSI
1702                     memset((void *)&data, 0, sizeof(data));
1704                     int baseLen = wcslen(parsed[i1]) + 2;
1705                     wchar_t* base = new wchar_t[baseLen];
1706                     wcsncpy( base, parsed[i1], baseLen );
1707                     wchar_t* last = wcsrchr( base, L'\\' );
1708                     if ( last )
1709                     {
1710                         last[1] = 0;
1711                     }
1712                     else
1713                     {
1714                         base[0] = 0;
1715                     }
1716                     baseLen = wcslen( base );
1718 #ifdef REPLACEARGS_ANSI
1719                     char target[MAX_PATH];
1720                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1721                     {
1722                         HANDLE hf = FindFirstFileA( target, &data );
1723 #else
1724                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1725 #endif // REPLACEARGS_ANSI
1726                         if ( hf != INVALID_HANDLE_VALUE )
1727                         {
1728                             BOOL found = TRUE;
1729                             do
1730                             {
1731 #ifdef REPLACEARGS_ANSI
1732                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1733                                 if ( howMany > 0 )
1734                                 {
1735                                     howMany += baseLen;
1736                                     wchar_t* tmp = new wchar_t[howMany + 1];
1737                                     wcsncpy( tmp, base, howMany + 1 );
1738                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1739                                     expandedArgs.push_back( tmp );
1740                                     found = FindNextFileA( hf, &data );
1741                                 }
1742 #else
1743                                 int howMany = wcslen(data.cFileName) + baseLen;
1744                                 wchar_t* tmp = new wchar_t[howMany + 1];
1745                                 wcsncpy( tmp, base, howMany + 1 );
1746                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1747                                 expandedArgs.push_back( tmp );
1748                                 found = FindNextFileW( hf, &data );
1749 #endif // REPLACEARGS_ANSI
1750                             } while ( found );
1752                             FindClose( hf );
1753                         }
1754                         else
1755                         {
1756                             expandedArgs.push_back( parsed[i1] );
1757                         }
1758 #ifdef REPLACEARGS_ANSI
1759                     }
1760 #endif // REPLACEARGS_ANSI
1762                     delete[] base;
1763                 }
1764                 else
1765                 {
1766                     expandedArgs.push_back( parsed[i1] );
1767                 }
1768             }
1770             {
1771                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1772                 int iz = 0;
1773                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1774                 {
1775                     block[iz++] = *it;
1776                 }
1777                 parsed = block;
1778                 numArgs = expandedArgs.size();
1779             }
1781             std::vector<gchar*> newArgs;
1782             for ( int i = 0; i < numArgs; i++ )
1783             {
1784                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1785                 if ( replacement )
1786                 {
1787 #ifdef REPLACEARGS_DEBUG
1788                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1790                     if ( safe2 )
1791                     {
1792                         {
1793                             char tmp[1024];
1794                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1795                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1796                         }
1797                         g_free( safe2 );
1798                     }
1799 #endif // REPLACEARGS_DEBUG
1801                     newArgs.push_back( replacement );
1802                 }
1803                 else
1804                 {
1805                     newArgs.push_back( blankParam );
1806                 }
1807             }
1809             // Now push our munged params to be the new argv and argc
1810             {
1811                 char** block = new char*[newArgs.size()];
1812                 int iz = 0;
1813                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1814                 {
1815                     block[iz++] = *it;
1816                 }
1817                 argv = block;
1818                 argc = newArgs.size();
1819                 worked = true;
1820             }
1821         }
1822 #ifdef REPLACEARGS_DEBUG
1823         else
1824         {
1825             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1826         }
1827 #endif // REPLACEARGS_DEBUG
1828     }
1829 #ifdef REPLACEARGS_DEBUG
1830     else
1831     {
1832         {
1833             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1834         }
1836         char* line2 = GetCommandLineA();
1837         if ( line2 )
1838         {
1839             gchar *safe = Inkscape::IO::sanitizeString(line2);
1840             {
1841                 {
1842                     char tmp[strlen(safe) + 32];
1843                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1844                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1845                 }
1846             }
1847         }
1848         else
1849         {
1850             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1851         }
1852     }
1853 #endif // REPLACEARGS_DEBUG
1855     return worked;
1857 #endif // WIN32
1859 static GSList *
1860 sp_process_args(poptContext ctx)
1862     GSList *fl = NULL;
1864     gint a;
1865     while ((a = poptGetNextOpt(ctx)) != -1) {
1866         switch (a) {
1867             case SP_ARG_FILE: {
1868                 gchar const *fn = poptGetOptArg(ctx);
1869                 if (fn != NULL) {
1870                     fl = g_slist_append(fl, g_strdup(fn));
1871                 }
1872                 break;
1873             }
1874             case SP_ARG_VERSION: {
1875                 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1876                 exit(0);
1877                 break;
1878             }
1879             case SP_ARG_EXTENSIONDIR: {
1880                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1881                 exit(0);
1882                 break;
1883             }
1884             case SP_ARG_VERB_LIST: {
1885                 // This really shouldn't go here, we should init the app.
1886                 // But, since we're just exiting in this path, there is
1887                 // no harm, and this is really a better place to put
1888                 // everything else.
1889                 Inkscape::Extension::init();
1890                 Inkscape::Verb::list();
1891                 exit(0);
1892                 break;
1893             }
1894             case SP_ARG_VERB:
1895             case SP_ARG_SELECT: {
1896                 gchar const *arg = poptGetOptArg(ctx);
1897                 if (arg != NULL) {
1898                     // printf("Adding in: %s\n", arg);
1899                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1900                 }
1901                 break;
1902             }
1903             case POPT_ERROR_BADOPT: {
1904                 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1905                 exit(1);
1906                 break;
1907             }
1908             default: {
1909                 break;
1910             }
1911         }
1912     }
1914     gchar const ** const args = poptGetArgs(ctx);
1915     if (args != NULL) {
1916         for (unsigned i = 0; args[i] != NULL; i++) {
1917             fl = g_slist_append(fl, g_strdup(args[i]));
1918         }
1919     }
1921     return fl;
1925 /*
1926   Local Variables:
1927   mode:c++
1928   c-file-style:"stroustrup"
1929   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1930   indent-tabs-mode:nil
1931   fill-column:99
1932   End:
1933 */
1934 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :