Code

the proper way to delete a SPDocument, according to mental
[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             }
916             Inkscape::GC::release (doc); 
917         }
918         fl = g_slist_remove(fl, fl->data);
919     }
922 /**
923  * Run the application as an interactive shell, parsing command lines from stdin
924  * Returns -1 on error.
925  */
926 int sp_main_shell(char const* command_name)
928     int retval = 0;
930     const unsigned int buffer_size = 4096;
931     gchar *command_line = g_strnfill(buffer_size, 0);
932     g_strlcpy(command_line, command_name, buffer_size);
933     gsize offset = g_strlcat(command_line, " ", buffer_size);
934     gsize sizeLeft = buffer_size - offset;
935     gchar *useme = command_line + offset;
937     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", INKSCAPE_VERSION);
938     fflush(stdout);
939     char* linedata = 0;
940     do {
941         fprintf(stdout, ">");
942         fflush(stdout);
943         if ((linedata = fgets(useme, sizeLeft, stdin))) {
944             size_t len = strlen(useme);
945             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
946                 fprintf(stdout, "ERROR: Command line too long\n");
947                 // Consume rest of line
948                 retval = -1; // If the while loop completes, this remains -1
949                 while (fgets(useme, sizeLeft, stdin) && retval) {
950                     len = strlen(command_line);
951                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
952                         retval = 0;
953                     }
954                 }
955             } else {
956                 useme[--len] = '\0';  // Strip newline
957                 if (useme[len - 1] == '\r') {
958                     useme[--len] = '\0';
959                 }
960                 if ( strcmp(useme, "quit") == 0 ) {
961                     // Time to quit
962                     fflush(stdout);
963                     linedata = 0; // mark for exit
964                 } else if ( len < 1 ) {
965                     // blank string. Do nothing.
966                 } else {
967                     GError* parseError = 0;
968                     gchar** argv = 0;
969                     gint argc = 0;
970                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
971                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
972                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
973                         if ( ctx ) {
974                             GSList *fl = sp_process_args(ctx);
975                             sp_process_file_list(fl);
976                             poptFreeContext(ctx);
977                         } else {
978                             retval = 1; // not sure why. But this was the previous return value
979                         }
980                         resetCommandlineGlobals();
981                         g_strfreev(argv);
982                     } else {
983                         g_warning("problem parsing commandline: %s", useme);
984                     }
985                 }
986             }
987         } // if (linedata...
988     } while (linedata && (retval == 0));
990     g_free(command_line);
991     return retval;
994 int sp_main_console(int argc, char const **argv)
996     /* We are started in text mode */
998     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
999      * in a non-Gtk environment.  Used in libnrtype's
1000      * FontInstance.cpp and FontFactory.cpp.
1001      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1002      */
1003     g_type_init();
1004     char **argv2 = const_cast<char **>(argv);
1005     gtk_init_check( &argc, &argv2 );
1006     //setlocale(LC_ALL, "");
1008     GSList *fl = NULL;
1009     int retVal = sp_common_main( argc, argv, &fl );
1010     g_return_val_if_fail(retVal == 0, 1);
1012     if (fl == NULL && !sp_shell) {
1013         g_print("Nothing to do!\n");
1014         exit(0);
1015     }
1017     inkscape_application_init(argv[0], false);
1019     if (sp_shell) {
1020         sp_main_shell(argv[0]); // Run as interactive shell
1021         exit(0);
1022     } else {
1023         sp_process_file_list(fl); // Normal command line invokation
1024     }
1025     inkscape_unref();
1027     return 0;
1030 static void
1031 do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id)
1033     SPObject *o = NULL;
1035     if (id) {
1036         o = doc->getObjectById(id);
1037         if (o) {
1038             if (!SP_IS_ITEM (o)) {
1039                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1040                 return;
1041             }
1042         } else {
1043             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1044             return;
1045         }
1046     } else {
1047         o = SP_DOCUMENT_ROOT(doc);
1048     }
1050     if (o) {
1051         sp_document_ensure_up_to_date (doc);
1052         SPItem *item = ((SPItem *) o);
1054         // "true" SVG bbox for scripting
1055         boost::optional<NR::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
1056         if (area) {
1057             Inkscape::SVGOStringStream os;
1058             if (extent) {
1059                 os << area->extent(axis);
1060             } else {
1061                 os << area->min()[axis];
1062             }
1063             g_print ("%s", os.str().c_str());
1064         } else {
1065             g_print("0");
1066         }
1067     }
1070 static void
1071 do_query_all (SPDocument *doc)
1073     SPObject *o = NULL;
1075     o = SP_DOCUMENT_ROOT(doc);
1077     if (o) {
1078         sp_document_ensure_up_to_date (doc);
1079         do_query_all_recurse(o);
1080     }
1083 static void
1084 do_query_all_recurse (SPObject *o)
1086     SPItem *item = ((SPItem *) o);
1087     if (o->id && SP_IS_ITEM(item)) {
1088         boost::optional<NR::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
1089         if (area) {
1090             Inkscape::SVGOStringStream os;
1091             os << o->id;
1092             os << "," << area->min()[NR::X];
1093             os << "," << area->min()[NR::Y];
1094             os << "," << area->extent(NR::X);
1095             os << "," << area->extent(NR::Y);
1096             g_print ("%s\n", os.str().c_str());
1097         }
1098     }
1100     SPObject *child = o->children;
1101     while (child) {
1102         do_query_all_recurse (child);
1103         child = child->next;
1104     }
1108 static void
1109 sp_do_export_png(SPDocument *doc)
1111     const gchar *filename = NULL;
1112     gdouble dpi = 0.0;
1114     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1115         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1116     }
1118     GSList *items = NULL;
1120     NRRect area;
1121     if (sp_export_id || sp_export_area_drawing) {
1123         SPObject *o = NULL;
1124         SPObject *o_area = NULL;
1125         if (sp_export_id && sp_export_area_drawing) {
1126             o = doc->getObjectById(sp_export_id);
1127             o_area = SP_DOCUMENT_ROOT (doc);
1128         } else if (sp_export_id) {
1129             o = doc->getObjectById(sp_export_id);
1130             o_area = o;
1131         } else if (sp_export_area_drawing) {
1132             o = SP_DOCUMENT_ROOT (doc);
1133             o_area = o;
1134         }
1136         if (o) {
1137             if (!SP_IS_ITEM (o)) {
1138                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1139                 return;
1140             }
1142             items = g_slist_prepend (items, SP_ITEM(o));
1144             if (sp_export_id_only) {
1145                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1146             }
1148             if (sp_export_use_hints) {
1150                 // retrieve export filename hint
1151                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1152                 if (fn_hint) {
1153                     if (sp_export_png) {
1154                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1155                         filename = sp_export_png;
1156                     } else {
1157                         filename = fn_hint;
1158                     }
1159                 } else {
1160                     g_warning ("Export filename hint not found for the object.");
1161                     filename = sp_export_png;
1162                 }
1164                 // retrieve export dpi hints
1165                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1166                 if (dpi_hint) {
1167                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1168                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1169                     } else {
1170                         dpi = atof(dpi_hint);
1171                     }
1172                 } else {
1173                     g_warning ("Export DPI hint not found for the object.");
1174                 }
1176             }
1178             // write object bbox to area
1179             sp_document_ensure_up_to_date (doc);
1180             boost::optional<NR::Rect> areaMaybe;
1181             sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2r_affine((SPItem *) o_area), TRUE);
1182             if (areaMaybe) {
1183                 area = NRRect(areaMaybe);
1184             } else {
1185                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1186                 return;
1187             }
1188         } else {
1189             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1190             return;
1191         }
1192     }
1194     if (sp_export_area) {
1195         /* Try to parse area (given in SVG pixels) */
1196         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &area.x0, &area.y0, &area.x1, &area.y1) == 4) {
1197             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1198             return;
1199         }
1200         if ((area.x0 >= area.x1) || (area.y0 >= area.y1)) {
1201             g_warning("Export area '%s' has negative width or height. Nothing exported.", sp_export_area);
1202             return;
1203         }
1204     } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1205         /* Export the whole canvas */
1206         sp_document_ensure_up_to_date (doc);
1207         area.x0 = SP_ROOT(doc->root)->x.computed;
1208         area.y0 = SP_ROOT(doc->root)->y.computed;
1209         area.x1 = area.x0 + sp_document_width (doc);
1210         area.y1 = area.y0 + sp_document_height (doc);
1211     }
1213     // set filename and dpi from options, if not yet set from the hints
1214     if (!filename) {
1215         if (!sp_export_png) {
1216             g_warning ("No export filename given and no filename hint. Nothing exported.");
1217             return;
1218         }
1219         filename = sp_export_png;
1220     }
1222     if (sp_export_dpi && dpi == 0.0) {
1223         dpi = atof(sp_export_dpi);
1224         if ((dpi < 0.1) || (dpi > 10000.0)) {
1225             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1226             return;
1227         }
1228         g_print("DPI: %g\n", dpi);
1229     }
1231     if (sp_export_area_snap) {
1232         area.x0 = std::floor (area.x0);
1233         area.y0 = std::floor (area.y0);
1234         area.x1 = std::ceil (area.x1);
1235         area.y1 = std::ceil (area.y1);
1236     }
1238     // default dpi
1239     if (dpi == 0.0) {
1240         dpi = PX_PER_IN;
1241     }
1243     unsigned long int width = 0;
1244     unsigned long int height = 0;
1246     if (sp_export_width) {
1247         errno=0;
1248         width = strtoul(sp_export_width, NULL, 0);
1249         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1250             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1251             return;
1252         }
1253         dpi = (gdouble) width * PX_PER_IN / (area.x1 - area.x0);
1254     }
1256     if (sp_export_height) {
1257         errno=0;
1258         height = strtoul(sp_export_height, NULL, 0);
1259         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1260             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1261             return;
1262         }
1263         dpi = (gdouble) height * PX_PER_IN / (area.y1 - area.y0);
1264     }
1266     if (!sp_export_width) {
1267         width = (unsigned long int) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1268     }
1270     if (!sp_export_height) {
1271         height = (unsigned long int) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1272     }
1274     guint32 bgcolor = 0x00000000;
1275     if (sp_export_background) {
1276         // override the page color
1277         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1278         bgcolor |= 0xff; // default is no opacity
1279     } else {
1280         // read from namedview
1281         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1282         if (nv && nv->attribute("pagecolor"))
1283             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1284         if (nv && nv->attribute("inkscape:pageopacity"))
1285             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1286     }
1288     if (sp_export_background_opacity) {
1289         // override opacity
1290         gfloat value;
1291         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1292             if (value > 1.0) {
1293                 value = CLAMP (value, 1.0f, 255.0f);
1294                 bgcolor &= (guint32) 0xffffff00;
1295                 bgcolor |= (guint32) floor(value);
1296             } else {
1297                 value = CLAMP (value, 0.0f, 1.0f);
1298                 bgcolor &= (guint32) 0xffffff00;
1299                 bgcolor |= SP_COLOR_F_TO_U(value);
1300             }
1301         }
1302     }
1304     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1306     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);
1308     g_print("Bitmap saved as: %s\n", filename);
1310     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1311         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);
1312     } else {
1313         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1314     }
1316     g_slist_free (items);
1320 /**
1321  *  Perform an export of either PS or EPS.
1322  *
1323  *  \param doc Document to export.
1324  *  \param uri URI to export to.
1325  *  \param mime MIME type to export as.
1326  */
1328 static void do_export_ps(SPDocument* doc, gchar const* uri, char const* mime)
1330     Inkscape::Extension::DB::OutputList o;
1331     Inkscape::Extension::db.get_output_list(o);
1332     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1333     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1334         i++;
1335     }
1337     if (i == o.end())
1338     {
1339         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1340         return;
1341     }
1343     bool old_text_to_path = false;
1344     bool old_font_embedded = false;
1345     bool old_bbox_page = false;
1347     try {
1348         old_text_to_path = (*i)->get_param_bool("textToPath");
1349         (*i)->set_param_bool("textToPath", sp_export_text_to_path);
1350     }
1351     catch (...) {
1352         g_warning ("Could not set export-text-to-path option for this export.");
1353     }
1355     try {
1356         old_font_embedded = (*i)->get_param_bool("fontEmbedded");
1357         (*i)->set_param_bool("fontEmbedded", sp_export_font);
1358     }
1359     catch (...) {
1360         g_warning ("Could not set export-font option for this export.");
1361     }
1363     try {
1364         old_bbox_page = (*i)->get_param_bool("pageBoundingBox");
1365         (*i)->set_param_bool("pageBoundingBox", sp_export_bbox_page);
1366     }
1367     catch (...) {
1368         g_warning ("Could not set export-bbox-page option for this export.");
1369     }
1371     (*i)->save(doc, uri);
1373     try {
1374         (*i)->set_param_bool("textToPath", old_text_to_path);
1375         (*i)->set_param_bool("fontEmbedded", old_font_embedded);
1376         (*i)->set_param_bool("pageBoundingBox", old_bbox_page);
1377     }
1378     catch (...) {
1380     }
1383 /**
1384  *  Perform a PDF export
1385  *
1386  *  \param doc Document to export.
1387  *  \param uri URI to export to.
1388  *  \param mime MIME type to export as.
1389  */
1391 static void do_export_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1393     Inkscape::Extension::DB::OutputList o;
1394     Inkscape::Extension::db.get_output_list(o);
1395     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1396     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1397         i++;
1398     }
1400     if (i == o.end())
1401     {
1402         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1403         return;
1404     }
1406     if (sp_export_id) {
1407         SPObject *o = doc->getObjectById(sp_export_id);
1408         if (o == NULL) {
1409             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1410             return;
1411         }
1412         (*i)->set_param_string ("exportId", sp_export_id);
1413     } else {
1414         (*i)->set_param_string ("exportId", "");
1415     }
1417     if (sp_export_area_drawing) {
1418         (*i)->set_param_bool ("exportDrawing", TRUE);
1419     } else {
1420         (*i)->set_param_bool ("exportDrawing", FALSE);
1421     }
1423     if (sp_export_area_canvas) {
1424         (*i)->set_param_bool ("exportCanvas", TRUE);
1425     } else {
1426         (*i)->set_param_bool ("exportCanvas", FALSE);
1427     }
1429     if (sp_export_text_to_path) {
1430         (*i)->set_param_bool("textToPath", TRUE);
1431     } else {
1432         (*i)->set_param_bool("textToPath", FALSE);
1433     }
1435     (*i)->save(doc, uri);
1438 #ifdef WIN32
1439 /**
1440  *  Export a document to EMF
1441  *
1442  *  \param doc Document to export.
1443  *  \param uri URI to export to.
1444  *  \param mime MIME type to export as (should be "image/x-emf")
1445  */
1447 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1449     Inkscape::Extension::DB::OutputList o;
1450     Inkscape::Extension::db.get_output_list(o);
1451     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1452     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1453         i++;
1454     }
1456     if (i == o.end())
1457     {
1458         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1459         return;
1460     }
1462     (*i)->save(doc, uri);
1464 #endif //WIN32
1466 #ifdef WIN32
1467 bool replaceArgs( int& argc, char**& argv )
1469     bool worked = false;
1471 #ifdef REPLACEARGS_DEBUG
1472     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1473 #endif // REPLACEARGS_DEBUG
1475     wchar_t* line = GetCommandLineW();
1476     if ( line )
1477     {
1478 #ifdef REPLACEARGS_DEBUG
1479         {
1480             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1481             if ( utf8Line )
1482             {
1483                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1484                 {
1485                     char tmp[strlen(safe) + 32];
1486                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1487                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1488                 }
1489             }
1490         }
1491 #endif // REPLACEARGS_DEBUG
1493         int numArgs = 0;
1494         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1496 #ifdef REPLACEARGS_ANSI
1497 // test code for trying things on Win95/98/ME
1498         if ( !parsed )
1499         {
1500 #ifdef REPLACEARGS_DEBUG
1501             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1502 #endif // REPLACEARGS_DEBUG
1503             int lineLen = wcslen(line) + 1;
1504             wchar_t* lineDup = new wchar_t[lineLen];
1505             wcsncpy( lineDup, line, lineLen );
1507             int pos = 0;
1508             bool inQuotes = false;
1509             bool inWhitespace = true;
1510             std::vector<int> places;
1511             while ( lineDup[pos] )
1512             {
1513                 if ( inQuotes )
1514                 {
1515                     if ( lineDup[pos] == L'"' )
1516                     {
1517                         inQuotes = false;
1518                     }
1519                 }
1520                 else if ( lineDup[pos] == L'"' )
1521                 {
1522                     inQuotes = true;
1523                     inWhitespace = false;
1524                     places.push_back(pos);
1525                 }
1526                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1527                 {
1528                     if ( !inWhitespace )
1529                     {
1530                         inWhitespace = true;
1531                         lineDup[pos] = 0;
1532                     }
1533                 }
1534                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1535                 {
1536                     inWhitespace = false;
1537                     places.push_back(pos);
1538                 }
1539                 else
1540                 {
1541                     // consume
1542                 }
1543                 pos++;
1544             }
1545 #ifdef REPLACEARGS_DEBUG
1546             {
1547                 char tmp[256];
1548                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1549                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1550             }
1551 #endif // REPLACEARGS_DEBUG
1553             wchar_t** block = new wchar_t*[places.size()];
1554             int i = 0;
1555             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1556             {
1557                 block[i++] = &lineDup[*it];
1558             }
1559             parsed = block;
1560             numArgs = places.size();
1561         }
1562 #endif // REPLACEARGS_ANSI
1564         if ( parsed )
1565         {
1566             std::vector<wchar_t*>expandedArgs;
1567             if ( numArgs > 0 )
1568             {
1569                 expandedArgs.push_back( parsed[0] );
1570             }
1572             for ( int i1 = 1; i1 < numArgs; i1++ )
1573             {
1574                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1575                 wildcarded &= parsed[i1][0] != L'"';
1576                 wildcarded &= parsed[i1][0] != L'-';
1577                 if ( wildcarded )
1578                 {
1579 #ifdef REPLACEARGS_ANSI
1580                     WIN32_FIND_DATAA data;
1581 #else
1582                     WIN32_FIND_DATAW data;
1583 #endif // REPLACEARGS_ANSI
1585                     memset((void *)&data, 0, sizeof(data));
1587                     int baseLen = wcslen(parsed[i1]) + 2;
1588                     wchar_t* base = new wchar_t[baseLen];
1589                     wcsncpy( base, parsed[i1], baseLen );
1590                     wchar_t* last = wcsrchr( base, L'\\' );
1591                     if ( last )
1592                     {
1593                         last[1] = 0;
1594                     }
1595                     else
1596                     {
1597                         base[0] = 0;
1598                     }
1599                     baseLen = wcslen( base );
1601 #ifdef REPLACEARGS_ANSI
1602                     char target[MAX_PATH];
1603                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1604                     {
1605                         HANDLE hf = FindFirstFileA( target, &data );
1606 #else
1607                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1608 #endif // REPLACEARGS_ANSI
1609                         if ( hf != INVALID_HANDLE_VALUE )
1610                         {
1611                             BOOL found = TRUE;
1612                             do
1613                             {
1614 #ifdef REPLACEARGS_ANSI
1615                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1616                                 if ( howMany > 0 )
1617                                 {
1618                                     howMany += baseLen;
1619                                     wchar_t* tmp = new wchar_t[howMany + 1];
1620                                     wcsncpy( tmp, base, howMany + 1 );
1621                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1622                                     expandedArgs.push_back( tmp );
1623                                     found = FindNextFileA( hf, &data );
1624                                 }
1625 #else
1626                                 int howMany = wcslen(data.cFileName) + baseLen;
1627                                 wchar_t* tmp = new wchar_t[howMany + 1];
1628                                 wcsncpy( tmp, base, howMany + 1 );
1629                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1630                                 expandedArgs.push_back( tmp );
1631                                 found = FindNextFileW( hf, &data );
1632 #endif // REPLACEARGS_ANSI
1633                             } while ( found );
1635                             FindClose( hf );
1636                         }
1637                         else
1638                         {
1639                             expandedArgs.push_back( parsed[i1] );
1640                         }
1641 #ifdef REPLACEARGS_ANSI
1642                     }
1643 #endif // REPLACEARGS_ANSI
1645                     delete[] base;
1646                 }
1647                 else
1648                 {
1649                     expandedArgs.push_back( parsed[i1] );
1650                 }
1651             }
1653             {
1654                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1655                 int iz = 0;
1656                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1657                 {
1658                     block[iz++] = *it;
1659                 }
1660                 parsed = block;
1661                 numArgs = expandedArgs.size();
1662             }
1664             std::vector<gchar*> newArgs;
1665             for ( int i = 0; i < numArgs; i++ )
1666             {
1667                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1668                 if ( replacement )
1669                 {
1670 #ifdef REPLACEARGS_DEBUG
1671                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1673                     if ( safe2 )
1674                     {
1675                         {
1676                             char tmp[1024];
1677                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1678                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1679                         }
1680                         g_free( safe2 );
1681                     }
1682 #endif // REPLACEARGS_DEBUG
1684                     newArgs.push_back( replacement );
1685                 }
1686                 else
1687                 {
1688                     newArgs.push_back( blankParam );
1689                 }
1690             }
1692             // Now push our munged params to be the new argv and argc
1693             {
1694                 char** block = new char*[newArgs.size()];
1695                 int iz = 0;
1696                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1697                 {
1698                     block[iz++] = *it;
1699                 }
1700                 argv = block;
1701                 argc = newArgs.size();
1702                 worked = true;
1703             }
1704         }
1705 #ifdef REPLACEARGS_DEBUG
1706         else
1707         {
1708             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1709         }
1710 #endif // REPLACEARGS_DEBUG
1711     }
1712 #ifdef REPLACEARGS_DEBUG
1713     else
1714     {
1715         {
1716             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1717         }
1719         char* line2 = GetCommandLineA();
1720         if ( line2 )
1721         {
1722             gchar *safe = Inkscape::IO::sanitizeString(line2);
1723             {
1724                 {
1725                     char tmp[strlen(safe) + 32];
1726                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1727                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1728                 }
1729             }
1730         }
1731         else
1732         {
1733             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1734         }
1735     }
1736 #endif // REPLACEARGS_DEBUG
1738     return worked;
1740 #endif // WIN32
1742 static GSList *
1743 sp_process_args(poptContext ctx)
1745     GSList *fl = NULL;
1747     gint a;
1748     while ((a = poptGetNextOpt(ctx)) >= 0) {
1749         switch (a) {
1750             case SP_ARG_FILE: {
1751                 gchar const *fn = poptGetOptArg(ctx);
1752                 if (fn != NULL) {
1753                     fl = g_slist_append(fl, g_strdup(fn));
1754                 }
1755                 break;
1756             }
1757             case SP_ARG_VERSION: {
1758                 printf("Inkscape %s (%s)\n", INKSCAPE_VERSION, __DATE__);
1759                 exit(0);
1760                 break;
1761             }
1762             case SP_ARG_EXTENSIONDIR: {
1763                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1764                 exit(0);
1765                 break;
1766             }
1767             case SP_ARG_VERB_LIST: {
1768                 // This really shouldn't go here, we should init the app.
1769                 // But, since we're just exiting in this path, there is
1770                 // no harm, and this is really a better place to put
1771                 // everything else.
1772                 Inkscape::Extension::init();
1773                 Inkscape::Verb::list();
1774                 exit(0);
1775                 break;
1776             }
1777             case SP_ARG_VERB:
1778             case SP_ARG_SELECT: {
1779                 gchar const *arg = poptGetOptArg(ctx);
1780                 if (arg != NULL) {
1781                     // printf("Adding in: %s\n", arg);
1782                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1783                 }
1784                 break;
1785             }
1786             default: {
1787                 break;
1788             }
1789         }
1790     }
1792     gchar const ** const args = poptGetArgs(ctx);
1793     if (args != NULL) {
1794         for (unsigned i = 0; args[i] != NULL; i++) {
1795             fl = g_slist_append(fl, g_strdup(args[i]));
1796         }
1797     }
1799     return fl;
1803 /*
1804   Local Variables:
1805   mode:c++
1806   c-file-style:"stroustrup"
1807   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1808   indent-tabs-mode:nil
1809   fill-column:99
1810   End:
1811 */
1812 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :