Code

Connector tool: make connectors avoid the convex hull of shapes.
[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>
94 #include <extension/input.h>
96 #ifdef WIN32
97 //#define REPLACEARGS_ANSI
98 //#define REPLACEARGS_DEBUG
100 #include "registrytool.h"
102 #include "extension/internal/win32.h"
103 using Inkscape::Extension::Internal::PrintWin32;
105 #endif // WIN32
107 #include "extension/init.h"
109 #include <glibmm/i18n.h>
110 #include <gtkmm/main.h>
112 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
113 #define bind_textdomain_codeset(p,c)
114 #endif
116 #include "application/application.h"
117 #include "main-cmdlineact.h"
118 #include "widgets/icon.h"
119 #include "ui/widget/panel.h"
121 #include <errno.h>
123 enum {
124     SP_ARG_NONE,
125     SP_ARG_NOGUI,
126     SP_ARG_GUI,
127     SP_ARG_FILE,
128     SP_ARG_PRINT,
129     SP_ARG_EXPORT_PNG,
130     SP_ARG_EXPORT_DPI,
131     SP_ARG_EXPORT_AREA,
132     SP_ARG_EXPORT_AREA_DRAWING,
133     SP_ARG_EXPORT_AREA_PAGE,
134     SP_ARG_EXPORT_AREA_SNAP,
135     SP_ARG_EXPORT_WIDTH,
136     SP_ARG_EXPORT_HEIGHT,
137     SP_ARG_EXPORT_ID,
138     SP_ARG_EXPORT_ID_ONLY,
139     SP_ARG_EXPORT_USE_HINTS,
140     SP_ARG_EXPORT_BACKGROUND,
141     SP_ARG_EXPORT_BACKGROUND_OPACITY,
142     SP_ARG_EXPORT_SVG,
143     SP_ARG_EXPORT_PS,
144     SP_ARG_EXPORT_EPS,
145     SP_ARG_EXPORT_PDF,
146 #ifdef WIN32
147     SP_ARG_EXPORT_EMF,
148 #endif //WIN32
149     SP_ARG_EXPORT_TEXT_TO_PATH,
150     SP_ARG_EXPORT_IGNORE_FILTERS,
151     SP_ARG_EXTENSIONDIR,
152     SP_ARG_QUERY_X,
153     SP_ARG_QUERY_Y,
154     SP_ARG_QUERY_WIDTH,
155     SP_ARG_QUERY_HEIGHT,
156     SP_ARG_QUERY_ALL,
157     SP_ARG_QUERY_ID,
158     SP_ARG_SHELL,
159     SP_ARG_VERSION,
160     SP_ARG_VACUUM_DEFS,
161     SP_ARG_VERB_LIST,
162     SP_ARG_VERB,
163     SP_ARG_SELECT,
164     SP_ARG_LAST
165 };
167 int sp_main_gui(int argc, char const **argv);
168 int sp_main_console(int argc, char const **argv);
169 static void sp_do_export_png(SPDocument *doc);
170 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime);
171 #ifdef WIN32
172 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
173 #endif //WIN32
174 static void do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id);
175 static void do_query_all (SPDocument *doc);
176 static void do_query_all_recurse (SPObject *o);
178 static gchar *sp_global_printer = NULL;
179 static gchar *sp_export_png = NULL;
180 static gchar *sp_export_dpi = NULL;
181 static gchar *sp_export_area = NULL;
182 static gboolean sp_export_area_drawing = FALSE;
183 static gboolean sp_export_area_page = FALSE;
184 static gchar *sp_export_width = NULL;
185 static gchar *sp_export_height = NULL;
186 static gchar *sp_export_id = NULL;
187 static gchar *sp_export_background = NULL;
188 static gchar *sp_export_background_opacity = NULL;
189 static gboolean sp_export_area_snap = FALSE;
190 static gboolean sp_export_use_hints = FALSE;
191 static gboolean sp_export_id_only = FALSE;
192 static gchar *sp_export_svg = NULL;
193 static gchar *sp_export_ps = NULL;
194 static gchar *sp_export_eps = NULL;
195 static gchar *sp_export_pdf = NULL;
196 #ifdef WIN32
197 static gchar *sp_export_emf = NULL;
198 #endif //WIN32
199 static gboolean sp_export_text_to_path = FALSE;
200 static gboolean sp_export_ignore_filters = FALSE;
201 static gboolean sp_export_font = FALSE;
202 static gboolean sp_query_x = FALSE;
203 static gboolean sp_query_y = FALSE;
204 static gboolean sp_query_width = FALSE;
205 static gboolean sp_query_height = FALSE;
206 static gboolean sp_query_all = FALSE;
207 static gchar *sp_query_id = NULL;
208 static int sp_new_gui = FALSE;
209 static gboolean sp_shell = FALSE;
210 static gboolean sp_vacuum_defs = FALSE;
212 static gchar *sp_export_png_utf8 = NULL;
213 static gchar *sp_export_svg_utf8 = NULL;
214 static gchar *sp_global_printer_utf8 = NULL;
217 /**
218  *  Reset variables to default values.
219  */
220 static void resetCommandlineGlobals() {
221         sp_global_printer = NULL;
222         sp_export_png = NULL;
223         sp_export_dpi = NULL;
224         sp_export_area = NULL;
225         sp_export_area_drawing = FALSE;
226         sp_export_area_page = FALSE;
227         sp_export_width = NULL;
228         sp_export_height = NULL;
229         sp_export_id = NULL;
230         sp_export_background = NULL;
231         sp_export_background_opacity = NULL;
232         sp_export_area_snap = FALSE;
233         sp_export_use_hints = FALSE;
234         sp_export_id_only = FALSE;
235         sp_export_svg = NULL;
236         sp_export_ps = NULL;
237         sp_export_eps = NULL;
238         sp_export_pdf = NULL;
239 #ifdef WIN32
240         sp_export_emf = NULL;
241 #endif //WIN32
242         sp_export_text_to_path = FALSE;
243         sp_export_ignore_filters = FALSE;
244         sp_export_font = FALSE;
245         sp_query_x = FALSE;
246         sp_query_y = FALSE;
247         sp_query_width = FALSE;
248         sp_query_height = FALSE;
249         sp_query_all = FALSE;
250         sp_query_id = NULL;
251         sp_vacuum_defs = FALSE;
253         sp_export_png_utf8 = NULL;
254         sp_export_svg_utf8 = NULL;
255         sp_global_printer_utf8 = NULL;
258 #ifdef WIN32
259 static bool replaceArgs( int& argc, char**& argv );
260 #endif
261 static GSList *sp_process_args(poptContext ctx);
262 struct poptOption options[] = {
263     {"version", 'V',
264      POPT_ARG_NONE, NULL, SP_ARG_VERSION,
265      N_("Print the Inkscape version number"),
266      NULL},
268     {"without-gui", 'z',
269      POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
270      N_("Do not use X server (only process files from console)"),
271      NULL},
273     {"with-gui", 'g',
274      POPT_ARG_NONE, NULL, SP_ARG_GUI,
275      N_("Try to use X server (even if $DISPLAY is not set)"),
276      NULL},
278     {"file", 'f',
279      POPT_ARG_STRING, NULL, SP_ARG_FILE,
280      N_("Open specified document(s) (option string may be excluded)"),
281      N_("FILENAME")},
283     {"print", 'p',
284      POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
285      N_("Print document(s) to specified output file (use '| program' for pipe)"),
286      N_("FILENAME")},
288     {"export-png", 'e',
289      POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
290      N_("Export document to a PNG file"),
291      N_("FILENAME")},
293     {"export-dpi", 'd',
294      POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
295      N_("Resolution for exporting to bitmap and for rasterization of filters in PS/EPS/PDF (default 90)"),
296      N_("DPI")},
298     {"export-area", 'a',
299      POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
300      N_("Exported area in SVG user units (default is the page; 0,0 is lower-left corner)"),
301      N_("x0:y0:x1:y1")},
303     {"export-area-drawing", 'D',
304      POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
305      N_("Exported area is the entire drawing (not page)"),
306      NULL},
308     {"export-area-page", 'C',
309      POPT_ARG_NONE, &sp_export_area_page, SP_ARG_EXPORT_AREA_PAGE,
310      N_("Exported area is the entire page"),
311      NULL},
313     {"export-area-snap", 0,
314      POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
315      N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
316      NULL},
318     {"export-width", 'w',
319      POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
320      N_("The width of exported bitmap in pixels (overrides export-dpi)"),
321      N_("WIDTH")},
323     {"export-height", 'h',
324      POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
325      N_("The height of exported bitmap in pixels (overrides export-dpi)"),
326      N_("HEIGHT")},
328     {"export-id", 'i',
329      POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
330      N_("The ID of the object to export"),
331      N_("ID")},
333     {"export-id-only", 'j',
334      POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
335      // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
336      //  See "man inkscape" for details.
337      N_("Export just the object with export-id, hide all others (only with export-id)"),
338      NULL},
340     {"export-use-hints", 't',
341      POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
342      N_("Use stored filename and DPI hints when exporting (only with export-id)"),
343      NULL},
345     {"export-background", 'b',
346      POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
347      N_("Background color of exported bitmap (any SVG-supported color string)"),
348      N_("COLOR")},
350     {"export-background-opacity", 'y',
351      POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
352      N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
353      N_("VALUE")},
355     {"export-plain-svg", 'l',
356      POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
357      N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
358      N_("FILENAME")},
360     {"export-ps", 'P',
361      POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
362      N_("Export document to a PS file"),
363      N_("FILENAME")},
365     {"export-eps", 'E',
366      POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
367      N_("Export document to an EPS file"),
368      N_("FILENAME")},
370     {"export-pdf", 'A',
371      POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
372      N_("Export document to a PDF file"),
373      N_("FILENAME")},
375 #ifdef WIN32
376     {"export-emf", 'M',
377      POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
378      N_("Export document to an Enhanced Metafile (EMF) File"),
379      N_("FILENAME")},
380 #endif //WIN32
382     {"export-text-to-path", 'T',
383      POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
384      N_("Convert text object to paths on export (PS, EPS, PDF)"),
385      NULL},
387     {"export-ignore-filters", 0,
388      POPT_ARG_NONE, &sp_export_ignore_filters, SP_ARG_EXPORT_IGNORE_FILTERS,
389      N_("Render filtered objects without filters, instead of rasterizing (PS, EPS, PDF)"),
390      NULL},
392     {"query-x", 'X',
393      POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
394      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
395      N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
396      NULL},
398     {"query-y", 'Y',
399      POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
400      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
401      N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
402      NULL},
404     {"query-width", 'W',
405      POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
406      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
407      N_("Query the width of the drawing or, if specified, of the object with --query-id"),
408      NULL},
410     {"query-height", 'H',
411      POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
412      // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
413      N_("Query the height of the drawing or, if specified, of the object with --query-id"),
414      NULL},
416     {"query-all", 'S',
417      POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
418      N_("List id,x,y,w,h for all objects"),
419      NULL},
421     {"query-id", 'I',
422      POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
423      N_("The ID of the object whose dimensions are queried"),
424      N_("ID")},
426     {"extension-directory", 'x',
427      POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
428      // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
429      N_("Print out the extension directory and exit"),
430      NULL},
432     {"vacuum-defs", 0,
433      POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
434      N_("Remove unused definitions from the defs section(s) of the document"),
435      NULL},
437     {"verb-list", 0,
438      POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
439      N_("List the IDs of all the verbs in Inkscape"),
440      NULL},
442     {"verb", 0,
443      POPT_ARG_STRING, NULL, SP_ARG_VERB,
444      N_("Verb to call when Inkscape opens."),
445      N_("VERB-ID")},
447     {"select", 0,
448      POPT_ARG_STRING, NULL, SP_ARG_SELECT,
449      N_("Object ID to select when Inkscape opens."),
450      N_("OBJECT-ID")},
452     {"shell", 0,
453      POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
454      N_("Start Inkscape in interactive shell mode."),
455      NULL},
457     POPT_AUTOHELP POPT_TABLEEND
458 };
460 static bool needToRecodeParams = true;
461 gchar * blankParam = g_strdup("");
465 #ifdef WIN32
467 /**
468  * Return the directory of the .exe that is currently running
469  */
470 static Glib::ustring _win32_getExePath()
472     char exeName[MAX_PATH+1];
473     // TODO these should use xxxW() calls explicitly and convert UTF-16 <--> UTF-8
474     GetModuleFileName(NULL, exeName, MAX_PATH);
475     char *slashPos = strrchr(exeName, '\\');
476     if (slashPos) {
477         *slashPos = '\0';
478     }
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)
489     // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
491     char *oldenv = getenv("PATH");
492     Glib::ustring tmp = "PATH=";
493     tmp += exePath;
494     tmp += ";";
495     tmp += exePath;
496     tmp += "\\python;";
497     tmp += exePath;
498     tmp += "\\python\\Scripts;";  // for uniconv.cmd
499     tmp += exePath;
500     tmp += "\\perl";
501     if(oldenv != NULL) {
502         tmp += ";";
503         tmp += oldenv;
504     }
505     _putenv(tmp.c_str());
507     oldenv = getenv("PYTHONPATH");
508     tmp = "PYTHONPATH=";
509     tmp += exePath;
510     tmp += "\\python;";
511     tmp += exePath;
512     tmp += "\\python\\Lib;";
513     tmp += exePath;
514     tmp += "\\python\\DLLs";
515     if(oldenv != NULL) {
516         tmp += ";";
517         tmp += oldenv;
518     }
519     _putenv(tmp.c_str());
521     return 0;
523 #endif
525 /**
526  * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
527  * can find inkex.py et al. (Bug #197475)
528  */
529 static int set_extensions_env()
531     char *oldenv = getenv("PYTHONPATH");
532     Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
533     if (oldenv != NULL) {
534         tmp += G_SEARCHPATH_SEPARATOR;
535         tmp += oldenv;
536     }
537     g_setenv("PYTHONPATH", tmp.c_str(), TRUE);
539     return 0;
543 /**
544  * This is the classic main() entry point of the program, though on some
545  * architectures it might be called by something else.
546  */
547 int
548 main(int argc, char **argv)
550 #ifdef HAVE_FPSETMASK
551     /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__.  It's probably
552        safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
553        the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
554     fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
555 #endif
557 #ifdef WIN32
558     /*
559       Set the current directory to the directory of the
560       executable.  This seems redundant, but is needed for
561       when inkscape.exe is executed from another directory.
562       We use relative paths on win32.
563       HKCR\svgfile\shell\open\command is a good example
564     */
565     Glib::ustring homedir = _win32_getExePath();
566     // TODO these should use xxxW() calls explicitly and convert UTF-16 <--> UTF-8
567     SetCurrentDirectory(homedir.c_str());
568     _win32_set_inkscape_env(homedir);
569     RegistryTool rt;
570     rt.setPathInfo();
571 #endif
573     // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
574     Gtk::Main::init_gtkmm_internals();
576     // Bug #197475
577     set_extensions_env();
579    /**
580     * Call bindtextdomain() for various machines's paths
581     */
582 #ifdef ENABLE_NLS
583 #ifdef WIN32
584     Glib::ustring localePath = homedir;
585     localePath += "\\";
586     localePath += PACKAGE_LOCALE_DIR;
587     bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
588 #else
589 #ifdef ENABLE_BINRELOC
590     bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
591 #else
592     bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
593 #endif
594 #endif
595     // Allow the user to override the locale directory by setting
596     // the environment variable INKSCAPE_LOCALEDIR.
597     char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
598     if (inkscape_localedir != NULL) {
599         bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
600     }
601 #endif
603     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
605 #ifdef ENABLE_NLS
606     textdomain(GETTEXT_PACKAGE);
607 #endif
609     LIBXML_TEST_VERSION
611     Inkscape::GC::init();
613     Inkscape::Debug::Logger::init();
615     gboolean use_gui;
617 #ifndef WIN32
618     // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
619     use_gui = (getenv("DISPLAY") != NULL);
620 #else
621     use_gui = TRUE;
622 #endif
623     /* Test whether with/without GUI is forced */
624     for (int i = 1; i < argc; i++) {
625         if (!strcmp(argv[i], "-z")
626             || !strcmp(argv[i], "--without-gui")
627             || !strcmp(argv[i], "-p")
628             || !strncmp(argv[i], "--print", 7)
629             || !strcmp(argv[i], "-e")
630             || !strncmp(argv[i], "--export-png", 12)
631             || !strcmp(argv[i], "-l")
632             || !strncmp(argv[i], "--export-plain-svg", 18)
633             || !strcmp(argv[i], "-i")
634             || !strncmp(argv[i], "--export-area-drawing", 21)
635             || !strcmp(argv[i], "-D")
636             || !strncmp(argv[i], "--export-area-page", 18)
637             || !strcmp(argv[i], "-C")
638             || !strncmp(argv[i], "--export-id", 11)
639             || !strcmp(argv[i], "-P")
640             || !strncmp(argv[i], "--export-ps", 11)
641             || !strcmp(argv[i], "-E")
642             || !strncmp(argv[i], "--export-eps", 12)
643             || !strcmp(argv[i], "-A")
644             || !strncmp(argv[i], "--export-pdf", 12)
645 #ifdef WIN32
646             || !strcmp(argv[i], "-M")
647             || !strncmp(argv[i], "--export-emf", 12)
648 #endif //WIN32
649             || !strcmp(argv[i], "-W")
650             || !strncmp(argv[i], "--query-width", 13)
651             || !strcmp(argv[i], "-H")
652             || !strncmp(argv[i], "--query-height", 14)
653             || !strcmp(argv[i], "-S")
654             || !strncmp(argv[i], "--query-all", 11)
655             || !strcmp(argv[i], "-X")
656             || !strncmp(argv[i], "--query-x", 9)
657             || !strcmp(argv[i], "-Y")
658             || !strncmp(argv[i], "--query-y", 9)
659             || !strcmp(argv[i], "--vacuum-defs")
660             || !strcmp(argv[i], "--shell")
661            )
662         {
663             /* main_console handles any exports -- not the gui */
664             use_gui = FALSE;
665             break;
666         } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
667             use_gui = TRUE;
668             break;
669         }
670     }
672 #ifdef WIN32
673 #ifndef REPLACEARGS_ANSI
674     if ( PrintWin32::is_os_wide() )
675 #endif // REPLACEARGS_ANSI
676     {
677         // If the call fails, we'll need to convert charsets
678         needToRecodeParams = !replaceArgs( argc, argv );
679     }
680 #endif // WIN32
682     /// \todo  Should this be a static object (see inkscape.cpp)?
683     Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
685     return app.run();
691 void fixupSingleFilename( gchar **orig, gchar **spare )
693     if ( orig && *orig && **orig ) {
694         GError *error = NULL;
695         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
696         if ( newFileName )
697         {
698             *orig = newFileName;
699             if ( spare ) {
700                 *spare = newFileName;
701             }
702 //             g_message("Set a replacement fixup");
703         }
704     }
709 GSList *fixupFilenameEncoding( GSList* fl )
711     GSList *newFl = NULL;
712     while ( fl ) {
713         gchar *fn = static_cast<gchar*>(fl->data);
714         fl = g_slist_remove( fl, fl->data );
715         gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
716         if ( newFileName ) {
718             if ( 0 )
719             {
720                 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
721                 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
722                 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
723                                                        "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
724                 gtk_dialog_run (GTK_DIALOG (w));
725                 gtk_widget_destroy (w);
726                 g_free(safeNewFn);
727                 g_free(safeFn);
728             }
730             g_free( fn );
731             fn = newFileName;
732             newFileName = 0;
733         }
734         else
735             if ( 0 )
736         {
737             gchar *safeFn = Inkscape::IO::sanitizeString(fn);
738             GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
739             gtk_dialog_run (GTK_DIALOG (w));
740             gtk_widget_destroy (w);
741             g_free(safeFn);
742         }
743         newFl = g_slist_append( newFl, fn );
744     }
745     return newFl;
748 int sp_common_main( int argc, char const **argv, GSList **flDest )
750     /// \todo fixme: Move these to some centralized location (Lauris)
751     sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
752     sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
755     // temporarily switch gettext encoding to locale, so that help messages can be output properly
756     gchar const *charset;
757     g_get_charset(&charset);
759     bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
761     poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
762     poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
763     g_return_val_if_fail(ctx != NULL, 1);
765     /* Collect own arguments */
766     GSList *fl = sp_process_args(ctx);
767     poptFreeContext(ctx);
769     // now switch gettext back to UTF-8 (for GUI)
770     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
772     // Now let's see if the file list still holds up
773     if ( needToRecodeParams )
774     {
775         fl = fixupFilenameEncoding( fl );
776     }
778     // Check the globals for filename-fixup
779     if ( needToRecodeParams )
780     {
781         fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
782         fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
783         fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
784     }
785     else
786     {
787         if ( sp_export_png )
788             sp_export_png_utf8 = g_strdup( sp_export_png );
789         if ( sp_export_svg )
790             sp_export_svg_utf8 = g_strdup( sp_export_svg );
791         if ( sp_global_printer )
792             sp_global_printer_utf8 = g_strdup( sp_global_printer );
793     }
795     // Return the list if wanted, else free it up.
796     if ( flDest ) {
797         *flDest = fl;
798         fl = 0;
799     } else {
800         while ( fl ) {
801             g_free( fl->data );
802             fl = g_slist_remove( fl, fl->data );
803         }
804     }
805     return 0;
808 static void
809 snooper(GdkEvent *event, gpointer /*data*/) {
810     if (inkscape_mapalt())  /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
811     {
812         GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
813         switch (event->type) {
814             case GDK_MOTION_NOTIFY:
815                 if(event->motion.state & mapping) {
816                     event->motion.state|=GDK_MOD1_MASK;
817                 }
818                 break;
819             case GDK_BUTTON_PRESS:
820                 if(event->button.state & mapping) {
821                     event->button.state|=GDK_MOD1_MASK;
822                 }
823                 break;
824              case GDK_KEY_PRESS:
825                  if(event->key.state & mapping) {
826                      event->key.state|=GDK_MOD1_MASK;
827                  }
828                  break;
829         default:
830             break;
831         }
832     }
834     if (inkscape_trackalt()) {
835         // MacOS X with X11 has some problem with the default
836         // xmodmapping.  A ~/.xmodmap solution does not work reliably due
837         // to the way we package our executable in a .app that can launch
838         // X11 or use an already-running X11.  The same problem has been
839         // reported on Linux but there is no .app/X11 to get in the way
840         // of ~/.xmodmap fixes.  So we make this a preference.
841         //
842         // For some reason, Gdk senses changes in Alt (Mod1) state for
843         // many message types, but not for keystrokes!  So this ugly hack
844         // tracks what the state of Alt-pressing is, and ensures
845         // GDK_MOD1_MASK is in the event->key.state as appropriate.
846         //
847         static gboolean altL_pressed = FALSE;
848         static gboolean altR_pressed = FALSE;
849         static gboolean alt_pressed = FALSE;
850         guint get_group0_keyval(GdkEventKey* event);
851         guint keyval = 0;
852         switch (event->type) {
853         case GDK_MOTION_NOTIFY:
854             alt_pressed = TRUE && (event->motion.state & GDK_MOD1_MASK);
855             break;
856         case GDK_BUTTON_PRESS:
857             alt_pressed = TRUE && (event->button.state & GDK_MOD1_MASK);
858             break;
859         case GDK_KEY_PRESS:
860             keyval = get_group0_keyval(&event->key);
861             if (keyval == GDK_Alt_L) altL_pressed = TRUE;
862             if (keyval == GDK_Alt_R) altR_pressed = TRUE;
863             alt_pressed = alt_pressed || altL_pressed || altR_pressed;
864             alt_pressed = alt_pressed || (event->button.state & GDK_MOD1_MASK);
865             if (alt_pressed)
866                 event->key.state |= GDK_MOD1_MASK;
867             else
868                 event->key.state &= ~GDK_MOD1_MASK;
869             break;
870         case GDK_KEY_RELEASE:
871             keyval = get_group0_keyval(&event->key);
872             if (keyval == GDK_Alt_L) altL_pressed = FALSE;
873             if (keyval == GDK_Alt_R) altR_pressed = FALSE;
874             if (!altL_pressed && !altR_pressed)
875                 alt_pressed = FALSE;
876             break;
877         default:
878             break;
879         }
880         //printf("alt_pressed: %s\n", alt_pressed? "+" : "-");
881     }
883     gtk_main_do_event (event);
886 static std::vector<Glib::ustring> getDirectorySet(const gchar* userDir, const gchar* const * systemDirs) {
887     std::vector<Glib::ustring> listing;
888     listing.push_back(userDir);
889     for ( const char* const* cur = systemDirs; *cur; cur++ )
890     {
891         listing.push_back(*cur);
892     }
893     return listing;
896 int
897 sp_main_gui(int argc, char const **argv)
899     Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
901     GSList *fl = NULL;
902     int retVal = sp_common_main( argc, argv, &fl );
903     g_return_val_if_fail(retVal == 0, 1);
905     // Add possible icon entry directories
906     std::vector<Glib::ustring> dataDirs = getDirectorySet( g_get_user_data_dir(),
907                                                            g_get_system_data_dirs() );
908     for (std::vector<Glib::ustring>::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
909     {
910         std::vector<Glib::ustring> listing;
911         listing.push_back(*it);
912         listing.push_back("inkscape");
913         listing.push_back("icons");
914         Glib::ustring dir = Glib::build_filename(listing);
915         gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir.c_str());
916     }
918     // Add our icon directory to the search path for icon theme lookups.
919     gchar *usericondir = profile_path("icons");
920     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), usericondir);
921     gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
922     g_free(usericondir);
924     gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
925     Inkscape::Debug::log_display_config();
927     // Set default window icon. Obeys the theme.
928     gtk_window_set_default_icon_name("inkscape");
929     // Do things that were previously in inkscape_gtk_stock_init().
930     sp_icon_get_phys_size(GTK_ICON_SIZE_MENU);
931     Inkscape::UI::Widget::Panel::prep();
933     gboolean create_new = TRUE;
935     /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
936     inkscape_application_init(argv[0], true);
938     while (fl) {
939         if (sp_file_open((gchar *)fl->data,NULL)) {
940             create_new=FALSE;
941         }
942         fl = g_slist_remove(fl, fl->data);
943     }
944     if (create_new) {
945         sp_file_new_default();
946     }
948     Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
949     main_instance.run();
951 #ifdef WIN32
952     //We might not need anything here
953     //sp_win32_finish(); <-- this is a NOP func
954 #endif
956     return 0;
959 /**
960  * Process file list
961  */
962 void sp_process_file_list(GSList *fl)
964     while (fl) {
965         const gchar *filename = (gchar *)fl->data;
967         SPDocument *doc = NULL;
968         try {
969             doc = Inkscape::Extension::open(NULL, filename);
970         } catch (Inkscape::Extension::Input::no_extension_found &e) {
971             doc = NULL;
972         } catch (Inkscape::Extension::Input::open_failed &e) {
973             doc = NULL;
974         }
976         if (doc == NULL) {
977             try {
978                 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
979             } catch (Inkscape::Extension::Input::no_extension_found &e) {
980                 doc = NULL;
981             } catch (Inkscape::Extension::Input::open_failed &e) {
982                 doc = NULL;
983             }
984         }
985         if (doc == NULL) {
986             g_warning("Specified document %s cannot be opened (does not exist or not a valid SVG file)", filename);
987         } else {
988             if (sp_vacuum_defs) {
989                 vacuum_document(doc);
990             }
991             if (sp_vacuum_defs && !sp_export_svg) {
992                 // save under the name given in the command line
993                 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
994             }
995             if (sp_global_printer) {
996                 sp_print_document_to_file(doc, sp_global_printer);
997             }
998             if (sp_export_png || (sp_export_id && sp_export_use_hints)) {
999                 sp_do_export_png(doc);
1000             }
1001             if (sp_export_svg) {
1002                 Inkscape::XML::Document *rdoc;
1003                 Inkscape::XML::Node *repr;
1004                 rdoc = sp_repr_document_new("svg:svg");
1005                 repr = rdoc->root();
1006                 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
1007                 sp_repr_save_rebased_file(repr->document(), sp_export_svg, SP_SVG_NS_URI,
1008                                           doc->base, sp_export_svg);
1009             }
1010             if (sp_export_ps) {
1011                 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
1012             }
1013             if (sp_export_eps) {
1014                 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
1015             }
1016             if (sp_export_pdf) {
1017                 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
1018             }
1019 #ifdef WIN32
1020             if (sp_export_emf) {
1021                 do_export_emf(doc, sp_export_emf, "image/x-emf");
1022             }
1023 #endif //WIN32
1024             if (sp_query_all) {
1025                 do_query_all (doc);
1026             } else if (sp_query_width || sp_query_height) {
1027                 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
1028             } else if (sp_query_x || sp_query_y) {
1029                 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
1030             }
1032             delete doc;
1033         }
1034         fl = g_slist_remove(fl, fl->data);
1035     }
1038 /**
1039  * Run the application as an interactive shell, parsing command lines from stdin
1040  * Returns -1 on error.
1041  */
1042 int sp_main_shell(char const* command_name)
1044     int retval = 0;
1046     const unsigned int buffer_size = 4096;
1047     gchar *command_line = g_strnfill(buffer_size, 0);
1048     g_strlcpy(command_line, command_name, buffer_size);
1049     gsize offset = g_strlcat(command_line, " ", buffer_size);
1050     gsize sizeLeft = buffer_size - offset;
1051     gchar *useme = command_line + offset;
1053     fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
1054     fflush(stdout);
1055     char* linedata = 0;
1056     do {
1057         fprintf(stdout, ">");
1058         fflush(stdout);
1059         if ((linedata = fgets(useme, sizeLeft, stdin))) {
1060             size_t len = strlen(useme);
1061             if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
1062                 fprintf(stdout, "ERROR: Command line too long\n");
1063                 // Consume rest of line
1064                 retval = -1; // If the while loop completes, this remains -1
1065                 while (fgets(useme, sizeLeft, stdin) && retval) {
1066                     len = strlen(command_line);
1067                     if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
1068                         retval = 0;
1069                     }
1070                 }
1071             } else {
1072                 useme[--len] = '\0';  // Strip newline
1073                 if (useme[len - 1] == '\r') {
1074                     useme[--len] = '\0';
1075                 }
1076                 if ( strcmp(useme, "quit") == 0 ) {
1077                     // Time to quit
1078                     fflush(stdout);
1079                     linedata = 0; // mark for exit
1080                 } else if ( len < 1 ) {
1081                     // blank string. Do nothing.
1082                 } else {
1083                     GError* parseError = 0;
1084                     gchar** argv = 0;
1085                     gint argc = 0;
1086                     if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
1087                         poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
1088                         poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
1089                         if ( ctx ) {
1090                             GSList *fl = sp_process_args(ctx);
1091                             sp_process_file_list(fl);
1092                             poptFreeContext(ctx);
1093                         } else {
1094                             retval = 1; // not sure why. But this was the previous return value
1095                         }
1096                         resetCommandlineGlobals();
1097                         g_strfreev(argv);
1098                     } else {
1099                         g_warning("Cannot parse commandline: %s", useme);
1100                     }
1101                 }
1102             }
1103         } // if (linedata...
1104     } while (linedata && (retval == 0));
1106     g_free(command_line);
1107     return retval;
1110 int sp_main_console(int argc, char const **argv)
1112     /* We are started in text mode */
1114     /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1115      * in a non-Gtk environment.  Used in libnrtype's
1116      * FontInstance.cpp and FontFactory.cpp.
1117      * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1118      */
1119     g_type_init();
1120     char **argv2 = const_cast<char **>(argv);
1121     gtk_init_check( &argc, &argv2 );
1122     //setlocale(LC_ALL, "");
1124     GSList *fl = NULL;
1125     int retVal = sp_common_main( argc, argv, &fl );
1126     g_return_val_if_fail(retVal == 0, 1);
1128     if (fl == NULL && !sp_shell) {
1129         g_print("Nothing to do!\n");
1130         exit(0);
1131     }
1133     inkscape_application_init(argv[0], false);
1135     if (sp_shell) {
1136         sp_main_shell(argv[0]); // Run as interactive shell
1137         exit(0);
1138     } else {
1139         sp_process_file_list(fl); // Normal command line invokation
1140     }
1142     return 0;
1145 static void
1146 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1148     SPObject *o = NULL;
1150     if (id) {
1151         o = doc->getObjectById(id);
1152         if (o) {
1153             if (!SP_IS_ITEM (o)) {
1154                 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1155                 return;
1156             }
1157         } else {
1158             g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1159             return;
1160         }
1161     } else {
1162         o = SP_DOCUMENT_ROOT(doc);
1163     }
1165     if (o) {
1166         sp_document_ensure_up_to_date (doc);
1167         SPItem *item = ((SPItem *) o);
1169         // "true" SVG bbox for scripting
1170         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1171         if (area) {
1172             Inkscape::SVGOStringStream os;
1173             if (extent) {
1174                 os << area->dimensions()[axis];
1175             } else {
1176                 os << area->min()[axis];
1177             }
1178             g_print ("%s", os.str().c_str());
1179         } else {
1180             g_print("0");
1181         }
1182     }
1185 static void
1186 do_query_all (SPDocument *doc)
1188     SPObject *o = NULL;
1190     o = SP_DOCUMENT_ROOT(doc);
1192     if (o) {
1193         sp_document_ensure_up_to_date (doc);
1194         do_query_all_recurse(o);
1195     }
1198 static void
1199 do_query_all_recurse (SPObject *o)
1201     SPItem *item = ((SPItem *) o);
1202     if (o->id && SP_IS_ITEM(item)) {
1203         Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1204         if (area) {
1205             Inkscape::SVGOStringStream os;
1206             os << o->id;
1207             os << "," << area->min()[Geom::X];
1208             os << "," << area->min()[Geom::Y];
1209             os << "," << area->dimensions()[Geom::X];
1210             os << "," << area->dimensions()[Geom::Y];
1211             g_print ("%s\n", os.str().c_str());
1212         }
1213     }
1215     SPObject *child = o->children;
1216     while (child) {
1217         do_query_all_recurse (child);
1218         child = child->next;
1219     }
1223 static void
1224 sp_do_export_png(SPDocument *doc)
1226     const gchar *filename = NULL;
1227     bool filename_from_hint = false;
1228     gdouble dpi = 0.0;
1230     if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1231         g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1232     }
1234     GSList *items = NULL;
1236     Geom::Rect area;
1237     if (sp_export_id || sp_export_area_drawing) {
1239         SPObject *o = NULL;
1240         SPObject *o_area = NULL;
1241         if (sp_export_id && sp_export_area_drawing) {
1242             o = doc->getObjectById(sp_export_id);
1243             o_area = SP_DOCUMENT_ROOT (doc);
1244         } else if (sp_export_id) {
1245             o = doc->getObjectById(sp_export_id);
1246             o_area = o;
1247         } else if (sp_export_area_drawing) {
1248             o = SP_DOCUMENT_ROOT (doc);
1249             o_area = o;
1250         }
1252         if (o) {
1253             if (!SP_IS_ITEM (o)) {
1254                 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1255                 return;
1256             }
1258             items = g_slist_prepend (items, SP_ITEM(o));
1260             if (sp_export_id_only) {
1261                 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1262             }
1264             if (sp_export_use_hints) {
1266                 // retrieve export filename hint
1267                 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1268                 if (fn_hint) {
1269                     if (sp_export_png) {
1270                         g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1271                         filename = sp_export_png;
1272                     } else {
1273                         filename = fn_hint;
1274                         filename_from_hint = true;
1275                     }
1276                 } else {
1277                     g_warning ("Export filename hint not found for the object.");
1278                     filename = sp_export_png;
1279                 }
1281                 // retrieve export dpi hints
1282                 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1283                 if (dpi_hint) {
1284                     if (sp_export_dpi || sp_export_width || sp_export_height) {
1285                         g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1286                     } else {
1287                         dpi = atof(dpi_hint);
1288                     }
1289                 } else {
1290                     g_warning ("Export DPI hint not found for the object.");
1291                 }
1293             }
1295             // write object bbox to area
1296             sp_document_ensure_up_to_date (doc);
1297             Geom::OptRect areaMaybe;
1298             sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1299             if (areaMaybe) {
1300                 area = *areaMaybe;
1301             } else {
1302                 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1303                 return;
1304             }
1305         } else {
1306             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1307             return;
1308         }
1309     }
1311     if (sp_export_area) {
1312         /* Try to parse area (given in SVG pixels) */
1313         gdouble x0,y0,x1,y1;
1314         if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1315             g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1316             return;
1317         }
1318         area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1319     } else if (sp_export_area_page || !(sp_export_id || sp_export_area_drawing)) {
1320         /* Export the whole page: note: Inkscape uses 'page' in all menus and dialogs, not 'canvas' */
1321         sp_document_ensure_up_to_date (doc);
1322         Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1323         area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1324     }
1326     // set filename and dpi from options, if not yet set from the hints
1327     if (!filename) {
1328         if (!sp_export_png) {
1329             g_warning ("No export filename given and no filename hint. Nothing exported.");
1330             return;
1331         }
1332         filename = sp_export_png;
1333     }
1335     if (sp_export_dpi && dpi == 0.0) {
1336         dpi = atof(sp_export_dpi);
1337         if ((dpi < 0.1) || (dpi > 10000.0)) {
1338             g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1339             return;
1340         }
1341         g_print("DPI: %g\n", dpi);
1342     }
1344     if (sp_export_area_snap) {
1345         round_rectangle_outwards(area);
1346     }
1348     // default dpi
1349     if (dpi == 0.0) {
1350         dpi = PX_PER_IN;
1351     }
1353     unsigned long int width = 0;
1354     unsigned long int height = 0;
1356     if (sp_export_width) {
1357         errno=0;
1358         width = strtoul(sp_export_width, NULL, 0);
1359         if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1360             g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1361             return;
1362         }
1363         dpi = (gdouble) width * PX_PER_IN / area.width();
1364     }
1366     if (sp_export_height) {
1367         errno=0;
1368         height = strtoul(sp_export_height, NULL, 0);
1369         if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1370             g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1371             return;
1372         }
1373         dpi = (gdouble) height * PX_PER_IN / area.height();
1374     }
1376     if (!sp_export_width) {
1377         width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1378     }
1380     if (!sp_export_height) {
1381         height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1382     }
1384     guint32 bgcolor = 0x00000000;
1385     if (sp_export_background) {
1386         // override the page color
1387         bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1388         bgcolor |= 0xff; // default is no opacity
1389     } else {
1390         // read from namedview
1391         Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1392         if (nv && nv->attribute("pagecolor"))
1393             bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1394         if (nv && nv->attribute("inkscape:pageopacity"))
1395             bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1396     }
1398     if (sp_export_background_opacity) {
1399         // override opacity
1400         gfloat value;
1401         if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1402             if (value > 1.0) {
1403                 value = CLAMP (value, 1.0f, 255.0f);
1404                 bgcolor &= (guint32) 0xffffff00;
1405                 bgcolor |= (guint32) floor(value);
1406             } else {
1407                 value = CLAMP (value, 0.0f, 1.0f);
1408                 bgcolor &= (guint32) 0xffffff00;
1409                 bgcolor |= SP_COLOR_F_TO_U(value);
1410             }
1411         }
1412     }
1414     gchar *path = 0;
1415     if (filename_from_hint) {
1416         //Make relative paths go from the document location, if possible:
1417         if (!g_path_is_absolute(filename) && doc->uri) {
1418             gchar *dirname = g_path_get_dirname(doc->uri);
1419             if (dirname) {
1420                 path = g_build_filename(dirname, filename, NULL);
1421                 g_free(dirname);
1422             }
1423         }
1424         if (!path) {
1425             path = g_strdup(filename);
1426         }
1427     } else {
1428         path = g_strdup(filename);
1429     }
1431     g_print("Background RRGGBBAA: %08x\n", bgcolor);
1433     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);
1435     g_print("Bitmap saved as: %s\n", filename);
1437     if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1438         sp_export_png_file(doc, path, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1439     } else {
1440         g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1441     }
1443     g_free (path);
1444     g_slist_free (items);
1448 /**
1449  *  Perform a PDF/PS/EPS export
1450  *
1451  *  \param doc Document to export.
1452  *  \param uri URI to export to.
1453  *  \param mime MIME type to export as.
1454  */
1456 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1458     Inkscape::Extension::DB::OutputList o;
1459     Inkscape::Extension::db.get_output_list(o);
1460     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1461     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1462         i++;
1463     }
1465     if (i == o.end())
1466     {
1467         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1468         return;
1469     }
1471     if (sp_export_id) {
1472         SPObject *o = doc->getObjectById(sp_export_id);
1473         if (o == NULL) {
1474             g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1475             return;
1476         }
1477         (*i)->set_param_string ("exportId", sp_export_id);
1478     } else {
1479         (*i)->set_param_string ("exportId", "");
1480     }
1482     if (sp_export_area_page && sp_export_area_drawing) {
1483         g_warning ("You cannot use --export-area-page and --export-area-drawing at the same time; only the former will take effect.");
1484         sp_export_area_drawing = false;
1485     }
1487     if (sp_export_area_drawing) {
1488         (*i)->set_param_bool ("areaDrawing", TRUE);
1489     } else {
1490         (*i)->set_param_bool ("areaDrawing", FALSE);
1491     }
1493     if (sp_export_area_page) {
1494         if (sp_export_eps) {
1495             g_warning ("EPS cannot have its bounding box extend beyond its content, so if your drawing is smaller than the page, --export-area-page will clip it to drawing.");
1496         }
1497         (*i)->set_param_bool ("areaPage", TRUE);
1498     } else {
1499         (*i)->set_param_bool ("areaPage", FALSE);
1500     }
1502     if (!sp_export_area_drawing && !sp_export_area_page && !sp_export_id) {
1503         // neither is set, set page as default for ps/pdf and drawing for eps
1504         if (sp_export_eps) {
1505             try {
1506                (*i)->set_param_bool("areaDrawing", TRUE);
1507             } catch (...) {}
1508         }
1509     }
1511     if (sp_export_text_to_path) {
1512         (*i)->set_param_bool("textToPath", TRUE);
1513     } else {
1514         (*i)->set_param_bool("textToPath", FALSE);
1515     }
1517     if (sp_export_ignore_filters) {
1518         (*i)->set_param_bool("blurToBitmap", FALSE);
1519     } else {
1520         (*i)->set_param_bool("blurToBitmap", TRUE);
1522         gdouble dpi = 90.0;
1523         if (sp_export_dpi) {
1524             dpi = atof(sp_export_dpi);
1525             if ((dpi < 1) || (dpi > 10000.0)) {
1526                 g_warning("DPI value %s out of range [1 - 10000]. Using 90 dpi instead.", sp_export_dpi);
1527                 dpi = 90;
1528             }
1529         }
1531         (*i)->set_param_int("resolution", (int) dpi);
1532     }
1534     (*i)->save(doc, uri);
1537 #ifdef WIN32
1538 /**
1539  *  Export a document to EMF
1540  *
1541  *  \param doc Document to export.
1542  *  \param uri URI to export to.
1543  *  \param mime MIME type to export as (should be "image/x-emf")
1544  */
1546 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1548     Inkscape::Extension::DB::OutputList o;
1549     Inkscape::Extension::db.get_output_list(o);
1550     Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1551     while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1552         i++;
1553     }
1555     if (i == o.end())
1556     {
1557         g_warning ("Could not find an extension to export to MIME type %s.", mime);
1558         return;
1559     }
1561     (*i)->save(doc, uri);
1563 #endif //WIN32
1565 #ifdef WIN32
1566 bool replaceArgs( int& argc, char**& argv )
1568     bool worked = false;
1570 #ifdef REPLACEARGS_DEBUG
1571     MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1572 #endif // REPLACEARGS_DEBUG
1574     wchar_t* line = GetCommandLineW();
1575     if ( line )
1576     {
1577 #ifdef REPLACEARGS_DEBUG
1578         {
1579             gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1580             if ( utf8Line )
1581             {
1582                 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1583                 {
1584                     char tmp[strlen(safe) + 32];
1585                     snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1586                     MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1587                 }
1588             }
1589         }
1590 #endif // REPLACEARGS_DEBUG
1592         int numArgs = 0;
1593         wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1595 #ifdef REPLACEARGS_ANSI
1596 // test code for trying things on Win95/98/ME
1597         if ( !parsed )
1598         {
1599 #ifdef REPLACEARGS_DEBUG
1600             MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1601 #endif // REPLACEARGS_DEBUG
1602             int lineLen = wcslen(line) + 1;
1603             wchar_t* lineDup = new wchar_t[lineLen];
1604             wcsncpy( lineDup, line, lineLen );
1606             int pos = 0;
1607             bool inQuotes = false;
1608             bool inWhitespace = true;
1609             std::vector<int> places;
1610             while ( lineDup[pos] )
1611             {
1612                 if ( inQuotes )
1613                 {
1614                     if ( lineDup[pos] == L'"' )
1615                     {
1616                         inQuotes = false;
1617                     }
1618                 }
1619                 else if ( lineDup[pos] == L'"' )
1620                 {
1621                     inQuotes = true;
1622                     inWhitespace = false;
1623                     places.push_back(pos);
1624                 }
1625                 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1626                 {
1627                     if ( !inWhitespace )
1628                     {
1629                         inWhitespace = true;
1630                         lineDup[pos] = 0;
1631                     }
1632                 }
1633                 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1634                 {
1635                     inWhitespace = false;
1636                     places.push_back(pos);
1637                 }
1638                 else
1639                 {
1640                     // consume
1641                 }
1642                 pos++;
1643             }
1644 #ifdef REPLACEARGS_DEBUG
1645             {
1646                 char tmp[256];
1647                 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1648                 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1649             }
1650 #endif // REPLACEARGS_DEBUG
1652             wchar_t** block = new wchar_t*[places.size()];
1653             int i = 0;
1654             for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1655             {
1656                 block[i++] = &lineDup[*it];
1657             }
1658             parsed = block;
1659             numArgs = places.size();
1660         }
1661 #endif // REPLACEARGS_ANSI
1663         if ( parsed )
1664         {
1665             std::vector<wchar_t*>expandedArgs;
1666             if ( numArgs > 0 )
1667             {
1668                 expandedArgs.push_back( parsed[0] );
1669             }
1671             for ( int i1 = 1; i1 < numArgs; i1++ )
1672             {
1673                 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1674                 wildcarded &= parsed[i1][0] != L'"';
1675                 wildcarded &= parsed[i1][0] != L'-';
1676                 if ( wildcarded )
1677                 {
1678 #ifdef REPLACEARGS_ANSI
1679                     WIN32_FIND_DATAA data;
1680 #else
1681                     WIN32_FIND_DATAW data;
1682 #endif // REPLACEARGS_ANSI
1684                     memset((void *)&data, 0, sizeof(data));
1686                     int baseLen = wcslen(parsed[i1]) + 2;
1687                     wchar_t* base = new wchar_t[baseLen];
1688                     wcsncpy( base, parsed[i1], baseLen );
1689                     wchar_t* last = wcsrchr( base, L'\\' );
1690                     if ( last )
1691                     {
1692                         last[1] = 0;
1693                     }
1694                     else
1695                     {
1696                         base[0] = 0;
1697                     }
1698                     baseLen = wcslen( base );
1700 #ifdef REPLACEARGS_ANSI
1701                     char target[MAX_PATH];
1702                     if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1703                     {
1704                         HANDLE hf = FindFirstFileA( target, &data );
1705 #else
1706                         HANDLE hf = FindFirstFileW( parsed[i1], &data );
1707 #endif // REPLACEARGS_ANSI
1708                         if ( hf != INVALID_HANDLE_VALUE )
1709                         {
1710                             BOOL found = TRUE;
1711                             do
1712                             {
1713 #ifdef REPLACEARGS_ANSI
1714                                 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1715                                 if ( howMany > 0 )
1716                                 {
1717                                     howMany += baseLen;
1718                                     wchar_t* tmp = new wchar_t[howMany + 1];
1719                                     wcsncpy( tmp, base, howMany + 1 );
1720                                     MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1721                                     expandedArgs.push_back( tmp );
1722                                     found = FindNextFileA( hf, &data );
1723                                 }
1724 #else
1725                                 int howMany = wcslen(data.cFileName) + baseLen;
1726                                 wchar_t* tmp = new wchar_t[howMany + 1];
1727                                 wcsncpy( tmp, base, howMany + 1 );
1728                                 wcsncat( tmp, data.cFileName, howMany + 1 );
1729                                 expandedArgs.push_back( tmp );
1730                                 found = FindNextFileW( hf, &data );
1731 #endif // REPLACEARGS_ANSI
1732                             } while ( found );
1734                             FindClose( hf );
1735                         }
1736                         else
1737                         {
1738                             expandedArgs.push_back( parsed[i1] );
1739                         }
1740 #ifdef REPLACEARGS_ANSI
1741                     }
1742 #endif // REPLACEARGS_ANSI
1744                     delete[] base;
1745                 }
1746                 else
1747                 {
1748                     expandedArgs.push_back( parsed[i1] );
1749                 }
1750             }
1752             {
1753                 wchar_t** block = new wchar_t*[expandedArgs.size()];
1754                 int iz = 0;
1755                 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1756                 {
1757                     block[iz++] = *it;
1758                 }
1759                 parsed = block;
1760                 numArgs = expandedArgs.size();
1761             }
1763             std::vector<gchar*> newArgs;
1764             for ( int i = 0; i < numArgs; i++ )
1765             {
1766                 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1767                 if ( replacement )
1768                 {
1769 #ifdef REPLACEARGS_DEBUG
1770                     gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1772                     if ( safe2 )
1773                     {
1774                         {
1775                             char tmp[1024];
1776                             snprintf( tmp, sizeof(tmp), "    [%2d] = '%s'", i, safe2 );
1777                             MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1778                         }
1779                         g_free( safe2 );
1780                     }
1781 #endif // REPLACEARGS_DEBUG
1783                     newArgs.push_back( replacement );
1784                 }
1785                 else
1786                 {
1787                     newArgs.push_back( blankParam );
1788                 }
1789             }
1791             // Now push our munged params to be the new argv and argc
1792             {
1793                 char** block = new char*[newArgs.size()];
1794                 int iz = 0;
1795                 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1796                 {
1797                     block[iz++] = *it;
1798                 }
1799                 argv = block;
1800                 argc = newArgs.size();
1801                 worked = true;
1802             }
1803         }
1804 #ifdef REPLACEARGS_DEBUG
1805         else
1806         {
1807             MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1808         }
1809 #endif // REPLACEARGS_DEBUG
1810     }
1811 #ifdef REPLACEARGS_DEBUG
1812     else
1813     {
1814         {
1815             MessageBoxA( NULL,  "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1816         }
1818         char* line2 = GetCommandLineA();
1819         if ( line2 )
1820         {
1821             gchar *safe = Inkscape::IO::sanitizeString(line2);
1822             {
1823                 {
1824                     char tmp[strlen(safe) + 32];
1825                     snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1826                     MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1827                 }
1828             }
1829         }
1830         else
1831         {
1832             MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1833         }
1834     }
1835 #endif // REPLACEARGS_DEBUG
1837     return worked;
1839 #endif // WIN32
1841 static GSList *
1842 sp_process_args(poptContext ctx)
1844     GSList *fl = NULL;
1846     gint a;
1847     while ((a = poptGetNextOpt(ctx)) != -1) {
1848         switch (a) {
1849             case SP_ARG_FILE: {
1850                 gchar const *fn = poptGetOptArg(ctx);
1851                 if (fn != NULL) {
1852                     fl = g_slist_append(fl, g_strdup(fn));
1853                 }
1854                 break;
1855             }
1856             case SP_ARG_VERSION: {
1857                 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1858                 exit(0);
1859                 break;
1860             }
1861             case SP_ARG_EXTENSIONDIR: {
1862                 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1863                 exit(0);
1864                 break;
1865             }
1866             case SP_ARG_VERB_LIST: {
1867                 // This really shouldn't go here, we should init the app.
1868                 // But, since we're just exiting in this path, there is
1869                 // no harm, and this is really a better place to put
1870                 // everything else.
1871                 Inkscape::Extension::init();
1872                 Inkscape::Verb::list();
1873                 exit(0);
1874                 break;
1875             }
1876             case SP_ARG_VERB:
1877             case SP_ARG_SELECT: {
1878                 gchar const *arg = poptGetOptArg(ctx);
1879                 if (arg != NULL) {
1880                     // printf("Adding in: %s\n", arg);
1881                     new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1882                 }
1883                 break;
1884             }
1885             case POPT_ERROR_BADOPT: {
1886                 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1887                 exit(1);
1888                 break;
1889             }
1890             default: {
1891                 break;
1892             }
1893         }
1894     }
1896     gchar const ** const args = poptGetArgs(ctx);
1897     if (args != NULL) {
1898         for (unsigned i = 0; args[i] != NULL; i++) {
1899             fl = g_slist_append(fl, g_strdup(args[i]));
1900         }
1901     }
1903     return fl;
1907 /*
1908   Local Variables:
1909   mode:c++
1910   c-file-style:"stroustrup"
1911   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1912   indent-tabs-mode:nil
1913   fill-column:99
1914   End:
1915 */
1916 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :