Code

SPDocument->Document
[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 #include "macros.h"
60 #include "file.h"
61 #include "document.h"
62 #include "sp-object.h"
63 #include "interface.h"
64 #include "print.h"
65 #include "color.h"
66 #include "sp-item.h"
67 #include "sp-root.h"
68 #include "unit-constants.h"
70 #include "svg/svg.h"
71 #include "svg/svg-color.h"
72 #include "svg/stringstream.h"
74 #include "inkscape-private.h"
75 #include "inkscape-version.h"
77 #include "sp-namedview.h"
78 #include "sp-guide.h"
79 #include "sp-object-repr.h"
80 #include "xml/repr.h"
82 #include "io/sys.h"
84 #include "debug/logger.h"
85 #include "debug/log-display-config.h"
87 #include "helper/png-write.h"
88 #include "helper/geom.h"
90 #include <extension/extension.h>
91 #include <extension/system.h>
92 #include <extension/db.h>
93 #include <extension/output.h>
95 #ifdef WIN32
96 //#define REPLACEARGS_ANSI
97 //#define REPLACEARGS_DEBUG
99 #include "registrytool.h"
101 #include "extension/internal/win32.h"
102 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_CANVAS,
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 #ifdef WIN32
146     SP_ARG_EXPORT_EMF,
147 #endif //WIN32
148     SP_ARG_EXPORT_TEXT_TO_PATH,
149     SP_ARG_EXPORT_IGNORE_FILTERS,
150     SP_ARG_EXTENSIONDIR,
151     SP_ARG_QUERY_X,
152     SP_ARG_QUERY_Y,
153     SP_ARG_QUERY_WIDTH,
154     SP_ARG_QUERY_HEIGHT,
155     SP_ARG_QUERY_ALL,
156     SP_ARG_QUERY_ID,
157     SP_ARG_SHELL,
158     SP_ARG_VERSION,
159     SP_ARG_VACUUM_DEFS,
160     SP_ARG_VERB_LIST,
161     SP_ARG_VERB,
162     SP_ARG_SELECT,
163     SP_ARG_LAST
164 };
166 int sp_main_gui(int argc, char const **argv);
167 int sp_main_console(int argc, char const **argv);
168 static void sp_do_export_png(Document *doc);
169 static void do_export_ps_pdf(Document* doc, gchar const* uri, char const *mime);
170 #ifdef WIN32
171 static void do_export_emf(Document* doc, gchar const* uri, char const *mime);
172 #endif //WIN32
173 static void do_query_dimension (Document *doc, bool extent, Geom::Dim2 const axis, const gchar *id);
174 static void do_query_all (Document *doc);
175 static void do_query_all_recurse (SPObject *o);
177 static gchar *sp_global_printer = NULL;
178 static gchar *sp_export_png = NULL;
179 static gchar *sp_export_dpi = NULL;
180 static gchar *sp_export_area = NULL;
181 static gboolean sp_export_area_drawing = FALSE;
182 static gboolean sp_export_area_canvas = FALSE;
183 static gchar *sp_export_width = NULL;
184 static gchar *sp_export_height = NULL;
185 static gchar *sp_export_id = NULL;
186 static gchar *sp_export_background = NULL;
187 static gchar *sp_export_background_opacity = NULL;
188 static gboolean sp_export_area_snap = FALSE;
189 static gboolean sp_export_use_hints = FALSE;
190 static gboolean sp_export_id_only = FALSE;
191 static gchar *sp_export_svg = NULL;
192 static gchar *sp_export_ps = NULL;
193 static gchar *sp_export_eps = NULL;
194 static gchar *sp_export_pdf = NULL;
195 #ifdef WIN32
196 static gchar *sp_export_emf = NULL;
197 #endif //WIN32
198 static gboolean sp_export_text_to_path = FALSE;
199 static gboolean sp_export_ignore_filters = FALSE;
200 static gboolean sp_export_font = FALSE;
201 static gboolean sp_query_x = FALSE;
202 static gboolean sp_query_y = FALSE;
203 static gboolean sp_query_width = FALSE;
204 static gboolean sp_query_height = FALSE;
205 static gboolean sp_query_all = FALSE;
206 static gchar *sp_query_id = NULL;
207 static int sp_new_gui = FALSE;
208 static gboolean sp_shell = FALSE;
209 static gboolean sp_vacuum_defs = FALSE;
211 static gchar *sp_export_png_utf8 = NULL;
212 static gchar *sp_export_svg_utf8 = NULL;
213 static gchar *sp_global_printer_utf8 = NULL;
216 /**
217  *  Reset variables to default values.
218  */
219 static void resetCommandlineGlobals() {
220         sp_global_printer = NULL;
221         sp_export_png = NULL;
222         sp_export_dpi = NULL;
223         sp_export_area = NULL;
224         sp_export_area_drawing = FALSE;
225         sp_export_area_canvas = FALSE;
226         sp_export_width = NULL;
227         sp_export_height = NULL;
228         sp_export_id = NULL;
229         sp_export_background = NULL;
230         sp_export_background_opacity = NULL;
231         sp_export_area_snap = FALSE;
232         sp_export_use_hints = FALSE;
233         sp_export_id_only = FALSE;
234         sp_export_svg = NULL;
235         sp_export_ps = NULL;
236         sp_export_eps = NULL;
237         sp_export_pdf = NULL;
238 #ifdef WIN32
239         sp_export_emf = NULL;
240 #endif //WIN32
241         sp_export_text_to_path = FALSE;
242         sp_export_ignore_filters = FALSE;
243         sp_export_font = FALSE;
244         sp_query_x = FALSE;
245         sp_query_y = FALSE;
246         sp_query_width = FALSE;
247         sp_query_height = FALSE;
248         sp_query_all = FALSE;
249         sp_query_id = NULL;
250         sp_vacuum_defs = FALSE;
252         sp_export_png_utf8 = NULL;
253         sp_export_svg_utf8 = NULL;
254         sp_global_printer_utf8 = NULL;
257 #ifdef WIN32
258 static bool replaceArgs( int& argc, char**& argv );
259 #endif
260 static GSList *sp_process_args(poptContext ctx);
261 struct poptOption options[] = {
262     {"version", 'V',
263      POPT_ARG_NONE, NULL, SP_ARG_VERSION,
264      N_("Print the Inkscape version number"),
265      NULL},
267     {"without-gui", 'z',
268      POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
269      N_("Do not use X server (only process files from console)"),
270      NULL},
272     {"with-gui", 'g',
273      POPT_ARG_NONE, NULL, SP_ARG_GUI,
274      N_("Try to use X server (even if $DISPLAY is not set)"),
275      NULL},
277     {"file", 'f',
278      POPT_ARG_STRING, NULL, SP_ARG_FILE,
279      N_("Open specified document(s) (option string may be excluded)"),
280      N_("FILENAME")},
282     {"print", 'p',
283      POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
284      N_("Print document(s) to specified output file (use '| program' for pipe)"),
285      N_("FILENAME")},
287     {"export-png", 'e',
288      POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
289      N_("Export document to a PNG file"),
290      N_("FILENAME")},
292     {"export-dpi", 'd',
293      POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
294      N_("Resolution for exporting to bitmap and for rasterization of filters in PS/EPS/PDF (default 90)"),
295      N_("DPI")},
297     {"export-area", 'a',
298      POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
299      N_("Exported area in SVG user units (default is the canvas; 0,0 is lower-left corner)"),
300      N_("x0:y0:x1:y1")},
302     {"export-area-drawing", 'D',
303      POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
304      N_("Exported area is the entire drawing (not canvas)"),
305      NULL},
307     {"export-area-canvas", 'C',
308      POPT_ARG_NONE, &sp_export_area_canvas, SP_ARG_EXPORT_AREA_CANVAS,
309      N_("Exported area is the entire canvas"),
310      NULL},
312     {"export-area-snap", 0,
313      POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
314      N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
315      NULL},
317     {"export-width", 'w',
318      POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
319      N_("The width of exported bitmap in pixels (overrides export-dpi)"),
320      N_("WIDTH")},
322     {"export-height", 'h',
323      POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
324      N_("The height of exported bitmap in pixels (overrides export-dpi)"),
325      N_("HEIGHT")},
327     {"export-id", 'i',
328      POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
329      N_("The ID of the object to export"),
330      N_("ID")},
332     {"export-id-only", 'j',
333      POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
334      // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
335      //  See "man inkscape" for details.
336      N_("Export just the object with export-id, hide all others (only with export-id)"),
337      NULL},
339     {"export-use-hints", 't',
340      POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
341      N_("Use stored filename and DPI hints when exporting (only with export-id)"),
342      NULL},
344     {"export-background", 'b',
345      POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
346      N_("Background color of exported bitmap (any SVG-supported color string)"),
347      N_("COLOR")},
349     {"export-background-opacity", 'y',
350      POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
351      N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
352      N_("VALUE")},
354     {"export-plain-svg", 'l',
355      POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
356      N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
357      N_("FILENAME")},
359     {"export-ps", 'P',
360      POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
361      N_("Export document to a PS file"),
362      N_("FILENAME")},
364     {"export-eps", 'E',
365      POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
366      N_("Export document to an EPS file"),
367      N_("FILENAME")},
369     {"export-pdf", 'A',
370      POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
371      N_("Export document to a PDF file"),
372      N_("FILENAME")},
374 #ifdef WIN32
375     {"export-emf", 'M',
376      POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
377      N_("Export document to an Enhanced Metafile (EMF) File"),
378      N_("FILENAME")},
379 #endif //WIN32
381     {"export-text-to-path", 'T',
382      POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
383      N_("Convert text object to paths on export (PS, EPS, PDF)"),
384      NULL},
386     {"export-ignore-filters", 0,
387      POPT_ARG_NONE, &sp_export_ignore_filters, SP_ARG_EXPORT_IGNORE_FILTERS,
388      N_("Render filtered objects without filters, instead of rasterizing (PS, EPS, PDF)"),
389      NULL},
391     {"query-x", 'X',
392      POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
393      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
394      N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
395      NULL},
397     {"query-y", 'Y',
398      POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
399      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
400      N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
401      NULL},
403     {"query-width", 'W',
404      POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
405      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
406      N_("Query the width of the drawing or, if specified, of the object with --query-id"),
407      NULL},
409     {"query-height", 'H',
410      POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
411      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
412      N_("Query the height of the drawing or, if specified, of the object with --query-id"),
413      NULL},
415     {"query-all", 'S',
416      POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
417      N_("List id,x,y,w,h for all objects"),
418      NULL},
420     {"query-id", 'I',
421      POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
422      N_("The ID of the object whose dimensions are queried"),
423      N_("ID")},
425     {"extension-directory", 'x',
426      POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
427      // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
428      N_("Print out the extension directory and exit"),
429      NULL},
431     {"vacuum-defs", 0,
432      POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
433      N_("Remove unused definitions from the defs section(s) of the document"),
434      NULL},
436     {"verb-list", 0,
437      POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
438      N_("List the IDs of all the verbs in Inkscape"),
439      NULL},
441     {"verb", 0,
442      POPT_ARG_STRING, NULL, SP_ARG_VERB,
443      N_("Verb to call when Inkscape opens."),
444      N_("VERB-ID")},
446     {"select", 0,
447      POPT_ARG_STRING, NULL, SP_ARG_SELECT,
448      N_("Object ID to select when Inkscape opens."),
449      N_("OBJECT-ID")},
451     {"shell", 0,
452      POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
453      N_("Start Inkscape in interactive shell mode."),
454      NULL},
456     POPT_AUTOHELP POPT_TABLEEND
457 };
459 static bool needToRecodeParams = true;
460 gchar * blankParam = g_strdup("");
464 #ifdef WIN32
466 /**
467  * Return the directory of the .exe that is currently running
468  */
469 static Glib::ustring _win32_getExePath()
471     char exeName[MAX_PATH+1];
472     // TODO these should use xxxW() calls explicitly and convert UTF-16 <--> UTF-8
473     GetModuleFileName(NULL, exeName, MAX_PATH);
474     char *slashPos = strrchr(exeName, '\\');
475     if (slashPos) {
476         *slashPos = '\0';
477     }
478     Glib::ustring s = exeName;
479     return s;
482 /**
483  * Set up the PATH and PYTHONPATH environment variables on
484  * win32
485  */
486 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
488     // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
490     char *oldenv = getenv("PATH");
491     Glib::ustring tmp = "PATH=";
492     tmp += exePath;
493     tmp += ";";
494     tmp += exePath;
495     tmp += "\\python;";
496     tmp += exePath;
497     tmp += "\\python\\Scripts;";  // for uniconv.cmd
498     tmp += exePath;
499     tmp += "\\perl";
500     if(oldenv != NULL) {
501         tmp += ";";
502         tmp += oldenv;
503     }
504     _putenv(tmp.c_str());
506     oldenv = getenv("PYTHONPATH");
507     tmp = "PYTHONPATH=";
508     tmp += exePath;
509     tmp += "\\python;";
510     tmp += exePath;
511     tmp += "\\python\\Lib;";
512     tmp += exePath;
513     tmp += "\\python\\DLLs";
514     if(oldenv != NULL) {
515         tmp += ";";
516         tmp += oldenv;
517     }
518     _putenv(tmp.c_str());
520     return 0;
522 #endif
524 /**
525  * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
526  * can find inkex.py et al. (Bug #197475)
527  */
528 static int set_extensions_env()
530     char *oldenv = getenv("PYTHONPATH");
531     Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
532     if (oldenv != NULL) {
533         tmp += G_SEARCHPATH_SEPARATOR;
534         tmp += oldenv;
535     }
536     g_setenv("PYTHONPATH", tmp.c_str(), TRUE);
538     return 0;
542 /**
543  * This is the classic main() entry point of the program, though on some
544  * architectures it might be called by something else.
545  */
546 int
547 main(int argc, char **argv)
549 #ifdef HAVE_FPSETMASK
550     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
551        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
552        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
553     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
554 #endif
556 #ifdef WIN32
557     /*
558       Set the current directory to the directory of the
559       executable.  This seems redundant, but is needed for
560       when inkscape.exe is executed from another directory.
561       We use relative paths on win32.
562       HKCR\svgfile\shell\open\command is a good example
563     */
564     Glib::ustring homedir = _win32_getExePath();
565     // TODO these should use xxxW() calls explicitly and convert UTF-16 <--> UTF-8
566     SetCurrentDirectory(homedir.c_str());
567     _win32_set_inkscape_env(homedir);
568     RegistryTool rt;
569     rt.setPathInfo();
570 #endif
572     // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
573     Gtk::Main::init_gtkmm_internals();
575     // Bug #197475
576     set_extensions_env();
578    /**
579     * Call bindtextdomain() for various machines's paths
580     */
581 #ifdef ENABLE_NLS
582 #ifdef WIN32
583     Glib::ustring localePath = homedir;
584     localePath += "\\";
585     localePath += PACKAGE_LOCALE_DIR;
586     bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
587 #else
588 #ifdef ENABLE_BINRELOC
589     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
590 #else
591     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
592 #endif
593 #endif
594     // Allow the user to override the locale directory by setting
595     // the environment variable INKSCAPE_LOCALEDIR.
596     char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
597     if (inkscape_localedir != NULL) {
598         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
599     }
600 #endif
602     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
604 #ifdef ENABLE_NLS
605     textdomain(GETTEXT_PACKAGE);
606 #endif
608     LIBXML_TEST_VERSION
610     Inkscape::GC::init();
612     Inkscape::Debug::Logger::init();
614     gboolean use_gui;
616 #ifndef WIN32
617     // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
618     use_gui = (getenv("DISPLAY") != NULL);
619 #else
620     use_gui = TRUE;
621 #endif
622     /* Test whether with/without GUI is forced */
623     for (int i = 1; i < argc; i++) {
624         if (!strcmp(argv[i], "-z")
625             || !strcmp(argv[i], "--without-gui")
626             || !strcmp(argv[i], "-p")
627             || !strncmp(argv[i], "--print", 7)
628             || !strcmp(argv[i], "-e")
629             || !strncmp(argv[i], "--export-png", 12)
630             || !strcmp(argv[i], "-l")
631             || !strncmp(argv[i], "--export-plain-svg", 12)
632             || !strcmp(argv[i], "-i")
633             || !strncmp(argv[i], "--export-area-drawing", 21)
634             || !strcmp(argv[i], "-D")
635             || !strncmp(argv[i], "--export-area-canvas", 20)
636             || !strcmp(argv[i], "-C")
637             || !strncmp(argv[i], "--export-id", 12)
638             || !strcmp(argv[i], "-P")
639             || !strncmp(argv[i], "--export-ps", 11)
640             || !strcmp(argv[i], "-E")
641             || !strncmp(argv[i], "--export-eps", 12)
642             || !strcmp(argv[i], "-A")
643             || !strncmp(argv[i], "--export-pdf", 12)
644 #ifdef WIN32
645             || !strcmp(argv[i], "-M")
646             || !strncmp(argv[i], "--export-emf", 12)
647 #endif //WIN32
648             || !strcmp(argv[i], "-W")
649             || !strncmp(argv[i], "--query-width", 13)
650             || !strcmp(argv[i], "-H")
651             || !strncmp(argv[i], "--query-height", 14)
652             || !strcmp(argv[i], "-S")
653             || !strncmp(argv[i], "--query-all", 11)
654             || !strcmp(argv[i], "-X")
655             || !strncmp(argv[i], "--query-x", 13)
656             || !strcmp(argv[i], "-Y")
657             || !strncmp(argv[i], "--query-y", 14)
658             || !strcmp(argv[i], "--vacuum-defs")
659             || !strncmp(argv[i], "--shell", 7)
660            )
661         {
662             /* main_console handles any exports -- not the gui */
663             use_gui = FALSE;
664             break;
665         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
666             use_gui = TRUE;
667             break;
668         }
669     }
671 #ifdef WIN32
672 #ifndef REPLACEARGS_ANSI
673     if ( PrintWin32::is_os_wide() )
674 #endif // REPLACEARGS_ANSI
675     {
676         // If the call fails, we'll need to convert charsets
677         needToRecodeParams = !replaceArgs( argc, argv );
678     }
679 #endif // WIN32
681     /// \todo  Should this be a static object (see inkscape.cpp)?
682     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
684     return app.run();
690 void fixupSingleFilename( gchar **orig, gchar **spare )
692     if ( orig && *orig && **orig ) {
693         GError *error = NULL;
694         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
695         if ( newFileName )
696         {
697             *orig = newFileName;
698             if ( spare ) {
699                 *spare = newFileName;
700             }
701 //             g_message("Set a replacement fixup");
702         }
703     }
708 GSList *fixupFilenameEncoding( GSList* fl )
710     GSList *newFl = NULL;
711     while ( fl ) {
712         gchar *fn = static_cast<gchar*>(fl->data);
713         fl = g_slist_remove( fl, fl->data );
714         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
715         if ( newFileName ) {
717             if ( 0 )
718             {
719                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
720                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
721                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
722                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
723                 gtk_dialog_run (GTK_DIALOG (w));
724                 gtk_widget_destroy (w);
725                 g_free(safeNewFn);
726                 g_free(safeFn);
727             }
729             g_free( fn );
730             fn = newFileName;
731             newFileName = 0;
732         }
733         else
734             if ( 0 )
735         {
736             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
737             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
738             gtk_dialog_run (GTK_DIALOG (w));
739             gtk_widget_destroy (w);
740             g_free(safeFn);
741         }
742         newFl = g_slist_append( newFl, fn );
743     }
744     return newFl;
747 int sp_common_main( int argc, char const **argv, GSList **flDest )
749     /// \todo fixme: Move these to some centralized location (Lauris)
750     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
751     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
754     // temporarily switch gettext encoding to locale, so that help messages can be output properly
755     gchar const *charset;
756     g_get_charset(&charset);
758     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
760     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
761     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
762     g_return_val_if_fail(ctx != NULL, 1);
764     /* Collect own arguments */
765     GSList *fl = sp_process_args(ctx);
766     poptFreeContext(ctx);
768     // now switch gettext back to UTF-8 (for GUI)
769     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
771     // Now let's see if the file list still holds up
772     if ( needToRecodeParams )
773     {
774         fl = fixupFilenameEncoding( fl );
775     }
777     // Check the globals for filename-fixup
778     if ( needToRecodeParams )
779     {
780         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
781         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
782         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
783     }
784     else
785     {
786         if ( sp_export_png )
787             sp_export_png_utf8 = g_strdup( sp_export_png );
788         if ( sp_export_svg )
789             sp_export_svg_utf8 = g_strdup( sp_export_svg );
790         if ( sp_global_printer )
791             sp_global_printer_utf8 = g_strdup( sp_global_printer );
792     }
794     // Return the list if wanted, else free it up.
795     if ( flDest ) {
796         *flDest = fl;
797         fl = 0;
798     } else {
799         while ( fl ) {
800             g_free( fl->data );
801             fl = g_slist_remove( fl, fl->data );
802         }
803     }
804     return 0;
807 static void
808 snooper(GdkEvent *event, gpointer /*data*/) {
809     if (inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
810     {
811         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
812         switch (event->type) {
813             case GDK_MOTION_NOTIFY:
814                 if(event->motion.state & mapping) {
815                     event->motion.state|=GDK_MOD1_MASK;
816                 }
817                 break;
818             case GDK_BUTTON_PRESS:
819                 if(event->button.state & mapping) {
820                     event->button.state|=GDK_MOD1_MASK;
821                 }
822                 break;
823              case GDK_KEY_PRESS:
824                  if(event->key.state & mapping) {
825                      event->key.state|=GDK_MOD1_MASK;
826                  }
827                  break;
828         default:
829             break;
830         }
831     }
833     if (inkscape_trackalt()) {
834         // MacOS X with X11 has some problem with the default
835         // xmodmapping.  A ~/.xmodmap solution does not work reliably due
836         // to the way we package our executable in a .app that can launch
837         // X11 or use an already-running X11.  The same problem has been
838         // reported on Linux but there is no .app/X11 to get in the way
839         // of ~/.xmodmap fixes.  So we make this a preference.
840         //
841         // For some reason, Gdk senses changes in Alt (Mod1) state for
842         // many message types, but not for keystrokes!  So this ugly hack
843         // tracks what the state of Alt-pressing is, and ensures
844         // GDK_MOD1_MASK is in the event->key.state as appropriate.
845         //
846         static gboolean altL_pressed = FALSE;
847         static gboolean altR_pressed = FALSE;
848         static gboolean alt_pressed = FALSE;
849         guint get_group0_keyval(GdkEventKey* event);
850         guint keyval = 0;
851         switch (event->type) {
852         case GDK_MOTION_NOTIFY:
853             alt_pressed = TRUE && (event->motion.state & GDK_MOD1_MASK);
854             break;
855         case GDK_BUTTON_PRESS:
856             alt_pressed = TRUE && (event->button.state & GDK_MOD1_MASK);
857             break;
858         case GDK_KEY_PRESS:
859             keyval = get_group0_keyval(&event->key);
860             if (keyval == GDK_Alt_L) altL_pressed = TRUE;
861             if (keyval == GDK_Alt_R) altR_pressed = TRUE;
862             alt_pressed = alt_pressed || altL_pressed || altR_pressed;
863             alt_pressed = alt_pressed || (event->button.state & GDK_MOD1_MASK);
864             if (alt_pressed)
865                 event->key.state |= GDK_MOD1_MASK;
866             else
867                 event->key.state &= ~GDK_MOD1_MASK;
868             break;
869         case GDK_KEY_RELEASE:
870             keyval = get_group0_keyval(&event->key);
871             if (keyval == GDK_Alt_L) altL_pressed = FALSE;
872             if (keyval == GDK_Alt_R) altR_pressed = FALSE;
873             if (!altL_pressed && !altR_pressed)
874                 alt_pressed = FALSE;
875             break;
876         default:
877             break;
878         }
879         //printf("alt_pressed: %s\n", alt_pressed? "+" : "-");
880     }
882     gtk_main_do_event (event);
885 static std::vector<Glib::ustring> getDirectorySet(const gchar* userDir, const gchar* const * systemDirs) {
886     std::vector<Glib::ustring> listing;
887     listing.push_back(userDir);
888     for ( const char* const* cur = systemDirs; *cur; cur++ )
889     {
890         listing.push_back(*cur);
891     }
892     return listing;
895 int
896 sp_main_gui(int argc, char const **argv)
898     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
900     GSList *fl = NULL;
901     int retVal = sp_common_main( argc, argv, &fl );
902     g_return_val_if_fail(retVal == 0, 1);
904     // Add possible icon entry directories
905     std::vector<Glib::ustring> dataDirs = getDirectorySet( g_get_user_data_dir(),
906                                                            g_get_system_data_dirs() );
907     for (std::vector<Glib::ustring>::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
908     {
909         std::vector<Glib::ustring> listing;
910         listing.push_back(*it);
911         listing.push_back("inkscape");
912         listing.push_back("icons");
913         Glib::ustring dir = Glib::build_filename(listing);
914         gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir.c_str());
915     }
917     // Add our icon directory to the search path for icon theme lookups.
918     gchar *usericondir = profile_path("icons");
919     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), usericondir);
920     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
921     g_free(usericondir);
923     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
924     Inkscape::Debug::log_display_config();
926     // Set default window icon. Obeys the theme.
927     gtk_window_set_default_icon_name("inkscape");
928     // Do things that were previously in inkscape_gtk_stock_init().
929     sp_icon_get_phys_size(GTK_ICON_SIZE_MENU);
930     Inkscape::UI::Widget::Panel::prep();
932     gboolean create_new = TRUE;
934     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
935     inkscape_application_init(argv[0], true);
937     while (fl) {
938         if (sp_file_open((gchar *)fl->data,NULL)) {
939             create_new=FALSE;
940         }
941         fl = g_slist_remove(fl, fl->data);
942     }
943     if (create_new) {
944         sp_file_new_default();
945     }
947     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
948     main_instance.run();
950 #ifdef WIN32
951     //We might not need anything here
952     //sp_win32_finish(); <-- this is a NOP func
953 #endif
955     return 0;
958 /**
959  * Process file list
960  */
961 void sp_process_file_list(GSList *fl)
963     while (fl) {
964         const gchar *filename = (gchar *)fl->data;
965         Document *doc = Inkscape::Extension::open(NULL, filename);
966         if (doc == NULL) {
967             doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
968         }
969         if (doc == NULL) {
970             g_warning("Specified document %s cannot be opened (is it a valid SVG file?)", filename);
971         } else {
972             if (sp_vacuum_defs) {
973                 vacuum_document(doc);
974             }
975             if (sp_vacuum_defs && !sp_export_svg) {
976                 // save under the name given in the command line
977                 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
978             }
979             if (sp_global_printer) {
980                 sp_print_document_to_file(doc, sp_global_printer);
981             }
982             if (sp_export_png) {
983                 sp_do_export_png(doc);
984             }
985             if (sp_export_svg) {
986                 Inkscape::XML::Document *rdoc;
987                 Inkscape::XML::Node *repr;
988                 rdoc = sp_repr_document_new("svg:svg");
989                 repr = rdoc->root();
990                 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
991                 sp_repr_save_rebased_file(repr->document(), sp_export_svg, SP_SVG_NS_URI,
992                                           doc->base, sp_export_svg);
993             }
994             if (sp_export_ps) {
995                 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
996             }
997             if (sp_export_eps) {
998                 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
999             }
1000             if (sp_export_pdf) {
1001                 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
1002             }
1003 #ifdef WIN32
1004             if (sp_export_emf) {
1005                 do_export_emf(doc, sp_export_emf, "image/x-emf");
1006             }
1007 #endif //WIN32
1008             if (sp_query_all) {
1009                 do_query_all (doc);
1010             } else if (sp_query_width || sp_query_height) {
1011                 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
1012             } else if (sp_query_x || sp_query_y) {
1013                 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
1014             }
1016             delete doc;
1017         }
1018         fl = g_slist_remove(fl, fl->data);
1019     }
1022 /**
1023  * Run the application as an interactive shell, parsing command lines from stdin
1024  * Returns -1 on error.
1025  */
1026 int sp_main_shell(char const* command_name)
1028     int retval = 0;
1030     const unsigned int buffer_size = 4096;
1031     gchar *command_line = g_strnfill(buffer_size, 0);
1032     g_strlcpy(command_line, command_name, buffer_size);
1033     gsize offset = g_strlcat(command_line, " ", buffer_size);
1034     gsize sizeLeft = buffer_size - offset;
1035     gchar *useme = command_line + offset;
1037     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
1038     fflush(stdout);
1039     char* linedata = 0;
1040     do {
1041         fprintf(stdout, ">");
1042         fflush(stdout);
1043         if ((linedata = fgets(useme, sizeLeft, stdin))) {
1044             size_t len = strlen(useme);
1045             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
1046                 fprintf(stdout, "ERROR: Command line too long\n");
1047                 // Consume rest of line
1048                 retval = -1; // If the while loop completes, this remains -1
1049                 while (fgets(useme, sizeLeft, stdin) && retval) {
1050                     len = strlen(command_line);
1051                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
1052                         retval = 0;
1053                     }
1054                 }
1055             } else {
1056                 useme[--len] = '\0';  // Strip newline
1057                 if (useme[len - 1] == '\r') {
1058                     useme[--len] = '\0';
1059                 }
1060                 if ( strcmp(useme, "quit") == 0 ) {
1061                     // Time to quit
1062                     fflush(stdout);
1063                     linedata = 0; // mark for exit
1064                 } else if ( len < 1 ) {
1065                     // blank string. Do nothing.
1066                 } else {
1067                     GError* parseError = 0;
1068                     gchar** argv = 0;
1069                     gint argc = 0;
1070                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
1071                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
1072                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
1073                         if ( ctx ) {
1074                             GSList *fl = sp_process_args(ctx);
1075                             sp_process_file_list(fl);
1076                             poptFreeContext(ctx);
1077                         } else {
1078                             retval = 1; // not sure why. But this was the previous return value
1079                         }
1080                         resetCommandlineGlobals();
1081                         g_strfreev(argv);
1082                     } else {
1083                         g_warning("Cannot parse commandline: %s", useme);
1084                     }
1085                 }
1086             }
1087         } // if (linedata...
1088     } while (linedata && (retval == 0));
1090     g_free(command_line);
1091     return retval;
1094 int sp_main_console(int argc, char const **argv)
1096     /* We are started in text mode */
1098     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1099      * in a non-Gtk environment.  Used in libnrtype's
1100      * FontInstance.cpp and FontFactory.cpp.
1101      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1102      */
1103     g_type_init();
1104     char **argv2 = const_cast<char **>(argv);
1105     gtk_init_check( &argc, &argv2 );
1106     //setlocale(LC_ALL, "");
1108     GSList *fl = NULL;
1109     int retVal = sp_common_main( argc, argv, &fl );
1110     g_return_val_if_fail(retVal == 0, 1);
1112     if (fl == NULL && !sp_shell) {
1113         g_print("Nothing to do!\n");
1114         exit(0);
1115     }
1117     inkscape_application_init(argv[0], false);
1119     if (sp_shell) {
1120         sp_main_shell(argv[0]); // Run as interactive shell
1121         exit(0);
1122     } else {
1123         sp_process_file_list(fl); // Normal command line invokation
1124     }
1126     return 0;
1129 static void
1130 do_query_dimension (Document *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1132     SPObject *o = NULL;
1134     if (id) {
1135         o = doc->getObjectById(id);
1136         if (o) {
1137             if (!SP_IS_ITEM (o)) {
1138                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1139                 return;
1140             }
1141         } else {
1142             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1143             return;
1144         }
1145     } else {
1146         o = SP_DOCUMENT_ROOT(doc);
1147     }
1149     if (o) {
1150         sp_document_ensure_up_to_date (doc);
1151         SPItem *item = ((SPItem *) o);
1153         // "true" SVG bbox for scripting
1154         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1155         if (area) {
1156             Inkscape::SVGOStringStream os;
1157             if (extent) {
1158                 os << area->dimensions()[axis];
1159             } else {
1160                 os << area->min()[axis];
1161             }
1162             g_print ("%s", os.str().c_str());
1163         } else {
1164             g_print("0");
1165         }
1166     }
1169 static void
1170 do_query_all (Document *doc)
1172     SPObject *o = NULL;
1174     o = SP_DOCUMENT_ROOT(doc);
1176     if (o) {
1177         sp_document_ensure_up_to_date (doc);
1178         do_query_all_recurse(o);
1179     }
1182 static void
1183 do_query_all_recurse (SPObject *o)
1185     SPItem *item = ((SPItem *) o);
1186     if (o->id && SP_IS_ITEM(item)) {
1187         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1188         if (area) {
1189             Inkscape::SVGOStringStream os;
1190             os << o->id;
1191             os << "," << area->min()[Geom::X];
1192             os << "," << area->min()[Geom::Y];
1193             os << "," << area->dimensions()[Geom::X];
1194             os << "," << area->dimensions()[Geom::Y];
1195             g_print ("%s\n", os.str().c_str());
1196         }
1197     }
1199     SPObject *child = o->children;
1200     while (child) {
1201         do_query_all_recurse (child);
1202         child = child->next;
1203     }
1207 static void
1208 sp_do_export_png(Document *doc)
1210     const gchar *filename = NULL;
1211     gdouble dpi = 0.0;
1213     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1214         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1215     }
1217     GSList *items = NULL;
1219     Geom::Rect area;
1220     if (sp_export_id || sp_export_area_drawing) {
1222         SPObject *o = NULL;
1223         SPObject *o_area = NULL;
1224         if (sp_export_id && sp_export_area_drawing) {
1225             o = doc->getObjectById(sp_export_id);
1226             o_area = SP_DOCUMENT_ROOT (doc);
1227         } else if (sp_export_id) {
1228             o = doc->getObjectById(sp_export_id);
1229             o_area = o;
1230         } else if (sp_export_area_drawing) {
1231             o = SP_DOCUMENT_ROOT (doc);
1232             o_area = o;
1233         }
1235         if (o) {
1236             if (!SP_IS_ITEM (o)) {
1237                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1238                 return;
1239             }
1241             items = g_slist_prepend (items, SP_ITEM(o));
1243             if (sp_export_id_only) {
1244                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1245             }
1247             if (sp_export_use_hints) {
1249                 // retrieve export filename hint
1250                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1251                 if (fn_hint) {
1252                     if (sp_export_png) {
1253                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1254                         filename = sp_export_png;
1255                     } else {
1256                         filename = fn_hint;
1257                     }
1258                 } else {
1259                     g_warning ("Export filename hint not found for the object.");
1260                     filename = sp_export_png;
1261                 }
1263                 // retrieve export dpi hints
1264                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1265                 if (dpi_hint) {
1266                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1267                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1268                     } else {
1269                         dpi = atof(dpi_hint);
1270                     }
1271                 } else {
1272                     g_warning ("Export DPI hint not found for the object.");
1273                 }
1275             }
1277             // write object bbox to area
1278             sp_document_ensure_up_to_date (doc);
1279             Geom::OptRect areaMaybe;
1280             sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1281             if (areaMaybe) {
1282                 area = *areaMaybe;
1283             } else {
1284                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1285                 return;
1286             }
1287         } else {
1288             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1289             return;
1290         }
1291     }
1293     if (sp_export_area) {
1294         /* Try to parse area (given in SVG pixels) */
1295         gdouble x0,y0,x1,y1;
1296         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1297             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1298             return;
1299         }
1300         area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1301     } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1302         /* Export the whole canvas */
1303         sp_document_ensure_up_to_date (doc);
1304         Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1305         area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1306     }
1308     // set filename and dpi from options, if not yet set from the hints
1309     if (!filename) {
1310         if (!sp_export_png) {
1311             g_warning ("No export filename given and no filename hint. Nothing exported.");
1312             return;
1313         }
1314         filename = sp_export_png;
1315     }
1317     if (sp_export_dpi && dpi == 0.0) {
1318         dpi = atof(sp_export_dpi);
1319         if ((dpi < 0.1) || (dpi > 10000.0)) {
1320             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1321             return;
1322         }
1323         g_print("DPI: %g\n", dpi);
1324     }
1326     if (sp_export_area_snap) {
1327         round_rectangle_outwards(area);
1328     }
1330     // default dpi
1331     if (dpi == 0.0) {
1332         dpi = PX_PER_IN;
1333     }
1335     unsigned long int width = 0;
1336     unsigned long int height = 0;
1338     if (sp_export_width) {
1339         errno=0;
1340         width = strtoul(sp_export_width, NULL, 0);
1341         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1342             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1343             return;
1344         }
1345         dpi = (gdouble) width * PX_PER_IN / area.width();
1346     }
1348     if (sp_export_height) {
1349         errno=0;
1350         height = strtoul(sp_export_height, NULL, 0);
1351         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1352             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1353             return;
1354         }
1355         dpi = (gdouble) height * PX_PER_IN / area.height();
1356     }
1358     if (!sp_export_width) {
1359         width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1360     }
1362     if (!sp_export_height) {
1363         height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1364     }
1366     guint32 bgcolor = 0x00000000;
1367     if (sp_export_background) {
1368         // override the page color
1369         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1370         bgcolor |= 0xff; // default is no opacity
1371     } else {
1372         // read from namedview
1373         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1374         if (nv && nv->attribute("pagecolor"))
1375             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1376         if (nv && nv->attribute("inkscape:pageopacity"))
1377             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1378     }
1380     if (sp_export_background_opacity) {
1381         // override opacity
1382         gfloat value;
1383         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1384             if (value > 1.0) {
1385                 value = CLAMP (value, 1.0f, 255.0f);
1386                 bgcolor &= (guint32) 0xffffff00;
1387                 bgcolor |= (guint32) floor(value);
1388             } else {
1389                 value = CLAMP (value, 0.0f, 1.0f);
1390                 bgcolor &= (guint32) 0xffffff00;
1391                 bgcolor |= SP_COLOR_F_TO_U(value);
1392             }
1393         }
1394     }
1396     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1398     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);
1400     g_print("Bitmap saved as: %s\n", filename);
1402     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1403         sp_export_png_file(doc, filename, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1404     } else {
1405         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1406     }
1408     g_slist_free (items);
1412 /**
1413  *  Perform a PDF/PS/EPS export
1414  *
1415  *  \param doc Document to export.
1416  *  \param uri URI to export to.
1417  *  \param mime MIME type to export as.
1418  */
1420 static void do_export_ps_pdf(Document* doc, gchar const* uri, char const* mime)
1422     Inkscape::Extension::DB::OutputList o;
1423     Inkscape::Extension::db.get_output_list(o);
1424     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1425     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1426         i++;
1427     }
1429     if (i == o.end())
1430     {
1431         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1432         return;
1433     }
1435     if (sp_export_id) {
1436         SPObject *o = doc->getObjectById(sp_export_id);
1437         if (o == NULL) {
1438             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1439             return;
1440         }
1441         (*i)->set_param_string ("exportId", sp_export_id);
1442     } else {
1443         (*i)->set_param_string ("exportId", "");
1444     }
1446     if (sp_export_area_canvas && sp_export_area_drawing) {
1447         g_warning ("You cannot use --export-area-canvas and --export-area-drawing at the same time; only the former will take effect.");
1448         sp_export_area_drawing = false;
1449     }
1451     if (sp_export_area_drawing) {
1452         (*i)->set_param_bool ("areaDrawing", TRUE);
1453     } else {
1454         (*i)->set_param_bool ("areaDrawing", FALSE);
1455     }
1457     if (sp_export_area_canvas) {
1458         if (sp_export_eps) {
1459             g_warning ("EPS cannot have its bounding box extend beyond its content, so if your drawing is smaller than the canvas, --export-area-canvas will clip it to drawing.");
1460         }
1461         (*i)->set_param_bool ("areaCanvas", TRUE);
1462     } else {
1463         (*i)->set_param_bool ("areaCanvas", FALSE);
1464     }
1466     if (!sp_export_area_drawing && !sp_export_area_canvas && !sp_export_id) {
1467         // neither is set, set canvas as default for ps/pdf and drawing for eps
1468         if (sp_export_eps) {
1469             try {
1470                (*i)->set_param_bool("areaDrawing", TRUE);
1471             } catch (...) {}
1472         }
1473     }
1475     if (sp_export_text_to_path) {
1476         (*i)->set_param_bool("textToPath", TRUE);
1477     } else {
1478         (*i)->set_param_bool("textToPath", FALSE);
1479     }
1481     if (sp_export_ignore_filters) {
1482         (*i)->set_param_bool("blurToBitmap", FALSE);
1483     } else {
1484         (*i)->set_param_bool("blurToBitmap", TRUE);
1486         gdouble dpi = 90.0;
1487         if (sp_export_dpi) {
1488             dpi = atof(sp_export_dpi);
1489             if ((dpi < 1) || (dpi > 10000.0)) {
1490                 g_warning("DPI value %s out of range [1 - 10000]. Using 90 dpi instead.", sp_export_dpi);
1491                 dpi = 90;
1492             }
1493         }
1495         (*i)->set_param_int("resolution", (int) dpi);
1496     }
1498     (*i)->save(doc, uri);
1501 #ifdef WIN32
1502 /**
1503  *  Export a document to EMF
1504  *
1505  *  \param doc Document to export.
1506  *  \param uri URI to export to.
1507  *  \param mime MIME type to export as (should be "image/x-emf")
1508  */
1510 static void do_export_emf(Document* doc, gchar const* uri, char const* mime)
1512     Inkscape::Extension::DB::OutputList o;
1513     Inkscape::Extension::db.get_output_list(o);
1514     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1515     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1516         i++;
1517     }
1519     if (i == o.end())
1520     {
1521         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1522         return;
1523     }
1525     (*i)->save(doc, uri);
1527 #endif //WIN32
1529 #ifdef WIN32
1530 bool replaceArgs( int& argc, char**& argv )
1532     bool worked = false;
1534 #ifdef REPLACEARGS_DEBUG
1535     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1536 #endif // REPLACEARGS_DEBUG
1538     wchar_t* line = GetCommandLineW();
1539     if ( line )
1540     {
1541 #ifdef REPLACEARGS_DEBUG
1542         {
1543             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1544             if ( utf8Line )
1545             {
1546                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1547                 {
1548                     char tmp[strlen(safe) + 32];
1549                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1550                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1551                 }
1552             }
1553         }
1554 #endif // REPLACEARGS_DEBUG
1556         int numArgs = 0;
1557         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1559 #ifdef REPLACEARGS_ANSI
1560 // test code for trying things on Win95/98/ME
1561         if ( !parsed )
1562         {
1563 #ifdef REPLACEARGS_DEBUG
1564             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1565 #endif // REPLACEARGS_DEBUG
1566             int lineLen = wcslen(line) + 1;
1567             wchar_t* lineDup = new wchar_t[lineLen];
1568             wcsncpy( lineDup, line, lineLen );
1570             int pos = 0;
1571             bool inQuotes = false;
1572             bool inWhitespace = true;
1573             std::vector<int> places;
1574             while ( lineDup[pos] )
1575             {
1576                 if ( inQuotes )
1577                 {
1578                     if ( lineDup[pos] == L'"' )
1579                     {
1580                         inQuotes = false;
1581                     }
1582                 }
1583                 else if ( lineDup[pos] == L'"' )
1584                 {
1585                     inQuotes = true;
1586                     inWhitespace = false;
1587                     places.push_back(pos);
1588                 }
1589                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1590                 {
1591                     if ( !inWhitespace )
1592                     {
1593                         inWhitespace = true;
1594                         lineDup[pos] = 0;
1595                     }
1596                 }
1597                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1598                 {
1599                     inWhitespace = false;
1600                     places.push_back(pos);
1601                 }
1602                 else
1603                 {
1604                     // consume
1605                 }
1606                 pos++;
1607             }
1608 #ifdef REPLACEARGS_DEBUG
1609             {
1610                 char tmp[256];
1611                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1612                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1613             }
1614 #endif // REPLACEARGS_DEBUG
1616             wchar_t** block = new wchar_t*[places.size()];
1617             int i = 0;
1618             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1619             {
1620                 block[i++] = &lineDup[*it];
1621             }
1622             parsed = block;
1623             numArgs = places.size();
1624         }
1625 #endif // REPLACEARGS_ANSI
1627         if ( parsed )
1628         {
1629             std::vector<wchar_t*>expandedArgs;
1630             if ( numArgs > 0 )
1631             {
1632                 expandedArgs.push_back( parsed[0] );
1633             }
1635             for ( int i1 = 1; i1 < numArgs; i1++ )
1636             {
1637                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1638                 wildcarded &= parsed[i1][0] != L'"';
1639                 wildcarded &= parsed[i1][0] != L'-';
1640                 if ( wildcarded )
1641                 {
1642 #ifdef REPLACEARGS_ANSI
1643                     WIN32_FIND_DATAA data;
1644 #else
1645                     WIN32_FIND_DATAW data;
1646 #endif // REPLACEARGS_ANSI
1648                     memset((void *)&data, 0, sizeof(data));
1650                     int baseLen = wcslen(parsed[i1]) + 2;
1651                     wchar_t* base = new wchar_t[baseLen];
1652                     wcsncpy( base, parsed[i1], baseLen );
1653                     wchar_t* last = wcsrchr( base, L'\\' );
1654                     if ( last )
1655                     {
1656                         last[1] = 0;
1657                     }
1658                     else
1659                     {
1660                         base[0] = 0;
1661                     }
1662                     baseLen = wcslen( base );
1664 #ifdef REPLACEARGS_ANSI
1665                     char target[MAX_PATH];
1666                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1667                     {
1668                         HANDLE hf = FindFirstFileA( target, &data );
1669 #else
1670                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1671 #endif // REPLACEARGS_ANSI
1672                         if ( hf != INVALID_HANDLE_VALUE )
1673                         {
1674                             BOOL found = TRUE;
1675                             do
1676                             {
1677 #ifdef REPLACEARGS_ANSI
1678                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1679                                 if ( howMany > 0 )
1680                                 {
1681                                     howMany += baseLen;
1682                                     wchar_t* tmp = new wchar_t[howMany + 1];
1683                                     wcsncpy( tmp, base, howMany + 1 );
1684                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1685                                     expandedArgs.push_back( tmp );
1686                                     found = FindNextFileA( hf, &data );
1687                                 }
1688 #else
1689                                 int howMany = wcslen(data.cFileName) + baseLen;
1690                                 wchar_t* tmp = new wchar_t[howMany + 1];
1691                                 wcsncpy( tmp, base, howMany + 1 );
1692                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1693                                 expandedArgs.push_back( tmp );
1694                                 found = FindNextFileW( hf, &data );
1695 #endif // REPLACEARGS_ANSI
1696                             } while ( found );
1698                             FindClose( hf );
1699                         }
1700                         else
1701                         {
1702                             expandedArgs.push_back( parsed[i1] );
1703                         }
1704 #ifdef REPLACEARGS_ANSI
1705                     }
1706 #endif // REPLACEARGS_ANSI
1708                     delete[] base;
1709                 }
1710                 else
1711                 {
1712                     expandedArgs.push_back( parsed[i1] );
1713                 }
1714             }
1716             {
1717                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1718                 int iz = 0;
1719                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1720                 {
1721                     block[iz++] = *it;
1722                 }
1723                 parsed = block;
1724                 numArgs = expandedArgs.size();
1725             }
1727             std::vector<gchar*> newArgs;
1728             for ( int i = 0; i < numArgs; i++ )
1729             {
1730                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1731                 if ( replacement )
1732                 {
1733 #ifdef REPLACEARGS_DEBUG
1734                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1736                     if ( safe2 )
1737                     {
1738                         {
1739                             char tmp[1024];
1740                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1741                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1742                         }
1743                         g_free( safe2 );
1744                     }
1745 #endif // REPLACEARGS_DEBUG
1747                     newArgs.push_back( replacement );
1748                 }
1749                 else
1750                 {
1751                     newArgs.push_back( blankParam );
1752                 }
1753             }
1755             // Now push our munged params to be the new argv and argc
1756             {
1757                 char** block = new char*[newArgs.size()];
1758                 int iz = 0;
1759                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1760                 {
1761                     block[iz++] = *it;
1762                 }
1763                 argv = block;
1764                 argc = newArgs.size();
1765                 worked = true;
1766             }
1767         }
1768 #ifdef REPLACEARGS_DEBUG
1769         else
1770         {
1771             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1772         }
1773 #endif // REPLACEARGS_DEBUG
1774     }
1775 #ifdef REPLACEARGS_DEBUG
1776     else
1777     {
1778         {
1779             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1780         }
1782         char* line2 = GetCommandLineA();
1783         if ( line2 )
1784         {
1785             gchar *safe = Inkscape::IO::sanitizeString(line2);
1786             {
1787                 {
1788                     char tmp[strlen(safe) + 32];
1789                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1790                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1791                 }
1792             }
1793         }
1794         else
1795         {
1796             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1797         }
1798     }
1799 #endif // REPLACEARGS_DEBUG
1801     return worked;
1803 #endif // WIN32
1805 static GSList *
1806 sp_process_args(poptContext ctx)
1808     GSList *fl = NULL;
1810     gint a;
1811     while ((a = poptGetNextOpt(ctx)) != -1) {
1812         switch (a) {
1813             case SP_ARG_FILE: {
1814                 gchar const *fn = poptGetOptArg(ctx);
1815                 if (fn != NULL) {
1816                     fl = g_slist_append(fl, g_strdup(fn));
1817                 }
1818                 break;
1819             }
1820             case SP_ARG_VERSION: {
1821                 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1822                 exit(0);
1823                 break;
1824             }
1825             case SP_ARG_EXTENSIONDIR: {
1826                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1827                 exit(0);
1828                 break;
1829             }
1830             case SP_ARG_VERB_LIST: {
1831                 // This really shouldn't go here, we should init the app.
1832                 // But, since we're just exiting in this path, there is
1833                 // no harm, and this is really a better place to put
1834                 // everything else.
1835                 Inkscape::Extension::init();
1836                 Inkscape::Verb::list();
1837                 exit(0);
1838                 break;
1839             }
1840             case SP_ARG_VERB:
1841             case SP_ARG_SELECT: {
1842                 gchar const *arg = poptGetOptArg(ctx);
1843                 if (arg != NULL) {
1844                     // printf("Adding in: %s\n", arg);
1845                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1846                 }
1847                 break;
1848             }
1849             case POPT_ERROR_BADOPT: {
1850                 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1851                 exit(1);
1852                 break;
1853             }
1854             default: {
1855                 break;
1856             }
1857         }
1858     }
1860     gchar const ** const args = poptGetArgs(ctx);
1861     if (args != NULL) {
1862         for (unsigned i = 0; args[i] != NULL; i++) {
1863             fl = g_slist_append(fl, g_strdup(args[i]));
1864         }
1865     }
1867     return fl;
1871 /*
1872   Local Variables:
1873   mode:c++
1874   c-file-style:"stroustrup"
1875   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1876   indent-tabs-mode:nil
1877   fill-column:99
1878   End:
1879 */
1880 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :