Code

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