Code

Merging from trunk
[inkscape.git] / src / main.cpp
1 #define __MAIN_C__
3 /** \file
4  * Inkscape - an ambitious vector drawing program
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Frank Felfe <innerspace@iname.com>
9  *   Davide Puricelli <evo@debian.org>
10  *   Mitsuru Oka <oka326@parkcity.ne.jp>
11  *   Masatake YAMATO  <jet@gyve.org>
12  *   F.J.Franklin <F.J.Franklin@sheffield.ac.uk>
13  *   Michael Meeks <michael@helixcode.com>
14  *   Chema Celorio <chema@celorio.com>
15  *   Pawel Palucha
16  *   Bryce Harrington <bryce@bryceharrington.com>
17  * ... and various people who have worked with various projects
18  *
19  * Copyright (C) 1999-2004 authors
20  * Copyright (C) 2001-2002 Ximian, Inc.
21  *
22  * Released under GNU GPL, read the file 'COPYING' for more information
23  */
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29 #include "path-prefix.h"
31 #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>
40 #include <popt.h>
41 #ifndef POPT_TABLEEND
42 #define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
43 #endif /* Not def: POPT_TABLEEND */
45 #include <libxml/tree.h>
46 #include <glib-object.h>
47 #include <gtk/gtkmain.h>
48 #include <gtk/gtksignal.h>
49 #include <gtk/gtkwindow.h>
50 #include <gtk/gtkbox.h>
52 #include "gc-core.h"
54 #include "macros.h"
55 #include "file.h"
56 #include "document.h"
57 #include "sp-object.h"
58 #include "interface.h"
59 #include "print.h"
60 #include "color.h"
61 #include "sp-item.h"
62 #include "sp-root.h"
63 #include "unit-constants.h"
65 #include "svg/svg.h"
66 #include "svg/svg-color.h"
67 #include "svg/stringstream.h"
69 #include "inkscape-private.h"
70 #include "inkscape-stock.h"
71 #include "inkscape_version.h"
73 #include "sp-namedview.h"
74 #include "sp-guide.h"
75 #include "sp-object-repr.h"
76 #include "xml/repr.h"
78 #include "io/sys.h"
80 #include "debug/logger.h"
81 #include "debug/log-display-config.h"
83 #include "helper/png-write.h"
85 #include <extension/extension.h>
86 #include <extension/system.h>
87 #include <extension/db.h>
88 #include <extension/output.h>
90 #ifdef WIN32
91 //#define REPLACEARGS_ANSI
92 //#define REPLACEARGS_DEBUG
94 #include "registrytool.h"
96 #include "extension/internal/win32.h"
97 using Inkscape::Extension::Internal::PrintWin32;
99 #endif // WIN32
101 #include "extension/init.h"
103 #include <glibmm/i18n.h>
104 #include <gtkmm/main.h>
106 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
107 #define bind_textdomain_codeset(p,c)
108 #endif
110 #include "application/application.h"
112 #include "main-cmdlineact.h"
114 #include <png.h>
115 #include <errno.h>
117 enum {
118     SP_ARG_NONE,
119     SP_ARG_NOGUI,
120     SP_ARG_GUI,
121     SP_ARG_FILE,
122     SP_ARG_PRINT,
123     SP_ARG_EXPORT_PNG,
124     SP_ARG_EXPORT_DPI,
125     SP_ARG_EXPORT_AREA,
126     SP_ARG_EXPORT_AREA_DRAWING,
127     SP_ARG_EXPORT_AREA_CANVAS,
128     SP_ARG_EXPORT_AREA_SNAP,
129     SP_ARG_EXPORT_WIDTH,
130     SP_ARG_EXPORT_HEIGHT,
131     SP_ARG_EXPORT_ID,
132     SP_ARG_EXPORT_ID_ONLY,
133     SP_ARG_EXPORT_USE_HINTS,
134     SP_ARG_EXPORT_BACKGROUND,
135     SP_ARG_EXPORT_BACKGROUND_OPACITY,
136     SP_ARG_EXPORT_SVG,
137     SP_ARG_EXPORT_PS,
138     SP_ARG_EXPORT_EPS,
139     SP_ARG_EXPORT_PDF,
140 #ifdef WIN32
141     SP_ARG_EXPORT_EMF,
142 #endif //WIN32
143     SP_ARG_EXPORT_TEXT_TO_PATH,
144     SP_ARG_EXPORT_FONT,
145     SP_ARG_EXPORT_BBOX_PAGE,
146     SP_ARG_EXTENSIONDIR,
147     SP_ARG_FIT_PAGE_TO_DRAWING,
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(SPDocument* doc, gchar const* uri, char const *mime);
167 static void do_export_pdf(SPDocument* doc, gchar const* uri, char const *mime);
168 #ifdef WIN32
169 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
170 #endif //WIN32
171 static void do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id);
172 static void do_query_all (SPDocument *doc);
173 static void do_query_all_recurse (SPObject *o);
175 static gchar *sp_global_printer = NULL;
176 static gchar *sp_export_png = NULL;
177 static gchar *sp_export_dpi = NULL;
178 static gchar *sp_export_area = NULL;
179 static gboolean sp_export_area_drawing = FALSE;
180 static gboolean sp_export_area_canvas = FALSE;
181 static gchar *sp_export_width = NULL;
182 static gchar *sp_export_height = NULL;
183 static gchar *sp_export_id = NULL;
184 static gchar *sp_export_background = NULL;
185 static gchar *sp_export_background_opacity = NULL;
186 static gboolean sp_export_area_snap = FALSE;
187 static gboolean sp_export_use_hints = FALSE;
188 static gboolean sp_export_id_only = FALSE;
189 static gchar *sp_export_svg = NULL;
190 static gchar *sp_export_ps = NULL;
191 static gchar *sp_export_eps = NULL;
192 static gchar *sp_export_pdf = NULL;
193 #ifdef WIN32
194 static gchar *sp_export_emf = NULL;
195 #endif //WIN32
196 static gboolean sp_export_text_to_path = FALSE;
197 static gboolean sp_export_font = FALSE;
198 static gboolean sp_export_bbox_page = FALSE;
199 static gboolean sp_query_x = FALSE;
200 static gboolean sp_query_y = FALSE;
201 static gboolean sp_query_width = FALSE;
202 static gboolean sp_query_height = FALSE;
203 static gboolean sp_query_all = FALSE;
204 static gchar *sp_query_id = NULL;
205 static int sp_new_gui = FALSE;
206 static gboolean sp_shell = FALSE;
207 static gboolean sp_vacuum_defs = FALSE;
209 static gchar *sp_export_png_utf8 = NULL;
210 static gchar *sp_export_svg_utf8 = NULL;
211 static gchar *sp_global_printer_utf8 = NULL;
214 /**
215  *  Reset variables to default values.
216  */
217 static void resetCommandlineGlobals() {
218         sp_global_printer = NULL;
219         sp_export_png = NULL;
220         sp_export_dpi = NULL;
221         sp_export_area = NULL;
222         sp_export_area_drawing = FALSE;
223         sp_export_area_canvas = FALSE;
224         sp_export_width = NULL;
225         sp_export_height = NULL;
226         sp_export_id = NULL;
227         sp_export_background = NULL;
228         sp_export_background_opacity = NULL;
229         sp_export_area_snap = FALSE;
230         sp_export_use_hints = FALSE;
231         sp_export_id_only = FALSE;
232         sp_export_svg = NULL;
233         sp_export_ps = NULL;
234         sp_export_eps = NULL;
235         sp_export_pdf = NULL;
236 #ifdef WIN32
237         sp_export_emf = NULL;
238 #endif //WIN32
239         sp_export_text_to_path = FALSE;
240         sp_export_font = FALSE;
241         sp_export_bbox_page = FALSE;
242         sp_query_x = FALSE;
243         sp_query_y = FALSE;
244         sp_query_width = FALSE;
245         sp_query_height = FALSE;
246         sp_query_all = FALSE;
247         sp_query_id = NULL;
248         sp_vacuum_defs = FALSE;
250         sp_export_png_utf8 = NULL;
251         sp_export_svg_utf8 = NULL;
252         sp_global_printer_utf8 = NULL;
255 #ifdef WIN32
256 static bool replaceArgs( int& argc, char**& argv );
257 #endif
258 static GSList *sp_process_args(poptContext ctx);
259 struct poptOption options[] = {
260     {"version", 'V',
261      POPT_ARG_NONE, NULL, SP_ARG_VERSION,
262      N_("Print the Inkscape version number"),
263      NULL},
265     {"without-gui", 'z',
266      POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
267      N_("Do not use X server (only process files from console)"),
268      NULL},
270     {"with-gui", 'g',
271      POPT_ARG_NONE, NULL, SP_ARG_GUI,
272      N_("Try to use X server (even if $DISPLAY is not set)"),
273      NULL},
275     {"file", 'f',
276      POPT_ARG_STRING, NULL, SP_ARG_FILE,
277      N_("Open specified document(s) (option string may be excluded)"),
278      N_("FILENAME")},
280     {"print", 'p',
281      POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
282      N_("Print document(s) to specified output file (use '| program' for pipe)"),
283      N_("FILENAME")},
285     {"export-png", 'e',
286      POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
287      N_("Export document to a PNG file"),
288      N_("FILENAME")},
290     {"export-dpi", 'd',
291      POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
292      N_("The resolution used for exporting SVG into bitmap (default 90)"),
293      N_("DPI")},
295     {"export-area", 'a',
296      POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
297      N_("Exported area in SVG user units (default is the canvas; 0,0 is lower-left corner)"),
298      N_("x0:y0:x1:y1")},
300     {"export-area-drawing", 'D',
301      POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
302      N_("Exported area is the entire drawing (not canvas)"),
303      NULL},
305     {"export-area-canvas", 'C',
306      POPT_ARG_NONE, &sp_export_area_canvas, SP_ARG_EXPORT_AREA_CANVAS,
307      N_("Exported area is the entire canvas"),
308      NULL},
310     {"export-area-snap", 0,
311      POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
312      N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
313      NULL},
315     {"export-width", 'w',
316      POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
317      N_("The width of exported bitmap in pixels (overrides export-dpi)"),
318      N_("WIDTH")},
320     {"export-height", 'h',
321      POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
322      N_("The height of exported bitmap in pixels (overrides export-dpi)"),
323      N_("HEIGHT")},
325     {"export-id", 'i',
326      POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
327      N_("The ID of the object to export"),
328      N_("ID")},
330     {"export-id-only", 'j',
331      POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
332      // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
333      //  See "man inkscape" for details.
334      N_("Export just the object with export-id, hide all others (only with export-id)"),
335      NULL},
337     {"export-use-hints", 't',
338      POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
339      N_("Use stored filename and DPI hints when exporting (only with export-id)"),
340      NULL},
342     {"export-background", 'b',
343      POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
344      N_("Background color of exported bitmap (any SVG-supported color string)"),
345      N_("COLOR")},
347     {"export-background-opacity", 'y',
348      POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
349      N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
350      N_("VALUE")},
352     {"export-plain-svg", 'l',
353      POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
354      N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
355      N_("FILENAME")},
357     {"export-ps", 'P',
358      POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
359      N_("Export document to a PS file"),
360      N_("FILENAME")},
362     {"export-eps", 'E',
363      POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
364      N_("Export document to an EPS file"),
365      N_("FILENAME")},
367     {"export-pdf", 'A',
368      POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
369      N_("Export document to a PDF file"),
370      N_("FILENAME")},
372 #ifdef WIN32
373     {"export-emf", 'M',
374      POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
375      N_("Export document to an Enhanced Metafile (EMF) File"),
376      N_("FILENAME")},
377 #endif //WIN32
379     {"export-text-to-path", 'T',
380      POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
381      N_("Convert text object to paths on export (EPS)"),
382      NULL},
384     {"export-embed-fonts", 'F',
385      POPT_ARG_NONE, &sp_export_font, SP_ARG_EXPORT_FONT,
386      N_("Embed fonts on export (Type 1 only) (EPS)"),
387      NULL},
389     {"export-bbox-page", 'B',
390      POPT_ARG_NONE, &sp_export_bbox_page, SP_ARG_EXPORT_BBOX_PAGE,
391      N_("Export files with the bounding box set to the page size (EPS)"),
392      NULL},
394     {"query-x", 'X',
395      POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
396      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
397      N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
398      NULL},
400     {"query-y", 'Y',
401      POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
402      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
403      N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
404      NULL},
406     {"query-width", 'W',
407      POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
408      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
409      N_("Query the width of the drawing or, if specified, of the object with --query-id"),
410      NULL},
412     {"query-height", 'H',
413      POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
414      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
415      N_("Query the height of the drawing or, if specified, of the object with --query-id"),
416      NULL},
418     {"query-all", 'S',
419      POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
420      N_("List id,x,y,w,h for all objects"),
421      NULL},
423     {"query-id", 'I',
424      POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
425      N_("The ID of the object whose dimensions are queried"),
426      N_("ID")},
428     {"extension-directory", 'x',
429      POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
430      // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
431      N_("Print out the extension directory and exit"),
432      NULL},
434     {"vacuum-defs", 0,
435      POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
436      N_("Remove unused definitions from the defs section(s) of the document"),
437      NULL},
439     {"verb-list", 0,
440      POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
441      N_("List the IDs of all the verbs in Inkscape"),
442      NULL},
444     {"verb", 0,
445      POPT_ARG_STRING, NULL, SP_ARG_VERB,
446      N_("Verb to call when Inkscape opens."),
447      N_("VERB-ID")},
449     {"select", 0,
450      POPT_ARG_STRING, NULL, SP_ARG_SELECT,
451      N_("Object ID to select when Inkscape opens."),
452      N_("OBJECT-ID")},
454     {"shell", 0,
455      POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
456      N_("Start Inkscape in interative shell mode."),
457      NULL},
459     POPT_AUTOHELP POPT_TABLEEND
460 };
462 static bool needToRecodeParams = true;
463 gchar * blankParam = g_strdup("");
467 #ifdef WIN32
469 /**
470  * Return the directory of the .exe that is currently running
471  */
472 static Glib::ustring _win32_getExePath()
474     char exeName[MAX_PATH+1];
475     GetModuleFileName(NULL, exeName, MAX_PATH);
476     char *slashPos = strrchr(exeName, '\\');
477     if (slashPos)
478         *slashPos = '\0';
479     Glib::ustring s = exeName;
480     return s;
483 /**
484  * Set up the PATH and PYTHONPATH environment variables on
485  * win32
486  */
487 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
490     char *oldenv = getenv("PATH");
491     Glib::ustring tmp = "PATH=";
492     tmp += exePath;
493     tmp += ";";
494     tmp += exePath;
495     tmp += "\\python;";
496     tmp += exePath;
497     tmp += "\\python\\Scripts;";  // for uniconv.cmd
498     tmp += exePath;
499     tmp += "\\perl";
500     if(oldenv != NULL) {
501         tmp += ";";
502         tmp += oldenv;
503     }
504     _putenv(tmp.c_str());
506     oldenv = getenv("PYTHONPATH");
507     tmp = "PYTHONPATH=";
508     tmp += exePath;
509     tmp += "\\python;";
510     tmp += exePath;
511     tmp += "\\python\\Lib;";
512     tmp += exePath;
513     tmp += "\\python\\DLLs";
514     if(oldenv != NULL) {
515         tmp += ";";
516         tmp += oldenv;
517     }
518     _putenv(tmp.c_str());
520     return 0;
522 #endif
526 /**
527  * This is the classic main() entry point of the program, though on some
528  * architectures it might be called by something else.
529  */
530 int
531 main(int argc, char **argv)
533 #ifdef HAVE_FPSETMASK
534     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
535        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
536        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
537     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
538 #endif
540 #ifdef WIN32
541     /*
542       Set the current directory to the directory of the
543       executable.  This seems redundant, but is needed for
544       when inkscape.exe is executed from another directory.
545       We use relative paths on win32.
546       HKCR\svgfile\shell\open\command is a good example
547     */
548     Glib::ustring homedir = _win32_getExePath();
549     SetCurrentDirectory(homedir.c_str());
550     _win32_set_inkscape_env(homedir);
551     RegistryTool rt;
552     rt.setPathInfo();
553 #endif
555    /**
556     * Call bindtextdomain() for various machines's paths
557     */
558 #ifdef ENABLE_NLS
559 #ifdef WIN32
560     Glib::ustring localePath = homedir;
561     localePath += "\\";
562     localePath += PACKAGE_LOCALE_DIR;
563     bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
564 #else
565 #ifdef ENABLE_BINRELOC
566     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
567 #else
568     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
569 #endif
570 #endif
571     // Allow the user to override the locale directory by setting
572     // the environment variable INKSCAPE_LOCALEDIR.
573     char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
574     if (inkscape_localedir != NULL) {
575         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
576     }
577 #endif
579     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
581 #ifdef ENABLE_NLS
582     textdomain(GETTEXT_PACKAGE);
583 #endif
585     LIBXML_TEST_VERSION
587     Inkscape::GC::init();
589     Inkscape::Debug::Logger::init();
591     gboolean use_gui;
593 #ifndef WIN32
594     use_gui = (getenv("DISPLAY") != NULL);
595 #else
596     use_gui = TRUE;
597 #endif
598     /* Test whether with/without GUI is forced */
599     for (int i = 1; i < argc; i++) {
600         if (!strcmp(argv[i], "-z")
601             || !strcmp(argv[i], "--without-gui")
602             || !strcmp(argv[i], "-p")
603             || !strncmp(argv[i], "--print", 7)
604             || !strcmp(argv[i], "-e")
605             || !strncmp(argv[i], "--export-png", 12)
606             || !strcmp(argv[i], "-l")
607             || !strncmp(argv[i], "--export-plain-svg", 12)
608             || !strcmp(argv[i], "-i")
609             || !strncmp(argv[i], "--export-area-drawing", 21)
610             || !strcmp(argv[i], "-D")
611             || !strncmp(argv[i], "--export-area-canvas", 20)
612             || !strcmp(argv[i], "-C")
613             || !strncmp(argv[i], "--export-id", 12)
614             || !strcmp(argv[i], "-P")
615             || !strncmp(argv[i], "--export-ps", 11)
616             || !strcmp(argv[i], "-E")
617             || !strncmp(argv[i], "--export-eps", 12)
618             || !strcmp(argv[i], "-A")
619             || !strncmp(argv[i], "--export-pdf", 12)
620 #ifdef WIN32
621             || !strcmp(argv[i], "-M")
622             || !strncmp(argv[i], "--export-emf", 12)
623 #endif //WIN32
624             || !strcmp(argv[i], "-W")
625             || !strncmp(argv[i], "--query-width", 13)
626             || !strcmp(argv[i], "-H")
627             || !strncmp(argv[i], "--query-height", 14)
628             || !strcmp(argv[i], "-S")
629             || !strncmp(argv[i], "--query-all", 11)
630             || !strcmp(argv[i], "-X")
631             || !strncmp(argv[i], "--query-x", 13)
632             || !strcmp(argv[i], "-Y")
633             || !strncmp(argv[i], "--query-y", 14)
634             || !strcmp(argv[i], "--vacuum-defs")
635             || !strncmp(argv[i], "--shell", 7)
636            )
637         {
638             /* main_console handles any exports -- not the gui */
639             use_gui = FALSE;
640             break;
641         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
642             use_gui = TRUE;
643             break;
644         }
645     }
647 #ifdef WIN32
648 #ifndef REPLACEARGS_ANSI
649     if ( PrintWin32::is_os_wide() )
650 #endif // REPLACEARGS_ANSI
651     {
652         // If the call fails, we'll need to convert charsets
653         needToRecodeParams = !replaceArgs( argc, argv );
654     }
655 #endif // WIN32
657     /// \todo  Should this be a static object (see inkscape.cpp)?
658     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
660     return app.run();
666 void fixupSingleFilename( gchar **orig, gchar **spare )
668     if ( orig && *orig && **orig ) {
669         GError *error = NULL;
670         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
671         if ( newFileName )
672         {
673             *orig = newFileName;
674             if ( spare ) {
675                 *spare = newFileName;
676             }
677 //             g_message("Set a replacement fixup");
678         }
679     }
684 GSList *fixupFilenameEncoding( GSList* fl )
686     GSList *newFl = NULL;
687     while ( fl ) {
688         gchar *fn = static_cast<gchar*>(fl->data);
689         fl = g_slist_remove( fl, fl->data );
690         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
691         if ( newFileName ) {
693             if ( 0 )
694             {
695                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
696                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
697                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
698                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
699                 gtk_dialog_run (GTK_DIALOG (w));
700                 gtk_widget_destroy (w);
701                 g_free(safeNewFn);
702                 g_free(safeFn);
703             }
705             g_free( fn );
706             fn = newFileName;
707             newFileName = 0;
708         }
709         else
710             if ( 0 )
711         {
712             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
713             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
714             gtk_dialog_run (GTK_DIALOG (w));
715             gtk_widget_destroy (w);
716             g_free(safeFn);
717         }
718         newFl = g_slist_append( newFl, fn );
719     }
720     return newFl;
723 int sp_common_main( int argc, char const **argv, GSList **flDest )
725     /// \todo fixme: Move these to some centralized location (Lauris)
726     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
727     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
730     // temporarily switch gettext encoding to locale, so that help messages can be output properly
731     gchar const *charset;
732     g_get_charset(&charset);
734     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
736     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
737     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
738     g_return_val_if_fail(ctx != NULL, 1);
740     /* Collect own arguments */
741     GSList *fl = sp_process_args(ctx);
742     poptFreeContext(ctx);
744     // now switch gettext back to UTF-8 (for GUI)
745     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
747     // Now let's see if the file list still holds up
748     if ( needToRecodeParams )
749     {
750         fl = fixupFilenameEncoding( fl );
751     }
753     // Check the globals for filename-fixup
754     if ( needToRecodeParams )
755     {
756         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
757         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
758         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
759     }
760     else
761     {
762         if ( sp_export_png )
763             sp_export_png_utf8 = g_strdup( sp_export_png );
764         if ( sp_export_svg )
765             sp_export_svg_utf8 = g_strdup( sp_export_svg );
766         if ( sp_global_printer )
767             sp_global_printer_utf8 = g_strdup( sp_global_printer );
768     }
770     // Return the list if wanted, else free it up.
771     if ( flDest ) {
772         *flDest = fl;
773         fl = 0;
774     } else {
775         while ( fl ) {
776             g_free( fl->data );
777             fl = g_slist_remove( fl, fl->data );
778         }
779     }
780     return 0;
783 static void
784 snooper(GdkEvent *event, gpointer /*data*/) {
785     if(inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
786     {
787         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
788         switch (event->type) {
789             case GDK_MOTION_NOTIFY:
790                 if(event->motion.state & mapping) {
791                     event->motion.state|=GDK_MOD1_MASK;
792                 }
793                 break;
794             case GDK_BUTTON_PRESS:
795                 if(event->button.state & mapping) {
796                     event->button.state|=GDK_MOD1_MASK;
797                 }
798                 break;
799              case GDK_KEY_PRESS:
800                  if(event->key.state & mapping) {
801                      event->key.state|=GDK_MOD1_MASK;
802                  }
803                  break;
804         default:
805             break;
806         }
807     }
808     gtk_main_do_event (event);
811 int
812 sp_main_gui(int argc, char const **argv)
814     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
816     GSList *fl = NULL;
817     int retVal = sp_common_main( argc, argv, &fl );
818     g_return_val_if_fail(retVal == 0, 1);
820     inkscape_gtk_stock_init();
822     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
824     Inkscape::Debug::log_display_config();
826     /* Set default icon */
827     gchar *filename = (gchar *) g_build_filename (INKSCAPE_APPICONDIR, "inkscape.png", NULL);
828     if (Inkscape::IO::file_test(filename, (GFileTest)(G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))) {
829         gtk_window_set_default_icon_from_file(filename, NULL);
830     }
831     g_free (filename);
832     filename = 0;
834     gboolean create_new = TRUE;
836     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
837     inkscape_application_init(argv[0], true);
839     while (fl) {
840         if (sp_file_open((gchar *)fl->data,NULL)) {
841             create_new=FALSE;
842         }
843         fl = g_slist_remove(fl, fl->data);
844     }
845     if (create_new) {
846         sp_file_new_default();
847     }
849     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
850     main_instance.run();
852 #ifdef WIN32
853     //We might not need anything here
854     //sp_win32_finish(); <-- this is a NOP func
855 #endif
857     return 0;
860 /**
861  * Process file list
862  */
863 void sp_process_file_list(GSList *fl)
865     while (fl) {
866         const gchar *filename = (gchar *)fl->data;
867         SPDocument *doc = Inkscape::Extension::open(NULL, filename);
868         if (doc == NULL) {
869             doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
870         }
871         if (doc == NULL) {
872             g_warning("Specified document %s cannot be opened (is it a valid SVG file?)", filename);
873         } else {
874             if (sp_vacuum_defs) {
875                 vacuum_document(doc);
876             }
877             if (sp_vacuum_defs && !sp_export_svg) {
878                 // save under the name given in the command line
879                 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
880             }
881             if (sp_global_printer) {
882                 sp_print_document_to_file(doc, sp_global_printer);
883             }
884             if (sp_export_png) {
885                 sp_do_export_png(doc);
886             }
887             if (sp_export_svg) {
888                 Inkscape::XML::Document *rdoc;
889                 Inkscape::XML::Node *repr;
890                 rdoc = sp_repr_document_new("svg:svg");
891                 repr = rdoc->root();
892                 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
893                 sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
894             }
895             if (sp_export_ps) {
896                 do_export_ps(doc, sp_export_ps, "image/x-postscript");
897             }
898             if (sp_export_eps) {
899                 do_export_ps(doc, sp_export_eps, "image/x-e-postscript");
900             }
901             if (sp_export_pdf) {
902                 do_export_pdf(doc, sp_export_pdf, "application/pdf");
903                 }
904 #ifdef WIN32
905                 if (sp_export_emf) {
906                     do_export_emf(doc, sp_export_emf, "image/x-emf");
907                 }
908 #endif //WIN32
909             if (sp_query_all) {
910                 do_query_all (doc);
911             } else if (sp_query_width || sp_query_height) {
912                 do_query_dimension (doc, true, sp_query_width? NR::X : NR::Y, sp_query_id);
913             } else if (sp_query_x || sp_query_y) {
914                 do_query_dimension (doc, false, sp_query_x? NR::X : NR::Y, sp_query_id);
915             }
917             delete doc;
918         }
919         fl = g_slist_remove(fl, fl->data);
920     }
923 /**
924  * Run the application as an interactive shell, parsing command lines from stdin
925  * Returns -1 on error.
926  */
927 int sp_main_shell(char const* command_name)
929     int retval = 0;
931     const unsigned int buffer_size = 4096;
932     gchar *command_line = g_strnfill(buffer_size, 0);
933     g_strlcpy(command_line, command_name, buffer_size);
934     gsize offset = g_strlcat(command_line, " ", buffer_size);
935     gsize sizeLeft = buffer_size - offset;
936     gchar *useme = command_line + offset;
938     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", INKSCAPE_VERSION);
939     fflush(stdout);
940     char* linedata = 0;
941     do {
942         fprintf(stdout, ">");
943         fflush(stdout);
944         if ((linedata = fgets(useme, sizeLeft, stdin))) {
945             size_t len = strlen(useme);
946             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
947                 fprintf(stdout, "ERROR: Command line too long\n");
948                 // Consume rest of line
949                 retval = -1; // If the while loop completes, this remains -1
950                 while (fgets(useme, sizeLeft, stdin) && retval) {
951                     len = strlen(command_line);
952                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
953                         retval = 0;
954                     }
955                 }
956             } else {
957                 useme[--len] = '\0';  // Strip newline
958                 if (useme[len - 1] == '\r') {
959                     useme[--len] = '\0';
960                 }
961                 if ( strcmp(useme, "quit") == 0 ) {
962                     // Time to quit
963                     fflush(stdout);
964                     linedata = 0; // mark for exit
965                 } else if ( len < 1 ) {
966                     // blank string. Do nothing.
967                 } else {
968                     GError* parseError = 0;
969                     gchar** argv = 0;
970                     gint argc = 0;
971                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
972                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
973                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
974                         if ( ctx ) {
975                             GSList *fl = sp_process_args(ctx);
976                             sp_process_file_list(fl);
977                             poptFreeContext(ctx);
978                         } else {
979                             retval = 1; // not sure why. But this was the previous return value
980                         }
981                         resetCommandlineGlobals();
982                         g_strfreev(argv);
983                     } else {
984                         g_warning("problem parsing commandline: %s", useme);
985                     }
986                 }
987             }
988         } // if (linedata...
989     } while (linedata && (retval == 0));
991     g_free(command_line);
992     return retval;
995 int sp_main_console(int argc, char const **argv)
997     /* We are started in text mode */
999     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1000      * in a non-Gtk environment.  Used in libnrtype's
1001      * FontInstance.cpp and FontFactory.cpp.
1002      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1003      */
1004     g_type_init();
1005     char **argv2 = const_cast<char **>(argv);
1006     gtk_init_check( &argc, &argv2 );
1007     //setlocale(LC_ALL, "");
1009     GSList *fl = NULL;
1010     int retVal = sp_common_main( argc, argv, &fl );
1011     g_return_val_if_fail(retVal == 0, 1);
1013     if (fl == NULL && !sp_shell) {
1014         g_print("Nothing to do!\n");
1015         exit(0);
1016     }
1018     inkscape_application_init(argv[0], false);
1020     if (sp_shell) {
1021         sp_main_shell(argv[0]); // Run as interactive shell
1022         exit(0);
1023     } else {
1024         sp_process_file_list(fl); // Normal command line invokation
1025     }
1026     inkscape_unref();
1028     return 0;
1031 static void
1032 do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id)
1034     SPObject *o = NULL;
1036     if (id) {
1037         o = doc->getObjectById(id);
1038         if (o) {
1039             if (!SP_IS_ITEM (o)) {
1040                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1041                 return;
1042             }
1043         } else {
1044             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1045             return;
1046         }
1047     } else {
1048         o = SP_DOCUMENT_ROOT(doc);
1049     }
1051     if (o) {
1052         sp_document_ensure_up_to_date (doc);
1053         SPItem *item = ((SPItem *) o);
1055         // "true" SVG bbox for scripting
1056         boost::optional<Geom::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
1057         if (area) {
1058             Inkscape::SVGOStringStream os;
1059             if (extent) {
1060                 os << area->dimensions()[axis];
1061             } else {
1062                 os << area->min()[axis];
1063             }
1064             g_print ("%s", os.str().c_str());
1065         } else {
1066             g_print("0");
1067         }
1068     }
1071 static void
1072 do_query_all (SPDocument *doc)
1074     SPObject *o = NULL;
1076     o = SP_DOCUMENT_ROOT(doc);
1078     if (o) {
1079         sp_document_ensure_up_to_date (doc);
1080         do_query_all_recurse(o);
1081     }
1084 static void
1085 do_query_all_recurse (SPObject *o)
1087     SPItem *item = ((SPItem *) o);
1088     if (o->id && SP_IS_ITEM(item)) {
1089         boost::optional<Geom::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
1090         if (area) {
1091             Inkscape::SVGOStringStream os;
1092             os << o->id;
1093             os << "," << area->min()[Geom::X];
1094             os << "," << area->min()[Geom::Y];
1095             os << "," << area->dimensions()[Geom::X];
1096             os << "," << area->dimensions()[Geom::Y];
1097             g_print ("%s\n", os.str().c_str());
1098         }
1099     }
1101     SPObject *child = o->children;
1102     while (child) {
1103         do_query_all_recurse (child);
1104         child = child->next;
1105     }
1109 static void
1110 sp_do_export_png(SPDocument *doc)
1112     const gchar *filename = NULL;
1113     gdouble dpi = 0.0;
1115     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1116         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1117     }
1119     GSList *items = NULL;
1121     NRRect area;
1122     if (sp_export_id || sp_export_area_drawing) {
1124         SPObject *o = NULL;
1125         SPObject *o_area = NULL;
1126         if (sp_export_id && sp_export_area_drawing) {
1127             o = doc->getObjectById(sp_export_id);
1128             o_area = SP_DOCUMENT_ROOT (doc);
1129         } else if (sp_export_id) {
1130             o = doc->getObjectById(sp_export_id);
1131             o_area = o;
1132         } else if (sp_export_area_drawing) {
1133             o = SP_DOCUMENT_ROOT (doc);
1134             o_area = o;
1135         }
1137         if (o) {
1138             if (!SP_IS_ITEM (o)) {
1139                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1140                 return;
1141             }
1143             items = g_slist_prepend (items, SP_ITEM(o));
1145             if (sp_export_id_only) {
1146                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1147             }
1149             if (sp_export_use_hints) {
1151                 // retrieve export filename hint
1152                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1153                 if (fn_hint) {
1154                     if (sp_export_png) {
1155                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1156                         filename = sp_export_png;
1157                     } else {
1158                         filename = fn_hint;
1159                     }
1160                 } else {
1161                     g_warning ("Export filename hint not found for the object.");
1162                     filename = sp_export_png;
1163                 }
1165                 // retrieve export dpi hints
1166                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1167                 if (dpi_hint) {
1168                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1169                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1170                     } else {
1171                         dpi = atof(dpi_hint);
1172                     }
1173                 } else {
1174                     g_warning ("Export DPI hint not found for the object.");
1175                 }
1177             }
1179             // write object bbox to area
1180             sp_document_ensure_up_to_date (doc);
1181             boost::optional<Geom::Rect> areaMaybe;
1182             sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2r_affine((SPItem *) o_area), TRUE);
1183             if (areaMaybe) {
1184                 area = NRRect(areaMaybe);
1185             } else {
1186                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1187                 return;
1188             }
1189         } else {
1190             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1191             return;
1192         }
1193     }
1195     if (sp_export_area) {
1196         /* Try to parse area (given in SVG pixels) */
1197         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &area.x0, &area.y0, &area.x1, &area.y1) == 4) {
1198             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1199             return;
1200         }
1201         if ((area.x0 >= area.x1) || (area.y0 >= area.y1)) {
1202             g_warning("Export area '%s' has negative width or height. Nothing exported.", sp_export_area);
1203             return;
1204         }
1205     } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1206         /* Export the whole canvas */
1207         sp_document_ensure_up_to_date (doc);
1208         area.x0 = SP_ROOT(doc->root)->x.computed;
1209         area.y0 = SP_ROOT(doc->root)->y.computed;
1210         area.x1 = area.x0 + sp_document_width (doc);
1211         area.y1 = area.y0 + sp_document_height (doc);
1212     }
1214     // set filename and dpi from options, if not yet set from the hints
1215     if (!filename) {
1216         if (!sp_export_png) {
1217             g_warning ("No export filename given and no filename hint. Nothing exported.");
1218             return;
1219         }
1220         filename = sp_export_png;
1221     }
1223     if (sp_export_dpi && dpi == 0.0) {
1224         dpi = atof(sp_export_dpi);
1225         if ((dpi < 0.1) || (dpi > 10000.0)) {
1226             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1227             return;
1228         }
1229         g_print("DPI: %g\n", dpi);
1230     }
1232     if (sp_export_area_snap) {
1233         area.x0 = std::floor (area.x0);
1234         area.y0 = std::floor (area.y0);
1235         area.x1 = std::ceil (area.x1);
1236         area.y1 = std::ceil (area.y1);
1237     }
1239     // default dpi
1240     if (dpi == 0.0) {
1241         dpi = PX_PER_IN;
1242     }
1244     unsigned long int width = 0;
1245     unsigned long int height = 0;
1247     if (sp_export_width) {
1248         errno=0;
1249         width = strtoul(sp_export_width, NULL, 0);
1250         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1251             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1252             return;
1253         }
1254         dpi = (gdouble) width * PX_PER_IN / (area.x1 - area.x0);
1255     }
1257     if (sp_export_height) {
1258         errno=0;
1259         height = strtoul(sp_export_height, NULL, 0);
1260         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1261             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1262             return;
1263         }
1264         dpi = (gdouble) height * PX_PER_IN / (area.y1 - area.y0);
1265     }
1267     if (!sp_export_width) {
1268         width = (unsigned long int) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1269     }
1271     if (!sp_export_height) {
1272         height = (unsigned long int) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1273     }
1275     guint32 bgcolor = 0x00000000;
1276     if (sp_export_background) {
1277         // override the page color
1278         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1279         bgcolor |= 0xff; // default is no opacity
1280     } else {
1281         // read from namedview
1282         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1283         if (nv && nv->attribute("pagecolor"))
1284             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1285         if (nv && nv->attribute("inkscape:pageopacity"))
1286             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1287     }
1289     if (sp_export_background_opacity) {
1290         // override opacity
1291         gfloat value;
1292         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1293             if (value > 1.0) {
1294                 value = CLAMP (value, 1.0f, 255.0f);
1295                 bgcolor &= (guint32) 0xffffff00;
1296                 bgcolor |= (guint32) floor(value);
1297             } else {
1298                 value = CLAMP (value, 0.0f, 1.0f);
1299                 bgcolor &= (guint32) 0xffffff00;
1300                 bgcolor |= SP_COLOR_F_TO_U(value);
1301             }
1302         }
1303     }
1305     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1307     g_print("Area %g:%g:%g:%g exported to %lu x %lu pixels (%g dpi)\n", area.x0, area.y0, area.x1, area.y1, width, height, dpi);
1309     g_print("Bitmap saved as: %s\n", filename);
1311     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1312         sp_export_png_file(doc, filename, area.x0, area.y0, area.x1, area.y1, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1313     } else {
1314         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1315     }
1317     g_slist_free (items);
1321 /**
1322  *  Perform an export of either PS or EPS.
1323  *
1324  *  \param doc Document to export.
1325  *  \param uri URI to export to.
1326  *  \param mime MIME type to export as.
1327  */
1329 static void do_export_ps(SPDocument* doc, gchar const* uri, char const* mime)
1331     Inkscape::Extension::DB::OutputList o;
1332     Inkscape::Extension::db.get_output_list(o);
1333     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1334     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1335         i++;
1336     }
1338     if (i == o.end())
1339     {
1340         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1341         return;
1342     }
1344     bool old_text_to_path = false;
1345     bool old_font_embedded = false;
1346     bool old_bbox_page = false;
1348     try {
1349         old_text_to_path = (*i)->get_param_bool("textToPath");
1350         (*i)->set_param_bool("textToPath", sp_export_text_to_path);
1351     }
1352     catch (...) {
1353         g_warning ("Could not set export-text-to-path option for this export.");
1354     }
1356     try {
1357         old_font_embedded = (*i)->get_param_bool("fontEmbedded");
1358         (*i)->set_param_bool("fontEmbedded", sp_export_font);
1359     }
1360     catch (...) {
1361         g_warning ("Could not set export-font option for this export.");
1362     }
1364     try {
1365         old_bbox_page = (*i)->get_param_bool("pageBoundingBox");
1366         (*i)->set_param_bool("pageBoundingBox", sp_export_bbox_page);
1367     }
1368     catch (...) {
1369         g_warning ("Could not set export-bbox-page option for this export.");
1370     }
1372     (*i)->save(doc, uri);
1374     try {
1375         (*i)->set_param_bool("textToPath", old_text_to_path);
1376         (*i)->set_param_bool("fontEmbedded", old_font_embedded);
1377         (*i)->set_param_bool("pageBoundingBox", old_bbox_page);
1378     }
1379     catch (...) {
1381     }
1384 /**
1385  *  Perform a PDF export
1386  *
1387  *  \param doc Document to export.
1388  *  \param uri URI to export to.
1389  *  \param mime MIME type to export as.
1390  */
1392 static void do_export_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1394     Inkscape::Extension::DB::OutputList o;
1395     Inkscape::Extension::db.get_output_list(o);
1396     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1397     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1398         i++;
1399     }
1401     if (i == o.end())
1402     {
1403         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1404         return;
1405     }
1407     if (sp_export_id) {
1408         SPObject *o = doc->getObjectById(sp_export_id);
1409         if (o == NULL) {
1410             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1411             return;
1412         }
1413         (*i)->set_param_string ("exportId", sp_export_id);
1414     } else {
1415         (*i)->set_param_string ("exportId", "");
1416     }
1418     if (sp_export_area_drawing) {
1419         (*i)->set_param_bool ("exportDrawing", TRUE);
1420     } else {
1421         (*i)->set_param_bool ("exportDrawing", FALSE);
1422     }
1424     if (sp_export_area_canvas) {
1425         (*i)->set_param_bool ("exportCanvas", TRUE);
1426     } else {
1427         (*i)->set_param_bool ("exportCanvas", FALSE);
1428     }
1430     if (sp_export_text_to_path) {
1431         (*i)->set_param_bool("textToPath", TRUE);
1432     } else {
1433         (*i)->set_param_bool("textToPath", FALSE);
1434     }
1436     (*i)->save(doc, uri);
1439 #ifdef WIN32
1440 /**
1441  *  Export a document to EMF
1442  *
1443  *  \param doc Document to export.
1444  *  \param uri URI to export to.
1445  *  \param mime MIME type to export as (should be "image/x-emf")
1446  */
1448 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1450     Inkscape::Extension::DB::OutputList o;
1451     Inkscape::Extension::db.get_output_list(o);
1452     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1453     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1454         i++;
1455     }
1457     if (i == o.end())
1458     {
1459         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1460         return;
1461     }
1463     (*i)->save(doc, uri);
1465 #endif //WIN32
1467 #ifdef WIN32
1468 bool replaceArgs( int& argc, char**& argv )
1470     bool worked = false;
1472 #ifdef REPLACEARGS_DEBUG
1473     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1474 #endif // REPLACEARGS_DEBUG
1476     wchar_t* line = GetCommandLineW();
1477     if ( line )
1478     {
1479 #ifdef REPLACEARGS_DEBUG
1480         {
1481             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1482             if ( utf8Line )
1483             {
1484                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1485                 {
1486                     char tmp[strlen(safe) + 32];
1487                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1488                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1489                 }
1490             }
1491         }
1492 #endif // REPLACEARGS_DEBUG
1494         int numArgs = 0;
1495         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1497 #ifdef REPLACEARGS_ANSI
1498 // test code for trying things on Win95/98/ME
1499         if ( !parsed )
1500         {
1501 #ifdef REPLACEARGS_DEBUG
1502             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1503 #endif // REPLACEARGS_DEBUG
1504             int lineLen = wcslen(line) + 1;
1505             wchar_t* lineDup = new wchar_t[lineLen];
1506             wcsncpy( lineDup, line, lineLen );
1508             int pos = 0;
1509             bool inQuotes = false;
1510             bool inWhitespace = true;
1511             std::vector<int> places;
1512             while ( lineDup[pos] )
1513             {
1514                 if ( inQuotes )
1515                 {
1516                     if ( lineDup[pos] == L'"' )
1517                     {
1518                         inQuotes = false;
1519                     }
1520                 }
1521                 else if ( lineDup[pos] == L'"' )
1522                 {
1523                     inQuotes = true;
1524                     inWhitespace = false;
1525                     places.push_back(pos);
1526                 }
1527                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1528                 {
1529                     if ( !inWhitespace )
1530                     {
1531                         inWhitespace = true;
1532                         lineDup[pos] = 0;
1533                     }
1534                 }
1535                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1536                 {
1537                     inWhitespace = false;
1538                     places.push_back(pos);
1539                 }
1540                 else
1541                 {
1542                     // consume
1543                 }
1544                 pos++;
1545             }
1546 #ifdef REPLACEARGS_DEBUG
1547             {
1548                 char tmp[256];
1549                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1550                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1551             }
1552 #endif // REPLACEARGS_DEBUG
1554             wchar_t** block = new wchar_t*[places.size()];
1555             int i = 0;
1556             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1557             {
1558                 block[i++] = &lineDup[*it];
1559             }
1560             parsed = block;
1561             numArgs = places.size();
1562         }
1563 #endif // REPLACEARGS_ANSI
1565         if ( parsed )
1566         {
1567             std::vector<wchar_t*>expandedArgs;
1568             if ( numArgs > 0 )
1569             {
1570                 expandedArgs.push_back( parsed[0] );
1571             }
1573             for ( int i1 = 1; i1 < numArgs; i1++ )
1574             {
1575                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1576                 wildcarded &= parsed[i1][0] != L'"';
1577                 wildcarded &= parsed[i1][0] != L'-';
1578                 if ( wildcarded )
1579                 {
1580 #ifdef REPLACEARGS_ANSI
1581                     WIN32_FIND_DATAA data;
1582 #else
1583                     WIN32_FIND_DATAW data;
1584 #endif // REPLACEARGS_ANSI
1586                     memset((void *)&data, 0, sizeof(data));
1588                     int baseLen = wcslen(parsed[i1]) + 2;
1589                     wchar_t* base = new wchar_t[baseLen];
1590                     wcsncpy( base, parsed[i1], baseLen );
1591                     wchar_t* last = wcsrchr( base, L'\\' );
1592                     if ( last )
1593                     {
1594                         last[1] = 0;
1595                     }
1596                     else
1597                     {
1598                         base[0] = 0;
1599                     }
1600                     baseLen = wcslen( base );
1602 #ifdef REPLACEARGS_ANSI
1603                     char target[MAX_PATH];
1604                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1605                     {
1606                         HANDLE hf = FindFirstFileA( target, &data );
1607 #else
1608                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1609 #endif // REPLACEARGS_ANSI
1610                         if ( hf != INVALID_HANDLE_VALUE )
1611                         {
1612                             BOOL found = TRUE;
1613                             do
1614                             {
1615 #ifdef REPLACEARGS_ANSI
1616                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1617                                 if ( howMany > 0 )
1618                                 {
1619                                     howMany += baseLen;
1620                                     wchar_t* tmp = new wchar_t[howMany + 1];
1621                                     wcsncpy( tmp, base, howMany + 1 );
1622                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1623                                     expandedArgs.push_back( tmp );
1624                                     found = FindNextFileA( hf, &data );
1625                                 }
1626 #else
1627                                 int howMany = wcslen(data.cFileName) + baseLen;
1628                                 wchar_t* tmp = new wchar_t[howMany + 1];
1629                                 wcsncpy( tmp, base, howMany + 1 );
1630                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1631                                 expandedArgs.push_back( tmp );
1632                                 found = FindNextFileW( hf, &data );
1633 #endif // REPLACEARGS_ANSI
1634                             } while ( found );
1636                             FindClose( hf );
1637                         }
1638                         else
1639                         {
1640                             expandedArgs.push_back( parsed[i1] );
1641                         }
1642 #ifdef REPLACEARGS_ANSI
1643                     }
1644 #endif // REPLACEARGS_ANSI
1646                     delete[] base;
1647                 }
1648                 else
1649                 {
1650                     expandedArgs.push_back( parsed[i1] );
1651                 }
1652             }
1654             {
1655                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1656                 int iz = 0;
1657                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1658                 {
1659                     block[iz++] = *it;
1660                 }
1661                 parsed = block;
1662                 numArgs = expandedArgs.size();
1663             }
1665             std::vector<gchar*> newArgs;
1666             for ( int i = 0; i < numArgs; i++ )
1667             {
1668                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1669                 if ( replacement )
1670                 {
1671 #ifdef REPLACEARGS_DEBUG
1672                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1674                     if ( safe2 )
1675                     {
1676                         {
1677                             char tmp[1024];
1678                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1679                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1680                         }
1681                         g_free( safe2 );
1682                     }
1683 #endif // REPLACEARGS_DEBUG
1685                     newArgs.push_back( replacement );
1686                 }
1687                 else
1688                 {
1689                     newArgs.push_back( blankParam );
1690                 }
1691             }
1693             // Now push our munged params to be the new argv and argc
1694             {
1695                 char** block = new char*[newArgs.size()];
1696                 int iz = 0;
1697                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1698                 {
1699                     block[iz++] = *it;
1700                 }
1701                 argv = block;
1702                 argc = newArgs.size();
1703                 worked = true;
1704             }
1705         }
1706 #ifdef REPLACEARGS_DEBUG
1707         else
1708         {
1709             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1710         }
1711 #endif // REPLACEARGS_DEBUG
1712     }
1713 #ifdef REPLACEARGS_DEBUG
1714     else
1715     {
1716         {
1717             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1718         }
1720         char* line2 = GetCommandLineA();
1721         if ( line2 )
1722         {
1723             gchar *safe = Inkscape::IO::sanitizeString(line2);
1724             {
1725                 {
1726                     char tmp[strlen(safe) + 32];
1727                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1728                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1729                 }
1730             }
1731         }
1732         else
1733         {
1734             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1735         }
1736     }
1737 #endif // REPLACEARGS_DEBUG
1739     return worked;
1741 #endif // WIN32
1743 static GSList *
1744 sp_process_args(poptContext ctx)
1746     GSList *fl = NULL;
1748     gint a;
1749     while ((a = poptGetNextOpt(ctx)) >= 0) {
1750         switch (a) {
1751             case SP_ARG_FILE: {
1752                 gchar const *fn = poptGetOptArg(ctx);
1753                 if (fn != NULL) {
1754                     fl = g_slist_append(fl, g_strdup(fn));
1755                 }
1756                 break;
1757             }
1758             case SP_ARG_VERSION: {
1759                 printf("Inkscape %s (%s)\n", INKSCAPE_VERSION, __DATE__);
1760                 exit(0);
1761                 break;
1762             }
1763             case SP_ARG_EXTENSIONDIR: {
1764                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1765                 exit(0);
1766                 break;
1767             }
1768             case SP_ARG_VERB_LIST: {
1769                 // This really shouldn't go here, we should init the app.
1770                 // But, since we're just exiting in this path, there is
1771                 // no harm, and this is really a better place to put
1772                 // everything else.
1773                 Inkscape::Extension::init();
1774                 Inkscape::Verb::list();
1775                 exit(0);
1776                 break;
1777             }
1778             case SP_ARG_VERB:
1779             case SP_ARG_SELECT: {
1780                 gchar const *arg = poptGetOptArg(ctx);
1781                 if (arg != NULL) {
1782                     // printf("Adding in: %s\n", arg);
1783                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1784                 }
1785                 break;
1786             }
1787             default: {
1788                 break;
1789             }
1790         }
1791     }
1793     gchar const ** const args = poptGetArgs(ctx);
1794     if (args != NULL) {
1795         for (unsigned i = 0; args[i] != NULL; i++) {
1796             fl = g_slist_append(fl, g_strdup(args[i]));
1797         }
1798     }
1800     return fl;
1804 /*
1805   Local Variables:
1806   mode:c++
1807   c-file-style:"stroustrup"
1808   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1809   indent-tabs-mode:nil
1810   fill-column:99
1811   End:
1812 */
1813 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :