Code

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