Code

add #include <gtk/gtk.h> to fix defect #333767
[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 #include <gtk/gtkmessagedialog.h>
33 #ifdef HAVE_IEEEFP_H
34 #include <ieeefp.h>
35 #endif
36 #include <cstring>
37 #include <string>
38 #include <locale.h>
39 #include <stdlib.h>
41 #include <popt.h>
42 #ifndef POPT_TABLEEND
43 #define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
44 #endif /* Not def: POPT_TABLEEND */
46 #include <libxml/tree.h>
47 #include <glib-object.h>
48 #include <gtk/gtk.h>
49 #include <gtk/gtkmain.h>
50 #include <gtk/gtksignal.h>
51 #include <gtk/gtkwindow.h>
52 #include <gtk/gtkbox.h>
54 #include "gc-core.h"
56 #include "macros.h"
57 #include "file.h"
58 #include "document.h"
59 #include "sp-object.h"
60 #include "interface.h"
61 #include "print.h"
62 #include "color.h"
63 #include "sp-item.h"
64 #include "sp-root.h"
65 #include "unit-constants.h"
67 #include "svg/svg.h"
68 #include "svg/svg-color.h"
69 #include "svg/stringstream.h"
71 #include "inkscape-private.h"
72 #include "inkscape-version.h"
74 #include "sp-namedview.h"
75 #include "sp-guide.h"
76 #include "sp-object-repr.h"
77 #include "xml/repr.h"
79 #include "io/sys.h"
81 #include "debug/logger.h"
82 #include "debug/log-display-config.h"
84 #include "helper/png-write.h"
85 #include "helper/geom.h"
87 #include <extension/extension.h>
88 #include <extension/system.h>
89 #include <extension/db.h>
90 #include <extension/output.h>
92 #ifdef WIN32
93 //#define REPLACEARGS_ANSI
94 //#define REPLACEARGS_DEBUG
96 #include "registrytool.h"
98 #include "extension/internal/win32.h"
99 using Inkscape::Extension::Internal::PrintWin32;
101 #endif // WIN32
103 #include "extension/init.h"
105 #include <glibmm/i18n.h>
106 #include <gtkmm/main.h>
108 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
109 #define bind_textdomain_codeset(p,c)
110 #endif
112 #include "application/application.h"
113 #include "main-cmdlineact.h"
114 #include "widgets/icon.h"
115 #include "ui/widget/panel.h"
118 #include <png.h>
119 #include <errno.h>
121 enum {
122     SP_ARG_NONE,
123     SP_ARG_NOGUI,
124     SP_ARG_GUI,
125     SP_ARG_FILE,
126     SP_ARG_PRINT,
127     SP_ARG_EXPORT_PNG,
128     SP_ARG_EXPORT_DPI,
129     SP_ARG_EXPORT_AREA,
130     SP_ARG_EXPORT_AREA_DRAWING,
131     SP_ARG_EXPORT_AREA_CANVAS,
132     SP_ARG_EXPORT_AREA_SNAP,
133     SP_ARG_EXPORT_WIDTH,
134     SP_ARG_EXPORT_HEIGHT,
135     SP_ARG_EXPORT_ID,
136     SP_ARG_EXPORT_ID_ONLY,
137     SP_ARG_EXPORT_USE_HINTS,
138     SP_ARG_EXPORT_BACKGROUND,
139     SP_ARG_EXPORT_BACKGROUND_OPACITY,
140     SP_ARG_EXPORT_SVG,
141     SP_ARG_EXPORT_PS,
142     SP_ARG_EXPORT_EPS,
143     SP_ARG_EXPORT_PDF,
144 #ifdef WIN32
145     SP_ARG_EXPORT_EMF,
146 #endif //WIN32
147     SP_ARG_EXPORT_TEXT_TO_PATH,
148     SP_ARG_EXPORT_IGNORE_FILTERS,
149     SP_ARG_EXTENSIONDIR,
150     SP_ARG_QUERY_X,
151     SP_ARG_QUERY_Y,
152     SP_ARG_QUERY_WIDTH,
153     SP_ARG_QUERY_HEIGHT,
154     SP_ARG_QUERY_ALL,
155     SP_ARG_QUERY_ID,
156     SP_ARG_SHELL,
157     SP_ARG_VERSION,
158     SP_ARG_VACUUM_DEFS,
159     SP_ARG_VERB_LIST,
160     SP_ARG_VERB,
161     SP_ARG_SELECT,
162     SP_ARG_LAST
163 };
165 int sp_main_gui(int argc, char const **argv);
166 int sp_main_console(int argc, char const **argv);
167 static void sp_do_export_png(SPDocument *doc);
168 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime);
169 #ifdef WIN32
170 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
171 #endif //WIN32
172 static void do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id);
173 static void do_query_all (SPDocument *doc);
174 static void do_query_all_recurse (SPObject *o);
176 static gchar *sp_global_printer = NULL;
177 static gchar *sp_export_png = NULL;
178 static gchar *sp_export_dpi = NULL;
179 static gchar *sp_export_area = NULL;
180 static gboolean sp_export_area_drawing = FALSE;
181 static gboolean sp_export_area_canvas = FALSE;
182 static gchar *sp_export_width = NULL;
183 static gchar *sp_export_height = NULL;
184 static gchar *sp_export_id = NULL;
185 static gchar *sp_export_background = NULL;
186 static gchar *sp_export_background_opacity = NULL;
187 static gboolean sp_export_area_snap = FALSE;
188 static gboolean sp_export_use_hints = FALSE;
189 static gboolean sp_export_id_only = FALSE;
190 static gchar *sp_export_svg = NULL;
191 static gchar *sp_export_ps = NULL;
192 static gchar *sp_export_eps = NULL;
193 static gchar *sp_export_pdf = NULL;
194 #ifdef WIN32
195 static gchar *sp_export_emf = NULL;
196 #endif //WIN32
197 static gboolean sp_export_text_to_path = FALSE;
198 static gboolean sp_export_ignore_filters = FALSE;
199 static gboolean sp_export_font = FALSE;
200 static gboolean sp_query_x = FALSE;
201 static gboolean sp_query_y = FALSE;
202 static gboolean sp_query_width = FALSE;
203 static gboolean sp_query_height = FALSE;
204 static gboolean sp_query_all = FALSE;
205 static gchar *sp_query_id = NULL;
206 static int sp_new_gui = FALSE;
207 static gboolean sp_shell = FALSE;
208 static gboolean sp_vacuum_defs = FALSE;
210 static gchar *sp_export_png_utf8 = NULL;
211 static gchar *sp_export_svg_utf8 = NULL;
212 static gchar *sp_global_printer_utf8 = NULL;
215 /**
216  *  Reset variables to default values.
217  */
218 static void resetCommandlineGlobals() {
219         sp_global_printer = NULL;
220         sp_export_png = NULL;
221         sp_export_dpi = NULL;
222         sp_export_area = NULL;
223         sp_export_area_drawing = FALSE;
224         sp_export_area_canvas = FALSE;
225         sp_export_width = NULL;
226         sp_export_height = NULL;
227         sp_export_id = NULL;
228         sp_export_background = NULL;
229         sp_export_background_opacity = NULL;
230         sp_export_area_snap = FALSE;
231         sp_export_use_hints = FALSE;
232         sp_export_id_only = FALSE;
233         sp_export_svg = NULL;
234         sp_export_ps = NULL;
235         sp_export_eps = NULL;
236         sp_export_pdf = NULL;
237 #ifdef WIN32
238         sp_export_emf = NULL;
239 #endif //WIN32
240         sp_export_text_to_path = FALSE;
241         sp_export_ignore_filters = FALSE;
242         sp_export_font = FALSE;
243         sp_query_x = FALSE;
244         sp_query_y = FALSE;
245         sp_query_width = FALSE;
246         sp_query_height = FALSE;
247         sp_query_all = FALSE;
248         sp_query_id = NULL;
249         sp_vacuum_defs = FALSE;
251         sp_export_png_utf8 = NULL;
252         sp_export_svg_utf8 = NULL;
253         sp_global_printer_utf8 = NULL;
256 #ifdef WIN32
257 static bool replaceArgs( int& argc, char**& argv );
258 #endif
259 static GSList *sp_process_args(poptContext ctx);
260 struct poptOption options[] = {
261     {"version", 'V',
262      POPT_ARG_NONE, NULL, SP_ARG_VERSION,
263      N_("Print the Inkscape version number"),
264      NULL},
266     {"without-gui", 'z',
267      POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
268      N_("Do not use X server (only process files from console)"),
269      NULL},
271     {"with-gui", 'g',
272      POPT_ARG_NONE, NULL, SP_ARG_GUI,
273      N_("Try to use X server (even if $DISPLAY is not set)"),
274      NULL},
276     {"file", 'f',
277      POPT_ARG_STRING, NULL, SP_ARG_FILE,
278      N_("Open specified document(s) (option string may be excluded)"),
279      N_("FILENAME")},
281     {"print", 'p',
282      POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
283      N_("Print document(s) to specified output file (use '| program' for pipe)"),
284      N_("FILENAME")},
286     {"export-png", 'e',
287      POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
288      N_("Export document to a PNG file"),
289      N_("FILENAME")},
291     {"export-dpi", 'd',
292      POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
293      N_("The resolution used for exporting SVG into bitmap (default 90)"),
294      N_("DPI")},
296     {"export-area", 'a',
297      POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
298      N_("Exported area in SVG user units (default is the canvas; 0,0 is lower-left corner)"),
299      N_("x0:y0:x1:y1")},
301     {"export-area-drawing", 'D',
302      POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
303      N_("Exported area is the entire drawing (not canvas)"),
304      NULL},
306     {"export-area-canvas", 'C',
307      POPT_ARG_NONE, &sp_export_area_canvas, SP_ARG_EXPORT_AREA_CANVAS,
308      N_("Exported area is the entire canvas"),
309      NULL},
311     {"export-area-snap", 0,
312      POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
313      N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
314      NULL},
316     {"export-width", 'w',
317      POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
318      N_("The width of exported bitmap in pixels (overrides export-dpi)"),
319      N_("WIDTH")},
321     {"export-height", 'h',
322      POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
323      N_("The height of exported bitmap in pixels (overrides export-dpi)"),
324      N_("HEIGHT")},
326     {"export-id", 'i',
327      POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
328      N_("The ID of the object to export"),
329      N_("ID")},
331     {"export-id-only", 'j',
332      POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
333      // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
334      //  See "man inkscape" for details.
335      N_("Export just the object with export-id, hide all others (only with export-id)"),
336      NULL},
338     {"export-use-hints", 't',
339      POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
340      N_("Use stored filename and DPI hints when exporting (only with export-id)"),
341      NULL},
343     {"export-background", 'b',
344      POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
345      N_("Background color of exported bitmap (any SVG-supported color string)"),
346      N_("COLOR")},
348     {"export-background-opacity", 'y',
349      POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
350      N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
351      N_("VALUE")},
353     {"export-plain-svg", 'l',
354      POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
355      N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
356      N_("FILENAME")},
358     {"export-ps", 'P',
359      POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
360      N_("Export document to a PS file"),
361      N_("FILENAME")},
363     {"export-eps", 'E',
364      POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
365      N_("Export document to an EPS file"),
366      N_("FILENAME")},
368     {"export-pdf", 'A',
369      POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
370      N_("Export document to a PDF file"),
371      N_("FILENAME")},
373 #ifdef WIN32
374     {"export-emf", 'M',
375      POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
376      N_("Export document to an Enhanced Metafile (EMF) File"),
377      N_("FILENAME")},
378 #endif //WIN32
380     {"export-text-to-path", 'T',
381      POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
382      N_("Convert text object to paths on export (PS, EPS, PDF)"),
383      NULL},
385     {"export-ignore-filters", 0,
386      POPT_ARG_NONE, &sp_export_ignore_filters, SP_ARG_EXPORT_IGNORE_FILTERS,
387      N_("Render filtered objects without filters, instead of rasterizing (PS, EPS, PDF)"),
388      NULL},
390     {"query-x", 'X',
391      POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
392      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
393      N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
394      NULL},
396     {"query-y", 'Y',
397      POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
398      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
399      N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
400      NULL},
402     {"query-width", 'W',
403      POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
404      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
405      N_("Query the width of the drawing or, if specified, of the object with --query-id"),
406      NULL},
408     {"query-height", 'H',
409      POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
410      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
411      N_("Query the height of the drawing or, if specified, of the object with --query-id"),
412      NULL},
414     {"query-all", 'S',
415      POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
416      N_("List id,x,y,w,h for all objects"),
417      NULL},
419     {"query-id", 'I',
420      POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
421      N_("The ID of the object whose dimensions are queried"),
422      N_("ID")},
424     {"extension-directory", 'x',
425      POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
426      // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
427      N_("Print out the extension directory and exit"),
428      NULL},
430     {"vacuum-defs", 0,
431      POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
432      N_("Remove unused definitions from the defs section(s) of the document"),
433      NULL},
435     {"verb-list", 0,
436      POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
437      N_("List the IDs of all the verbs in Inkscape"),
438      NULL},
440     {"verb", 0,
441      POPT_ARG_STRING, NULL, SP_ARG_VERB,
442      N_("Verb to call when Inkscape opens."),
443      N_("VERB-ID")},
445     {"select", 0,
446      POPT_ARG_STRING, NULL, SP_ARG_SELECT,
447      N_("Object ID to select when Inkscape opens."),
448      N_("OBJECT-ID")},
450     {"shell", 0,
451      POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
452      N_("Start Inkscape in interative shell mode."),
453      NULL},
455     POPT_AUTOHELP POPT_TABLEEND
456 };
458 static bool needToRecodeParams = true;
459 gchar * blankParam = g_strdup("");
463 #ifdef WIN32
465 /**
466  * Return the directory of the .exe that is currently running
467  */
468 static Glib::ustring _win32_getExePath()
470     char exeName[MAX_PATH+1];
471     GetModuleFileName(NULL, exeName, MAX_PATH);
472     char *slashPos = strrchr(exeName, '\\');
473     if (slashPos)
474         *slashPos = '\0';
475     Glib::ustring s = exeName;
476     return s;
479 /**
480  * Set up the PATH and PYTHONPATH environment variables on
481  * win32
482  */
483 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
486     char *oldenv = getenv("PATH");
487     Glib::ustring tmp = "PATH=";
488     tmp += exePath;
489     tmp += ";";
490     tmp += exePath;
491     tmp += "\\python;";
492     tmp += exePath;
493     tmp += "\\python\\Scripts;";  // for uniconv.cmd
494     tmp += exePath;
495     tmp += "\\perl";
496     if(oldenv != NULL) {
497         tmp += ";";
498         tmp += oldenv;
499     }
500     _putenv(tmp.c_str());
502     oldenv = getenv("PYTHONPATH");
503     tmp = "PYTHONPATH=";
504     tmp += exePath;
505     tmp += "\\python;";
506     tmp += exePath;
507     tmp += "\\python\\Lib;";
508     tmp += exePath;
509     tmp += "\\python\\DLLs";
510     if(oldenv != NULL) {
511         tmp += ";";
512         tmp += oldenv;
513     }
514     _putenv(tmp.c_str());
516     return 0;
518 #endif
520 /**
521  * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
522  * can find inkex.py et al. (Bug #197475)
523  */
524 static int set_extensions_env()
526     char *oldenv = getenv("PYTHONPATH");
527     Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
528     if (oldenv != NULL) {
529         tmp += G_SEARCHPATH_SEPARATOR;
530         tmp += oldenv;
531     }
532     g_setenv("PYTHONPATH", tmp.c_str(), TRUE);
533     
534     return 0;
538 /**
539  * This is the classic main() entry point of the program, though on some
540  * architectures it might be called by something else.
541  */
542 int
543 main(int argc, char **argv)
545 #ifdef HAVE_FPSETMASK
546     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
547        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
548        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
549     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
550 #endif
552 #ifdef WIN32
553     /*
554       Set the current directory to the directory of the
555       executable.  This seems redundant, but is needed for
556       when inkscape.exe is executed from another directory.
557       We use relative paths on win32.
558       HKCR\svgfile\shell\open\command is a good example
559     */
560     Glib::ustring homedir = _win32_getExePath();
561     SetCurrentDirectory(homedir.c_str());
562     _win32_set_inkscape_env(homedir);
563     RegistryTool rt;
564     rt.setPathInfo();
565 #endif
567     // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
568     Gtk::Main::init_gtkmm_internals();
570     // Bug #197475
571     set_extensions_env();
573    /**
574     * Call bindtextdomain() for various machines's paths
575     */
576 #ifdef ENABLE_NLS
577 #ifdef WIN32
578     Glib::ustring localePath = homedir;
579     localePath += "\\";
580     localePath += PACKAGE_LOCALE_DIR;
581     bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
582 #else
583 #ifdef ENABLE_BINRELOC
584     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
585 #else
586     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
587 #endif
588 #endif
589     // Allow the user to override the locale directory by setting
590     // the environment variable INKSCAPE_LOCALEDIR.
591     char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
592     if (inkscape_localedir != NULL) {
593         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
594     }
595 #endif
597     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
599 #ifdef ENABLE_NLS
600     textdomain(GETTEXT_PACKAGE);
601 #endif
603     LIBXML_TEST_VERSION
605     Inkscape::GC::init();
607     Inkscape::Debug::Logger::init();
609     gboolean use_gui;
611 #ifndef WIN32
612     use_gui = (getenv("DISPLAY") != NULL);
613 #else
614     use_gui = TRUE;
615 #endif
616     /* Test whether with/without GUI is forced */
617     for (int i = 1; i < argc; i++) {
618         if (!strcmp(argv[i], "-z")
619             || !strcmp(argv[i], "--without-gui")
620             || !strcmp(argv[i], "-p")
621             || !strncmp(argv[i], "--print", 7)
622             || !strcmp(argv[i], "-e")
623             || !strncmp(argv[i], "--export-png", 12)
624             || !strcmp(argv[i], "-l")
625             || !strncmp(argv[i], "--export-plain-svg", 12)
626             || !strcmp(argv[i], "-i")
627             || !strncmp(argv[i], "--export-area-drawing", 21)
628             || !strcmp(argv[i], "-D")
629             || !strncmp(argv[i], "--export-area-canvas", 20)
630             || !strcmp(argv[i], "-C")
631             || !strncmp(argv[i], "--export-id", 12)
632             || !strcmp(argv[i], "-P")
633             || !strncmp(argv[i], "--export-ps", 11)
634             || !strcmp(argv[i], "-E")
635             || !strncmp(argv[i], "--export-eps", 12)
636             || !strcmp(argv[i], "-A")
637             || !strncmp(argv[i], "--export-pdf", 12)
638 #ifdef WIN32
639             || !strcmp(argv[i], "-M")
640             || !strncmp(argv[i], "--export-emf", 12)
641 #endif //WIN32
642             || !strcmp(argv[i], "-W")
643             || !strncmp(argv[i], "--query-width", 13)
644             || !strcmp(argv[i], "-H")
645             || !strncmp(argv[i], "--query-height", 14)
646             || !strcmp(argv[i], "-S")
647             || !strncmp(argv[i], "--query-all", 11)
648             || !strcmp(argv[i], "-X")
649             || !strncmp(argv[i], "--query-x", 13)
650             || !strcmp(argv[i], "-Y")
651             || !strncmp(argv[i], "--query-y", 14)
652             || !strcmp(argv[i], "--vacuum-defs")
653             || !strncmp(argv[i], "--shell", 7)
654            )
655         {
656             /* main_console handles any exports -- not the gui */
657             use_gui = FALSE;
658             break;
659         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
660             use_gui = TRUE;
661             break;
662         }
663     }
665 #ifdef WIN32
666 #ifndef REPLACEARGS_ANSI
667     if ( PrintWin32::is_os_wide() )
668 #endif // REPLACEARGS_ANSI
669     {
670         // If the call fails, we'll need to convert charsets
671         needToRecodeParams = !replaceArgs( argc, argv );
672     }
673 #endif // WIN32
675     /// \todo  Should this be a static object (see inkscape.cpp)?
676     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
678     return app.run();
684 void fixupSingleFilename( gchar **orig, gchar **spare )
686     if ( orig && *orig && **orig ) {
687         GError *error = NULL;
688         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
689         if ( newFileName )
690         {
691             *orig = newFileName;
692             if ( spare ) {
693                 *spare = newFileName;
694             }
695 //             g_message("Set a replacement fixup");
696         }
697     }
702 GSList *fixupFilenameEncoding( GSList* fl )
704     GSList *newFl = NULL;
705     while ( fl ) {
706         gchar *fn = static_cast<gchar*>(fl->data);
707         fl = g_slist_remove( fl, fl->data );
708         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
709         if ( newFileName ) {
711             if ( 0 )
712             {
713                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
714                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
715                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
716                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
717                 gtk_dialog_run (GTK_DIALOG (w));
718                 gtk_widget_destroy (w);
719                 g_free(safeNewFn);
720                 g_free(safeFn);
721             }
723             g_free( fn );
724             fn = newFileName;
725             newFileName = 0;
726         }
727         else
728             if ( 0 )
729         {
730             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
731             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
732             gtk_dialog_run (GTK_DIALOG (w));
733             gtk_widget_destroy (w);
734             g_free(safeFn);
735         }
736         newFl = g_slist_append( newFl, fn );
737     }
738     return newFl;
741 int sp_common_main( int argc, char const **argv, GSList **flDest )
743     /// \todo fixme: Move these to some centralized location (Lauris)
744     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
745     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
748     // temporarily switch gettext encoding to locale, so that help messages can be output properly
749     gchar const *charset;
750     g_get_charset(&charset);
752     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
754     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
755     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
756     g_return_val_if_fail(ctx != NULL, 1);
758     /* Collect own arguments */
759     GSList *fl = sp_process_args(ctx);
760     poptFreeContext(ctx);
762     // now switch gettext back to UTF-8 (for GUI)
763     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
765     // Now let's see if the file list still holds up
766     if ( needToRecodeParams )
767     {
768         fl = fixupFilenameEncoding( fl );
769     }
771     // Check the globals for filename-fixup
772     if ( needToRecodeParams )
773     {
774         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
775         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
776         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
777     }
778     else
779     {
780         if ( sp_export_png )
781             sp_export_png_utf8 = g_strdup( sp_export_png );
782         if ( sp_export_svg )
783             sp_export_svg_utf8 = g_strdup( sp_export_svg );
784         if ( sp_global_printer )
785             sp_global_printer_utf8 = g_strdup( sp_global_printer );
786     }
788     // Return the list if wanted, else free it up.
789     if ( flDest ) {
790         *flDest = fl;
791         fl = 0;
792     } else {
793         while ( fl ) {
794             g_free( fl->data );
795             fl = g_slist_remove( fl, fl->data );
796         }
797     }
798     return 0;
801 static void
802 snooper(GdkEvent *event, gpointer /*data*/) {
803     if(inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
804     {
805         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
806         switch (event->type) {
807             case GDK_MOTION_NOTIFY:
808                 if(event->motion.state & mapping) {
809                     event->motion.state|=GDK_MOD1_MASK;
810                 }
811                 break;
812             case GDK_BUTTON_PRESS:
813                 if(event->button.state & mapping) {
814                     event->button.state|=GDK_MOD1_MASK;
815                 }
816                 break;
817              case GDK_KEY_PRESS:
818                  if(event->key.state & mapping) {
819                      event->key.state|=GDK_MOD1_MASK;
820                  }
821                  break;
822         default:
823             break;
824         }
825     }
826     gtk_main_do_event (event);
829 int
830 sp_main_gui(int argc, char const **argv)
832     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
834     GSList *fl = NULL;
835     int retVal = sp_common_main( argc, argv, &fl );
836     g_return_val_if_fail(retVal == 0, 1);
838     // Add our icon directory to the search path for icon theme lookups.
839     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
841     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
842     Inkscape::Debug::log_display_config();
844     // Set default window icon. Obeys the theme.
845     gtk_window_set_default_icon_name("inkscape");
846     // Do things that were previously in inkscape_gtk_stock_init().
847     sp_icon_get_phys_size(GTK_ICON_SIZE_MENU);
848     Inkscape::UI::Widget::Panel::prep();
850     gboolean create_new = TRUE;
852     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
853     inkscape_application_init(argv[0], true);
855     while (fl) {
856         if (sp_file_open((gchar *)fl->data,NULL)) {
857             create_new=FALSE;
858         }
859         fl = g_slist_remove(fl, fl->data);
860     }
861     if (create_new) {
862         sp_file_new_default();
863     }
865     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
866     main_instance.run();
868 #ifdef WIN32
869     //We might not need anything here
870     //sp_win32_finish(); <-- this is a NOP func
871 #endif
873     return 0;
876 /**
877  * Process file list
878  */
879 void sp_process_file_list(GSList *fl)
881     while (fl) {
882         const gchar *filename = (gchar *)fl->data;
883         SPDocument *doc = Inkscape::Extension::open(NULL, filename);
884         if (doc == NULL) {
885             doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
886         }
887         if (doc == NULL) {
888             g_warning("Specified document %s cannot be opened (is it a valid SVG file?)", filename);
889         } else {
890             if (sp_vacuum_defs) {
891                 vacuum_document(doc);
892             }
893             if (sp_vacuum_defs && !sp_export_svg) {
894                 // save under the name given in the command line
895                 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
896             }
897             if (sp_global_printer) {
898                 sp_print_document_to_file(doc, sp_global_printer);
899             }
900             if (sp_export_png) {
901                 sp_do_export_png(doc);
902             }
903             if (sp_export_svg) {
904                 Inkscape::XML::Document *rdoc;
905                 Inkscape::XML::Node *repr;
906                 rdoc = sp_repr_document_new("svg:svg");
907                 repr = rdoc->root();
908                 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
909                 sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
910             }
911             if (sp_export_ps) {
912                 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
913             }
914             if (sp_export_eps) {
915                 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
916             }
917             if (sp_export_pdf) {
918                 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
919             }
920 #ifdef WIN32
921             if (sp_export_emf) {
922                 do_export_emf(doc, sp_export_emf, "image/x-emf");
923             }
924 #endif //WIN32
925             if (sp_query_all) {
926                 do_query_all (doc);
927             } else if (sp_query_width || sp_query_height) {
928                 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
929             } else if (sp_query_x || sp_query_y) {
930                 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
931             }
933             delete doc;
934         }
935         fl = g_slist_remove(fl, fl->data);
936     }
939 /**
940  * Run the application as an interactive shell, parsing command lines from stdin
941  * Returns -1 on error.
942  */
943 int sp_main_shell(char const* command_name)
945     int retval = 0;
947     const unsigned int buffer_size = 4096;
948     gchar *command_line = g_strnfill(buffer_size, 0);
949     g_strlcpy(command_line, command_name, buffer_size);
950     gsize offset = g_strlcat(command_line, " ", buffer_size);
951     gsize sizeLeft = buffer_size - offset;
952     gchar *useme = command_line + offset;
954     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
955     fflush(stdout);
956     char* linedata = 0;
957     do {
958         fprintf(stdout, ">");
959         fflush(stdout);
960         if ((linedata = fgets(useme, sizeLeft, stdin))) {
961             size_t len = strlen(useme);
962             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
963                 fprintf(stdout, "ERROR: Command line too long\n");
964                 // Consume rest of line
965                 retval = -1; // If the while loop completes, this remains -1
966                 while (fgets(useme, sizeLeft, stdin) && retval) {
967                     len = strlen(command_line);
968                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
969                         retval = 0;
970                     }
971                 }
972             } else {
973                 useme[--len] = '\0';  // Strip newline
974                 if (useme[len - 1] == '\r') {
975                     useme[--len] = '\0';
976                 }
977                 if ( strcmp(useme, "quit") == 0 ) {
978                     // Time to quit
979                     fflush(stdout);
980                     linedata = 0; // mark for exit
981                 } else if ( len < 1 ) {
982                     // blank string. Do nothing.
983                 } else {
984                     GError* parseError = 0;
985                     gchar** argv = 0;
986                     gint argc = 0;
987                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
988                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
989                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
990                         if ( ctx ) {
991                             GSList *fl = sp_process_args(ctx);
992                             sp_process_file_list(fl);
993                             poptFreeContext(ctx);
994                         } else {
995                             retval = 1; // not sure why. But this was the previous return value
996                         }
997                         resetCommandlineGlobals();
998                         g_strfreev(argv);
999                     } else {
1000                         g_warning("Cannot parse commandline: %s", useme);
1001                     }
1002                 }
1003             }
1004         } // if (linedata...
1005     } while (linedata && (retval == 0));
1007     g_free(command_line);
1008     return retval;
1011 int sp_main_console(int argc, char const **argv)
1013     /* We are started in text mode */
1015     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1016      * in a non-Gtk environment.  Used in libnrtype's
1017      * FontInstance.cpp and FontFactory.cpp.
1018      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1019      */
1020     g_type_init();
1021     char **argv2 = const_cast<char **>(argv);
1022     gtk_init_check( &argc, &argv2 );
1023     //setlocale(LC_ALL, "");
1025     GSList *fl = NULL;
1026     int retVal = sp_common_main( argc, argv, &fl );
1027     g_return_val_if_fail(retVal == 0, 1);
1029     if (fl == NULL && !sp_shell) {
1030         g_print("Nothing to do!\n");
1031         exit(0);
1032     }
1034     inkscape_application_init(argv[0], false);
1036     if (sp_shell) {
1037         sp_main_shell(argv[0]); // Run as interactive shell
1038         exit(0);
1039     } else {
1040         sp_process_file_list(fl); // Normal command line invokation
1041     }
1043     return 0;
1046 static void
1047 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1049     SPObject *o = NULL;
1051     if (id) {
1052         o = doc->getObjectById(id);
1053         if (o) {
1054             if (!SP_IS_ITEM (o)) {
1055                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1056                 return;
1057             }
1058         } else {
1059             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1060             return;
1061         }
1062     } else {
1063         o = SP_DOCUMENT_ROOT(doc);
1064     }
1066     if (o) {
1067         sp_document_ensure_up_to_date (doc);
1068         SPItem *item = ((SPItem *) o);
1070         // "true" SVG bbox for scripting
1071         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1072         if (area) {
1073             Inkscape::SVGOStringStream os;
1074             if (extent) {
1075                 os << area->dimensions()[axis];
1076             } else {
1077                 os << area->min()[axis];
1078             }
1079             g_print ("%s", os.str().c_str());
1080         } else {
1081             g_print("0");
1082         }
1083     }
1086 static void
1087 do_query_all (SPDocument *doc)
1089     SPObject *o = NULL;
1091     o = SP_DOCUMENT_ROOT(doc);
1093     if (o) {
1094         sp_document_ensure_up_to_date (doc);
1095         do_query_all_recurse(o);
1096     }
1099 static void
1100 do_query_all_recurse (SPObject *o)
1102     SPItem *item = ((SPItem *) o);
1103     if (o->id && SP_IS_ITEM(item)) {
1104         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1105         if (area) {
1106             Inkscape::SVGOStringStream os;
1107             os << o->id;
1108             os << "," << area->min()[Geom::X];
1109             os << "," << area->min()[Geom::Y];
1110             os << "," << area->dimensions()[Geom::X];
1111             os << "," << area->dimensions()[Geom::Y];
1112             g_print ("%s\n", os.str().c_str());
1113         }
1114     }
1116     SPObject *child = o->children;
1117     while (child) {
1118         do_query_all_recurse (child);
1119         child = child->next;
1120     }
1124 static void
1125 sp_do_export_png(SPDocument *doc)
1127     const gchar *filename = NULL;
1128     gdouble dpi = 0.0;
1130     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1131         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1132     }
1134     GSList *items = NULL;
1136     Geom::Rect area;
1137     if (sp_export_id || sp_export_area_drawing) {
1139         SPObject *o = NULL;
1140         SPObject *o_area = NULL;
1141         if (sp_export_id && sp_export_area_drawing) {
1142             o = doc->getObjectById(sp_export_id);
1143             o_area = SP_DOCUMENT_ROOT (doc);
1144         } else if (sp_export_id) {
1145             o = doc->getObjectById(sp_export_id);
1146             o_area = o;
1147         } else if (sp_export_area_drawing) {
1148             o = SP_DOCUMENT_ROOT (doc);
1149             o_area = o;
1150         }
1152         if (o) {
1153             if (!SP_IS_ITEM (o)) {
1154                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1155                 return;
1156             }
1158             items = g_slist_prepend (items, SP_ITEM(o));
1160             if (sp_export_id_only) {
1161                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1162             }
1164             if (sp_export_use_hints) {
1166                 // retrieve export filename hint
1167                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1168                 if (fn_hint) {
1169                     if (sp_export_png) {
1170                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1171                         filename = sp_export_png;
1172                     } else {
1173                         filename = fn_hint;
1174                     }
1175                 } else {
1176                     g_warning ("Export filename hint not found for the object.");
1177                     filename = sp_export_png;
1178                 }
1180                 // retrieve export dpi hints
1181                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1182                 if (dpi_hint) {
1183                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1184                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1185                     } else {
1186                         dpi = atof(dpi_hint);
1187                     }
1188                 } else {
1189                     g_warning ("Export DPI hint not found for the object.");
1190                 }
1192             }
1194             // write object bbox to area
1195             sp_document_ensure_up_to_date (doc);
1196             Geom::OptRect areaMaybe;
1197             sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1198             if (areaMaybe) {
1199                 area = *areaMaybe;
1200             } else {
1201                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1202                 return;
1203             }
1204         } else {
1205             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1206             return;
1207         }
1208     }
1210     if (sp_export_area) {
1211         /* Try to parse area (given in SVG pixels) */
1212         gdouble x0,y0,x1,y1;
1213         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1214             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1215             return;
1216         }
1217         area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1218     } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1219         /* Export the whole canvas */
1220         sp_document_ensure_up_to_date (doc);
1221         Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1222         area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1223     }
1225     // set filename and dpi from options, if not yet set from the hints
1226     if (!filename) {
1227         if (!sp_export_png) {
1228             g_warning ("No export filename given and no filename hint. Nothing exported.");
1229             return;
1230         }
1231         filename = sp_export_png;
1232     }
1234     if (sp_export_dpi && dpi == 0.0) {
1235         dpi = atof(sp_export_dpi);
1236         if ((dpi < 0.1) || (dpi > 10000.0)) {
1237             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1238             return;
1239         }
1240         g_print("DPI: %g\n", dpi);
1241     }
1243     if (sp_export_area_snap) {
1244         round_rectangle_outwards(area);
1245     }
1247     // default dpi
1248     if (dpi == 0.0) {
1249         dpi = PX_PER_IN;
1250     }
1252     unsigned long int width = 0;
1253     unsigned long int height = 0;
1255     if (sp_export_width) {
1256         errno=0;
1257         width = strtoul(sp_export_width, NULL, 0);
1258         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1259             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1260             return;
1261         }
1262         dpi = (gdouble) width * PX_PER_IN / area.width();
1263     }
1265     if (sp_export_height) {
1266         errno=0;
1267         height = strtoul(sp_export_height, NULL, 0);
1268         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1269             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1270             return;
1271         }
1272         dpi = (gdouble) height * PX_PER_IN / area.height();
1273     }
1275     if (!sp_export_width) {
1276         width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1277     }
1279     if (!sp_export_height) {
1280         height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1281     }
1283     guint32 bgcolor = 0x00000000;
1284     if (sp_export_background) {
1285         // override the page color
1286         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1287         bgcolor |= 0xff; // default is no opacity
1288     } else {
1289         // read from namedview
1290         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1291         if (nv && nv->attribute("pagecolor"))
1292             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1293         if (nv && nv->attribute("inkscape:pageopacity"))
1294             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1295     }
1297     if (sp_export_background_opacity) {
1298         // override opacity
1299         gfloat value;
1300         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1301             if (value > 1.0) {
1302                 value = CLAMP (value, 1.0f, 255.0f);
1303                 bgcolor &= (guint32) 0xffffff00;
1304                 bgcolor |= (guint32) floor(value);
1305             } else {
1306                 value = CLAMP (value, 0.0f, 1.0f);
1307                 bgcolor &= (guint32) 0xffffff00;
1308                 bgcolor |= SP_COLOR_F_TO_U(value);
1309             }
1310         }
1311     }
1313     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1315     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);
1317     g_print("Bitmap saved as: %s\n", filename);
1319     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1320         sp_export_png_file(doc, filename, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1321     } else {
1322         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1323     }
1325     g_slist_free (items);
1329 /**
1330  *  Perform a PDF/PS/EPS export
1331  *
1332  *  \param doc Document to export.
1333  *  \param uri URI to export to.
1334  *  \param mime MIME type to export as.
1335  */
1337 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1339     Inkscape::Extension::DB::OutputList o;
1340     Inkscape::Extension::db.get_output_list(o);
1341     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1342     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1343         i++;
1344     }
1346     if (i == o.end())
1347     {
1348         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1349         return;
1350     }
1352     if (sp_export_id) {
1353         SPObject *o = doc->getObjectById(sp_export_id);
1354         if (o == NULL) {
1355             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1356             return;
1357         }
1358         (*i)->set_param_string ("exportId", sp_export_id);
1359     } else {
1360         (*i)->set_param_string ("exportId", "");
1361     }
1363     if (sp_export_area_canvas && sp_export_area_drawing) {
1364         g_warning ("You cannot use --export-area-canvas and --export-area-drawing at the same time; only the former will take effect.");
1365         sp_export_area_drawing = false;
1366     }
1368     if (sp_export_area_drawing) {
1369         (*i)->set_param_bool ("areaDrawing", TRUE);
1370     } else {
1371         (*i)->set_param_bool ("areaDrawing", FALSE);
1372     }
1374     if (sp_export_area_canvas) {
1375         if (sp_export_eps) {
1376             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.");
1377         } 
1378         (*i)->set_param_bool ("areaCanvas", TRUE);
1379     } else {
1380         (*i)->set_param_bool ("areaCanvas", FALSE);
1381     }
1383     if (!sp_export_area_drawing && !sp_export_area_canvas && !sp_export_id) { 
1384         // neither is set, set canvas as default for ps/pdf and drawing for eps
1385         if (sp_export_eps) {
1386             try {
1387                (*i)->set_param_bool("areaDrawing", TRUE);
1388             } catch (...) {}
1389         } 
1390     }
1392     if (sp_export_text_to_path) {
1393         (*i)->set_param_bool("textToPath", TRUE);
1394     } else {
1395         (*i)->set_param_bool("textToPath", FALSE);
1396     }
1398     if (sp_export_ignore_filters) {
1399         (*i)->set_param_bool("blurToBitmap", FALSE);
1400     } else {
1401         (*i)->set_param_bool("blurToBitmap", TRUE);
1402     }
1404     (*i)->save(doc, uri);
1407 #ifdef WIN32
1408 /**
1409  *  Export a document to EMF
1410  *
1411  *  \param doc Document to export.
1412  *  \param uri URI to export to.
1413  *  \param mime MIME type to export as (should be "image/x-emf")
1414  */
1416 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1418     Inkscape::Extension::DB::OutputList o;
1419     Inkscape::Extension::db.get_output_list(o);
1420     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1421     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1422         i++;
1423     }
1425     if (i == o.end())
1426     {
1427         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1428         return;
1429     }
1431     (*i)->save(doc, uri);
1433 #endif //WIN32
1435 #ifdef WIN32
1436 bool replaceArgs( int& argc, char**& argv )
1438     bool worked = false;
1440 #ifdef REPLACEARGS_DEBUG
1441     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1442 #endif // REPLACEARGS_DEBUG
1444     wchar_t* line = GetCommandLineW();
1445     if ( line )
1446     {
1447 #ifdef REPLACEARGS_DEBUG
1448         {
1449             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1450             if ( utf8Line )
1451             {
1452                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1453                 {
1454                     char tmp[strlen(safe) + 32];
1455                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1456                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1457                 }
1458             }
1459         }
1460 #endif // REPLACEARGS_DEBUG
1462         int numArgs = 0;
1463         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1465 #ifdef REPLACEARGS_ANSI
1466 // test code for trying things on Win95/98/ME
1467         if ( !parsed )
1468         {
1469 #ifdef REPLACEARGS_DEBUG
1470             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1471 #endif // REPLACEARGS_DEBUG
1472             int lineLen = wcslen(line) + 1;
1473             wchar_t* lineDup = new wchar_t[lineLen];
1474             wcsncpy( lineDup, line, lineLen );
1476             int pos = 0;
1477             bool inQuotes = false;
1478             bool inWhitespace = true;
1479             std::vector<int> places;
1480             while ( lineDup[pos] )
1481             {
1482                 if ( inQuotes )
1483                 {
1484                     if ( lineDup[pos] == L'"' )
1485                     {
1486                         inQuotes = false;
1487                     }
1488                 }
1489                 else if ( lineDup[pos] == L'"' )
1490                 {
1491                     inQuotes = true;
1492                     inWhitespace = false;
1493                     places.push_back(pos);
1494                 }
1495                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1496                 {
1497                     if ( !inWhitespace )
1498                     {
1499                         inWhitespace = true;
1500                         lineDup[pos] = 0;
1501                     }
1502                 }
1503                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1504                 {
1505                     inWhitespace = false;
1506                     places.push_back(pos);
1507                 }
1508                 else
1509                 {
1510                     // consume
1511                 }
1512                 pos++;
1513             }
1514 #ifdef REPLACEARGS_DEBUG
1515             {
1516                 char tmp[256];
1517                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1518                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1519             }
1520 #endif // REPLACEARGS_DEBUG
1522             wchar_t** block = new wchar_t*[places.size()];
1523             int i = 0;
1524             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1525             {
1526                 block[i++] = &lineDup[*it];
1527             }
1528             parsed = block;
1529             numArgs = places.size();
1530         }
1531 #endif // REPLACEARGS_ANSI
1533         if ( parsed )
1534         {
1535             std::vector<wchar_t*>expandedArgs;
1536             if ( numArgs > 0 )
1537             {
1538                 expandedArgs.push_back( parsed[0] );
1539             }
1541             for ( int i1 = 1; i1 < numArgs; i1++ )
1542             {
1543                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1544                 wildcarded &= parsed[i1][0] != L'"';
1545                 wildcarded &= parsed[i1][0] != L'-';
1546                 if ( wildcarded )
1547                 {
1548 #ifdef REPLACEARGS_ANSI
1549                     WIN32_FIND_DATAA data;
1550 #else
1551                     WIN32_FIND_DATAW data;
1552 #endif // REPLACEARGS_ANSI
1554                     memset((void *)&data, 0, sizeof(data));
1556                     int baseLen = wcslen(parsed[i1]) + 2;
1557                     wchar_t* base = new wchar_t[baseLen];
1558                     wcsncpy( base, parsed[i1], baseLen );
1559                     wchar_t* last = wcsrchr( base, L'\\' );
1560                     if ( last )
1561                     {
1562                         last[1] = 0;
1563                     }
1564                     else
1565                     {
1566                         base[0] = 0;
1567                     }
1568                     baseLen = wcslen( base );
1570 #ifdef REPLACEARGS_ANSI
1571                     char target[MAX_PATH];
1572                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1573                     {
1574                         HANDLE hf = FindFirstFileA( target, &data );
1575 #else
1576                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1577 #endif // REPLACEARGS_ANSI
1578                         if ( hf != INVALID_HANDLE_VALUE )
1579                         {
1580                             BOOL found = TRUE;
1581                             do
1582                             {
1583 #ifdef REPLACEARGS_ANSI
1584                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1585                                 if ( howMany > 0 )
1586                                 {
1587                                     howMany += baseLen;
1588                                     wchar_t* tmp = new wchar_t[howMany + 1];
1589                                     wcsncpy( tmp, base, howMany + 1 );
1590                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1591                                     expandedArgs.push_back( tmp );
1592                                     found = FindNextFileA( hf, &data );
1593                                 }
1594 #else
1595                                 int howMany = wcslen(data.cFileName) + baseLen;
1596                                 wchar_t* tmp = new wchar_t[howMany + 1];
1597                                 wcsncpy( tmp, base, howMany + 1 );
1598                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1599                                 expandedArgs.push_back( tmp );
1600                                 found = FindNextFileW( hf, &data );
1601 #endif // REPLACEARGS_ANSI
1602                             } while ( found );
1604                             FindClose( hf );
1605                         }
1606                         else
1607                         {
1608                             expandedArgs.push_back( parsed[i1] );
1609                         }
1610 #ifdef REPLACEARGS_ANSI
1611                     }
1612 #endif // REPLACEARGS_ANSI
1614                     delete[] base;
1615                 }
1616                 else
1617                 {
1618                     expandedArgs.push_back( parsed[i1] );
1619                 }
1620             }
1622             {
1623                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1624                 int iz = 0;
1625                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1626                 {
1627                     block[iz++] = *it;
1628                 }
1629                 parsed = block;
1630                 numArgs = expandedArgs.size();
1631             }
1633             std::vector<gchar*> newArgs;
1634             for ( int i = 0; i < numArgs; i++ )
1635             {
1636                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1637                 if ( replacement )
1638                 {
1639 #ifdef REPLACEARGS_DEBUG
1640                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1642                     if ( safe2 )
1643                     {
1644                         {
1645                             char tmp[1024];
1646                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1647                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1648                         }
1649                         g_free( safe2 );
1650                     }
1651 #endif // REPLACEARGS_DEBUG
1653                     newArgs.push_back( replacement );
1654                 }
1655                 else
1656                 {
1657                     newArgs.push_back( blankParam );
1658                 }
1659             }
1661             // Now push our munged params to be the new argv and argc
1662             {
1663                 char** block = new char*[newArgs.size()];
1664                 int iz = 0;
1665                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1666                 {
1667                     block[iz++] = *it;
1668                 }
1669                 argv = block;
1670                 argc = newArgs.size();
1671                 worked = true;
1672             }
1673         }
1674 #ifdef REPLACEARGS_DEBUG
1675         else
1676         {
1677             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1678         }
1679 #endif // REPLACEARGS_DEBUG
1680     }
1681 #ifdef REPLACEARGS_DEBUG
1682     else
1683     {
1684         {
1685             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1686         }
1688         char* line2 = GetCommandLineA();
1689         if ( line2 )
1690         {
1691             gchar *safe = Inkscape::IO::sanitizeString(line2);
1692             {
1693                 {
1694                     char tmp[strlen(safe) + 32];
1695                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1696                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1697                 }
1698             }
1699         }
1700         else
1701         {
1702             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1703         }
1704     }
1705 #endif // REPLACEARGS_DEBUG
1707     return worked;
1709 #endif // WIN32
1711 static GSList *
1712 sp_process_args(poptContext ctx)
1714     GSList *fl = NULL;
1716     gint a;
1717     while ((a = poptGetNextOpt(ctx)) != -1) {
1718         switch (a) {
1719             case SP_ARG_FILE: {
1720                 gchar const *fn = poptGetOptArg(ctx);
1721                 if (fn != NULL) {
1722                     fl = g_slist_append(fl, g_strdup(fn));
1723                 }
1724                 break;
1725             }
1726             case SP_ARG_VERSION: {
1727                 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1728                 exit(0);
1729                 break;
1730             }
1731             case SP_ARG_EXTENSIONDIR: {
1732                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1733                 exit(0);
1734                 break;
1735             }
1736             case SP_ARG_VERB_LIST: {
1737                 // This really shouldn't go here, we should init the app.
1738                 // But, since we're just exiting in this path, there is
1739                 // no harm, and this is really a better place to put
1740                 // everything else.
1741                 Inkscape::Extension::init();
1742                 Inkscape::Verb::list();
1743                 exit(0);
1744                 break;
1745             }
1746             case SP_ARG_VERB:
1747             case SP_ARG_SELECT: {
1748                 gchar const *arg = poptGetOptArg(ctx);
1749                 if (arg != NULL) {
1750                     // printf("Adding in: %s\n", arg);
1751                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1752                 }
1753                 break;
1754             }
1755             case POPT_ERROR_BADOPT: {
1756                 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1757                 exit(1);
1758                 break;
1759             }
1760             default: {
1761                 break;
1762             }
1763         }
1764     }
1766     gchar const ** const args = poptGetArgs(ctx);
1767     if (args != NULL) {
1768         for (unsigned i = 0; args[i] != NULL; i++) {
1769             fl = g_slist_append(fl, g_strdup(args[i]));
1770         }
1771     }
1773     return fl;
1777 /*
1778   Local Variables:
1779   mode:c++
1780   c-file-style:"stroustrup"
1781   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1782   indent-tabs-mode:nil
1783   fill-column:99
1784   End:
1785 */
1786 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :