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;
255 }
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 interactive 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()
470 {
471 char exeName[MAX_PATH+1];
472 // TODO these should use xxxW() calls explicitly and convert UTF-16 <--> UTF-8
473 GetModuleFileName(NULL, exeName, MAX_PATH);
474 char *slashPos = strrchr(exeName, '\\');
475 if (slashPos) {
476 *slashPos = '\0';
477 }
478 Glib::ustring s = exeName;
479 return s;
480 }
482 /**
483 * Set up the PATH and PYTHONPATH environment variables on
484 * win32
485 */
486 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
487 {
488 // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
490 char *oldenv = getenv("PATH");
491 Glib::ustring tmp = "PATH=";
492 tmp += exePath;
493 tmp += ";";
494 tmp += exePath;
495 tmp += "\\python;";
496 tmp += exePath;
497 tmp += "\\python\\Scripts;"; // for uniconv.cmd
498 tmp += exePath;
499 tmp += "\\perl";
500 if(oldenv != NULL) {
501 tmp += ";";
502 tmp += oldenv;
503 }
504 _putenv(tmp.c_str());
506 oldenv = getenv("PYTHONPATH");
507 tmp = "PYTHONPATH=";
508 tmp += exePath;
509 tmp += "\\python;";
510 tmp += exePath;
511 tmp += "\\python\\Lib;";
512 tmp += exePath;
513 tmp += "\\python\\DLLs";
514 if(oldenv != NULL) {
515 tmp += ";";
516 tmp += oldenv;
517 }
518 _putenv(tmp.c_str());
520 return 0;
521 }
522 #endif
524 /**
525 * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
526 * can find inkex.py et al. (Bug #197475)
527 */
528 static int set_extensions_env()
529 {
530 char *oldenv = getenv("PYTHONPATH");
531 Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
532 if (oldenv != NULL) {
533 tmp += G_SEARCHPATH_SEPARATOR;
534 tmp += oldenv;
535 }
536 g_setenv("PYTHONPATH", tmp.c_str(), TRUE);
538 return 0;
539 }
542 /**
543 * This is the classic main() entry point of the program, though on some
544 * architectures it might be called by something else.
545 */
546 int
547 main(int argc, char **argv)
548 {
549 #ifdef HAVE_FPSETMASK
550 /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__. It's probably
551 safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
552 the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
553 fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
554 #endif
556 #ifdef WIN32
557 /*
558 Set the current directory to the directory of the
559 executable. This seems redundant, but is needed for
560 when inkscape.exe is executed from another directory.
561 We use relative paths on win32.
562 HKCR\svgfile\shell\open\command is a good example
563 */
564 Glib::ustring homedir = _win32_getExePath();
565 // TODO these should use xxxW() calls explicitly and convert UTF-16 <--> UTF-8
566 SetCurrentDirectory(homedir.c_str());
567 _win32_set_inkscape_env(homedir);
568 RegistryTool rt;
569 rt.setPathInfo();
570 #endif
572 // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
573 Gtk::Main::init_gtkmm_internals();
575 // Bug #197475
576 set_extensions_env();
578 /**
579 * Call bindtextdomain() for various machines's paths
580 */
581 #ifdef ENABLE_NLS
582 #ifdef WIN32
583 Glib::ustring localePath = homedir;
584 localePath += "\\";
585 localePath += PACKAGE_LOCALE_DIR;
586 bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
587 #else
588 #ifdef ENABLE_BINRELOC
589 bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
590 #else
591 bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
592 #endif
593 #endif
594 // Allow the user to override the locale directory by setting
595 // the environment variable INKSCAPE_LOCALEDIR.
596 char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
597 if (inkscape_localedir != NULL) {
598 bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
599 }
600 #endif
602 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
604 #ifdef ENABLE_NLS
605 textdomain(GETTEXT_PACKAGE);
606 #endif
608 LIBXML_TEST_VERSION
610 Inkscape::GC::init();
612 Inkscape::Debug::Logger::init();
614 gboolean use_gui;
616 #ifndef WIN32
617 // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
618 use_gui = (getenv("DISPLAY") != NULL);
619 #else
620 use_gui = TRUE;
621 #endif
622 /* Test whether with/without GUI is forced */
623 for (int i = 1; i < argc; i++) {
624 if (!strcmp(argv[i], "-z")
625 || !strcmp(argv[i], "--without-gui")
626 || !strcmp(argv[i], "-p")
627 || !strncmp(argv[i], "--print", 7)
628 || !strcmp(argv[i], "-e")
629 || !strncmp(argv[i], "--export-png", 12)
630 || !strcmp(argv[i], "-l")
631 || !strncmp(argv[i], "--export-plain-svg", 12)
632 || !strcmp(argv[i], "-i")
633 || !strncmp(argv[i], "--export-area-drawing", 21)
634 || !strcmp(argv[i], "-D")
635 || !strncmp(argv[i], "--export-area-canvas", 20)
636 || !strcmp(argv[i], "-C")
637 || !strncmp(argv[i], "--export-id", 12)
638 || !strcmp(argv[i], "-P")
639 || !strncmp(argv[i], "--export-ps", 11)
640 || !strcmp(argv[i], "-E")
641 || !strncmp(argv[i], "--export-eps", 12)
642 || !strcmp(argv[i], "-A")
643 || !strncmp(argv[i], "--export-pdf", 12)
644 #ifdef WIN32
645 || !strcmp(argv[i], "-M")
646 || !strncmp(argv[i], "--export-emf", 12)
647 #endif //WIN32
648 || !strcmp(argv[i], "-W")
649 || !strncmp(argv[i], "--query-width", 13)
650 || !strcmp(argv[i], "-H")
651 || !strncmp(argv[i], "--query-height", 14)
652 || !strcmp(argv[i], "-S")
653 || !strncmp(argv[i], "--query-all", 11)
654 || !strcmp(argv[i], "-X")
655 || !strncmp(argv[i], "--query-x", 13)
656 || !strcmp(argv[i], "-Y")
657 || !strncmp(argv[i], "--query-y", 14)
658 || !strcmp(argv[i], "--vacuum-defs")
659 || !strncmp(argv[i], "--shell", 7)
660 )
661 {
662 /* main_console handles any exports -- not the gui */
663 use_gui = FALSE;
664 break;
665 } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
666 use_gui = TRUE;
667 break;
668 }
669 }
671 #ifdef WIN32
672 #ifndef REPLACEARGS_ANSI
673 if ( PrintWin32::is_os_wide() )
674 #endif // REPLACEARGS_ANSI
675 {
676 // If the call fails, we'll need to convert charsets
677 needToRecodeParams = !replaceArgs( argc, argv );
678 }
679 #endif // WIN32
681 /// \todo Should this be a static object (see inkscape.cpp)?
682 Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
684 return app.run();
685 }
690 void fixupSingleFilename( gchar **orig, gchar **spare )
691 {
692 if ( orig && *orig && **orig ) {
693 GError *error = NULL;
694 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
695 if ( newFileName )
696 {
697 *orig = newFileName;
698 if ( spare ) {
699 *spare = newFileName;
700 }
701 // g_message("Set a replacement fixup");
702 }
703 }
704 }
708 GSList *fixupFilenameEncoding( GSList* fl )
709 {
710 GSList *newFl = NULL;
711 while ( fl ) {
712 gchar *fn = static_cast<gchar*>(fl->data);
713 fl = g_slist_remove( fl, fl->data );
714 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
715 if ( newFileName ) {
717 if ( 0 )
718 {
719 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
720 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
721 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
722 "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
723 gtk_dialog_run (GTK_DIALOG (w));
724 gtk_widget_destroy (w);
725 g_free(safeNewFn);
726 g_free(safeFn);
727 }
729 g_free( fn );
730 fn = newFileName;
731 newFileName = 0;
732 }
733 else
734 if ( 0 )
735 {
736 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
737 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
738 gtk_dialog_run (GTK_DIALOG (w));
739 gtk_widget_destroy (w);
740 g_free(safeFn);
741 }
742 newFl = g_slist_append( newFl, fn );
743 }
744 return newFl;
745 }
747 int sp_common_main( int argc, char const **argv, GSList **flDest )
748 {
749 /// \todo fixme: Move these to some centralized location (Lauris)
750 sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
751 sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
754 // temporarily switch gettext encoding to locale, so that help messages can be output properly
755 gchar const *charset;
756 g_get_charset(&charset);
758 bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
760 poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
761 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
762 g_return_val_if_fail(ctx != NULL, 1);
764 /* Collect own arguments */
765 GSList *fl = sp_process_args(ctx);
766 poptFreeContext(ctx);
768 // now switch gettext back to UTF-8 (for GUI)
769 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
771 // Now let's see if the file list still holds up
772 if ( needToRecodeParams )
773 {
774 fl = fixupFilenameEncoding( fl );
775 }
777 // Check the globals for filename-fixup
778 if ( needToRecodeParams )
779 {
780 fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
781 fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
782 fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
783 }
784 else
785 {
786 if ( sp_export_png )
787 sp_export_png_utf8 = g_strdup( sp_export_png );
788 if ( sp_export_svg )
789 sp_export_svg_utf8 = g_strdup( sp_export_svg );
790 if ( sp_global_printer )
791 sp_global_printer_utf8 = g_strdup( sp_global_printer );
792 }
794 // Return the list if wanted, else free it up.
795 if ( flDest ) {
796 *flDest = fl;
797 fl = 0;
798 } else {
799 while ( fl ) {
800 g_free( fl->data );
801 fl = g_slist_remove( fl, fl->data );
802 }
803 }
804 return 0;
805 }
807 static void
808 snooper(GdkEvent *event, gpointer /*data*/) {
809 if (inkscape_mapalt()) /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
810 {
811 GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
812 switch (event->type) {
813 case GDK_MOTION_NOTIFY:
814 if(event->motion.state & mapping) {
815 event->motion.state|=GDK_MOD1_MASK;
816 }
817 break;
818 case GDK_BUTTON_PRESS:
819 if(event->button.state & mapping) {
820 event->button.state|=GDK_MOD1_MASK;
821 }
822 break;
823 case GDK_KEY_PRESS:
824 if(event->key.state & mapping) {
825 event->key.state|=GDK_MOD1_MASK;
826 }
827 break;
828 default:
829 break;
830 }
831 }
833 if (inkscape_trackalt()) {
834 // MacOS X with X11 has some problem with the default
835 // xmodmapping. A ~/.xmodmap solution does not work reliably due
836 // to the way we package our executable in a .app that can launch
837 // X11 or use an already-running X11. The same problem has been
838 // reported on Linux but there is no .app/X11 to get in the way
839 // of ~/.xmodmap fixes. So we make this a preference.
840 //
841 // For some reason, Gdk senses changes in Alt (Mod1) state for
842 // many message types, but not for keystrokes! So this ugly hack
843 // tracks what the state of Alt-pressing is, and ensures
844 // GDK_MOD1_MASK is in the event->key.state as appropriate.
845 //
846 static gboolean altL_pressed = FALSE;
847 static gboolean altR_pressed = FALSE;
848 static gboolean alt_pressed = FALSE;
849 guint get_group0_keyval(GdkEventKey* event);
850 guint keyval = 0;
851 switch (event->type) {
852 case GDK_MOTION_NOTIFY:
853 alt_pressed = TRUE && (event->motion.state & GDK_MOD1_MASK);
854 break;
855 case GDK_BUTTON_PRESS:
856 alt_pressed = TRUE && (event->button.state & GDK_MOD1_MASK);
857 break;
858 case GDK_KEY_PRESS:
859 keyval = get_group0_keyval(&event->key);
860 if (keyval == GDK_Alt_L) altL_pressed = TRUE;
861 if (keyval == GDK_Alt_R) altR_pressed = TRUE;
862 alt_pressed = alt_pressed || altL_pressed || altR_pressed;
863 alt_pressed = alt_pressed || (event->button.state & GDK_MOD1_MASK);
864 if (alt_pressed)
865 event->key.state |= GDK_MOD1_MASK;
866 else
867 event->key.state &= ~GDK_MOD1_MASK;
868 break;
869 case GDK_KEY_RELEASE:
870 keyval = get_group0_keyval(&event->key);
871 if (keyval == GDK_Alt_L) altL_pressed = FALSE;
872 if (keyval == GDK_Alt_R) altR_pressed = FALSE;
873 if (!altL_pressed && !altR_pressed)
874 alt_pressed = FALSE;
875 break;
876 default:
877 break;
878 }
879 //printf("alt_pressed: %s\n", alt_pressed? "+" : "-");
880 }
882 gtk_main_do_event (event);
883 }
885 static std::vector<Glib::ustring> getDirectorySet(const gchar* userDir, const gchar* const * systemDirs) {
886 std::vector<Glib::ustring> listing;
887 listing.push_back(userDir);
888 for ( const char* const* cur = systemDirs; *cur; cur++ )
889 {
890 listing.push_back(*cur);
891 }
892 return listing;
893 }
895 int
896 sp_main_gui(int argc, char const **argv)
897 {
898 Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
900 GSList *fl = NULL;
901 int retVal = sp_common_main( argc, argv, &fl );
902 g_return_val_if_fail(retVal == 0, 1);
904 // Add possible icon entry directories
905 std::vector<Glib::ustring> dataDirs = getDirectorySet( g_get_user_data_dir(),
906 g_get_system_data_dirs() );
907 for (std::vector<Glib::ustring>::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
908 {
909 std::vector<Glib::ustring> listing;
910 listing.push_back(*it);
911 listing.push_back("inkscape");
912 listing.push_back("icons");
913 Glib::ustring dir = Glib::build_filename(listing);
914 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir.c_str());
915 }
917 // Add our icon directory to the search path for icon theme lookups.
918 gchar *usericondir = profile_path("icons");
919 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), usericondir);
920 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
921 g_free(usericondir);
923 gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
924 Inkscape::Debug::log_display_config();
926 // Set default window icon. Obeys the theme.
927 gtk_window_set_default_icon_name("inkscape");
928 // Do things that were previously in inkscape_gtk_stock_init().
929 sp_icon_get_phys_size(GTK_ICON_SIZE_MENU);
930 Inkscape::UI::Widget::Panel::prep();
932 gboolean create_new = TRUE;
934 /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
935 inkscape_application_init(argv[0], true);
937 while (fl) {
938 if (sp_file_open((gchar *)fl->data,NULL)) {
939 create_new=FALSE;
940 }
941 fl = g_slist_remove(fl, fl->data);
942 }
943 if (create_new) {
944 sp_file_new_default();
945 }
947 Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
948 main_instance.run();
950 #ifdef WIN32
951 //We might not need anything here
952 //sp_win32_finish(); <-- this is a NOP func
953 #endif
955 return 0;
956 }
958 /**
959 * Process file list
960 */
961 void sp_process_file_list(GSList *fl)
962 {
963 while (fl) {
964 const gchar *filename = (gchar *)fl->data;
965 SPDocument *doc = Inkscape::Extension::open(NULL, filename);
966 if (doc == NULL) {
967 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
968 }
969 if (doc == NULL) {
970 g_warning("Specified document %s cannot be opened (is it a valid SVG file?)", filename);
971 } else {
972 if (sp_vacuum_defs) {
973 vacuum_document(doc);
974 }
975 if (sp_vacuum_defs && !sp_export_svg) {
976 // save under the name given in the command line
977 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
978 }
979 if (sp_global_printer) {
980 sp_print_document_to_file(doc, sp_global_printer);
981 }
982 if (sp_export_png) {
983 sp_do_export_png(doc);
984 }
985 if (sp_export_svg) {
986 Inkscape::XML::Document *rdoc;
987 Inkscape::XML::Node *repr;
988 rdoc = sp_repr_document_new("svg:svg");
989 repr = rdoc->root();
990 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
991 sp_repr_save_rebased_file(repr->document(), sp_export_svg, SP_SVG_NS_URI,
992 doc->base, sp_export_svg);
993 }
994 if (sp_export_ps) {
995 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
996 }
997 if (sp_export_eps) {
998 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
999 }
1000 if (sp_export_pdf) {
1001 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
1002 }
1003 #ifdef WIN32
1004 if (sp_export_emf) {
1005 do_export_emf(doc, sp_export_emf, "image/x-emf");
1006 }
1007 #endif //WIN32
1008 if (sp_query_all) {
1009 do_query_all (doc);
1010 } else if (sp_query_width || sp_query_height) {
1011 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
1012 } else if (sp_query_x || sp_query_y) {
1013 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
1014 }
1016 delete doc;
1017 }
1018 fl = g_slist_remove(fl, fl->data);
1019 }
1020 }
1022 /**
1023 * Run the application as an interactive shell, parsing command lines from stdin
1024 * Returns -1 on error.
1025 */
1026 int sp_main_shell(char const* command_name)
1027 {
1028 int retval = 0;
1030 const unsigned int buffer_size = 4096;
1031 gchar *command_line = g_strnfill(buffer_size, 0);
1032 g_strlcpy(command_line, command_name, buffer_size);
1033 gsize offset = g_strlcat(command_line, " ", buffer_size);
1034 gsize sizeLeft = buffer_size - offset;
1035 gchar *useme = command_line + offset;
1037 fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
1038 fflush(stdout);
1039 char* linedata = 0;
1040 do {
1041 fprintf(stdout, ">");
1042 fflush(stdout);
1043 if ((linedata = fgets(useme, sizeLeft, stdin))) {
1044 size_t len = strlen(useme);
1045 if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
1046 fprintf(stdout, "ERROR: Command line too long\n");
1047 // Consume rest of line
1048 retval = -1; // If the while loop completes, this remains -1
1049 while (fgets(useme, sizeLeft, stdin) && retval) {
1050 len = strlen(command_line);
1051 if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
1052 retval = 0;
1053 }
1054 }
1055 } else {
1056 useme[--len] = '\0'; // Strip newline
1057 if (useme[len - 1] == '\r') {
1058 useme[--len] = '\0';
1059 }
1060 if ( strcmp(useme, "quit") == 0 ) {
1061 // Time to quit
1062 fflush(stdout);
1063 linedata = 0; // mark for exit
1064 } else if ( len < 1 ) {
1065 // blank string. Do nothing.
1066 } else {
1067 GError* parseError = 0;
1068 gchar** argv = 0;
1069 gint argc = 0;
1070 if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
1071 poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
1072 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
1073 if ( ctx ) {
1074 GSList *fl = sp_process_args(ctx);
1075 sp_process_file_list(fl);
1076 poptFreeContext(ctx);
1077 } else {
1078 retval = 1; // not sure why. But this was the previous return value
1079 }
1080 resetCommandlineGlobals();
1081 g_strfreev(argv);
1082 } else {
1083 g_warning("Cannot parse commandline: %s", useme);
1084 }
1085 }
1086 }
1087 } // if (linedata...
1088 } while (linedata && (retval == 0));
1090 g_free(command_line);
1091 return retval;
1092 }
1094 int sp_main_console(int argc, char const **argv)
1095 {
1096 /* We are started in text mode */
1098 /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1099 * in a non-Gtk environment. Used in libnrtype's
1100 * FontInstance.cpp and FontFactory.cpp.
1101 * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1102 */
1103 g_type_init();
1104 char **argv2 = const_cast<char **>(argv);
1105 gtk_init_check( &argc, &argv2 );
1106 //setlocale(LC_ALL, "");
1108 GSList *fl = NULL;
1109 int retVal = sp_common_main( argc, argv, &fl );
1110 g_return_val_if_fail(retVal == 0, 1);
1112 if (fl == NULL && !sp_shell) {
1113 g_print("Nothing to do!\n");
1114 exit(0);
1115 }
1117 inkscape_application_init(argv[0], false);
1119 if (sp_shell) {
1120 sp_main_shell(argv[0]); // Run as interactive shell
1121 exit(0);
1122 } else {
1123 sp_process_file_list(fl); // Normal command line invokation
1124 }
1126 return 0;
1127 }
1129 static void
1130 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1131 {
1132 SPObject *o = NULL;
1134 if (id) {
1135 o = doc->getObjectById(id);
1136 if (o) {
1137 if (!SP_IS_ITEM (o)) {
1138 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1139 return;
1140 }
1141 } else {
1142 g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1143 return;
1144 }
1145 } else {
1146 o = SP_DOCUMENT_ROOT(doc);
1147 }
1149 if (o) {
1150 sp_document_ensure_up_to_date (doc);
1151 SPItem *item = ((SPItem *) o);
1153 // "true" SVG bbox for scripting
1154 Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1155 if (area) {
1156 Inkscape::SVGOStringStream os;
1157 if (extent) {
1158 os << area->dimensions()[axis];
1159 } else {
1160 os << area->min()[axis];
1161 }
1162 g_print ("%s", os.str().c_str());
1163 } else {
1164 g_print("0");
1165 }
1166 }
1167 }
1169 static void
1170 do_query_all (SPDocument *doc)
1171 {
1172 SPObject *o = NULL;
1174 o = SP_DOCUMENT_ROOT(doc);
1176 if (o) {
1177 sp_document_ensure_up_to_date (doc);
1178 do_query_all_recurse(o);
1179 }
1180 }
1182 static void
1183 do_query_all_recurse (SPObject *o)
1184 {
1185 SPItem *item = ((SPItem *) o);
1186 if (o->id && SP_IS_ITEM(item)) {
1187 Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1188 if (area) {
1189 Inkscape::SVGOStringStream os;
1190 os << o->id;
1191 os << "," << area->min()[Geom::X];
1192 os << "," << area->min()[Geom::Y];
1193 os << "," << area->dimensions()[Geom::X];
1194 os << "," << area->dimensions()[Geom::Y];
1195 g_print ("%s\n", os.str().c_str());
1196 }
1197 }
1199 SPObject *child = o->children;
1200 while (child) {
1201 do_query_all_recurse (child);
1202 child = child->next;
1203 }
1204 }
1207 static void
1208 sp_do_export_png(SPDocument *doc)
1209 {
1210 const gchar *filename = NULL;
1211 gdouble dpi = 0.0;
1213 if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1214 g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1215 }
1217 GSList *items = NULL;
1219 Geom::Rect area;
1220 if (sp_export_id || sp_export_area_drawing) {
1222 SPObject *o = NULL;
1223 SPObject *o_area = NULL;
1224 if (sp_export_id && sp_export_area_drawing) {
1225 o = doc->getObjectById(sp_export_id);
1226 o_area = SP_DOCUMENT_ROOT (doc);
1227 } else if (sp_export_id) {
1228 o = doc->getObjectById(sp_export_id);
1229 o_area = o;
1230 } else if (sp_export_area_drawing) {
1231 o = SP_DOCUMENT_ROOT (doc);
1232 o_area = o;
1233 }
1235 if (o) {
1236 if (!SP_IS_ITEM (o)) {
1237 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1238 return;
1239 }
1241 items = g_slist_prepend (items, SP_ITEM(o));
1243 if (sp_export_id_only) {
1244 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1245 }
1247 if (sp_export_use_hints) {
1249 // retrieve export filename hint
1250 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1251 if (fn_hint) {
1252 if (sp_export_png) {
1253 g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1254 filename = sp_export_png;
1255 } else {
1256 filename = fn_hint;
1257 }
1258 } else {
1259 g_warning ("Export filename hint not found for the object.");
1260 filename = sp_export_png;
1261 }
1263 // retrieve export dpi hints
1264 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1265 if (dpi_hint) {
1266 if (sp_export_dpi || sp_export_width || sp_export_height) {
1267 g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1268 } else {
1269 dpi = atof(dpi_hint);
1270 }
1271 } else {
1272 g_warning ("Export DPI hint not found for the object.");
1273 }
1275 }
1277 // write object bbox to area
1278 sp_document_ensure_up_to_date (doc);
1279 Geom::OptRect areaMaybe;
1280 sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1281 if (areaMaybe) {
1282 area = *areaMaybe;
1283 } else {
1284 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1285 return;
1286 }
1287 } else {
1288 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1289 return;
1290 }
1291 }
1293 if (sp_export_area) {
1294 /* Try to parse area (given in SVG pixels) */
1295 gdouble x0,y0,x1,y1;
1296 if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1297 g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1298 return;
1299 }
1300 area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1301 } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1302 /* Export the whole canvas */
1303 sp_document_ensure_up_to_date (doc);
1304 Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1305 area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1306 }
1308 // set filename and dpi from options, if not yet set from the hints
1309 if (!filename) {
1310 if (!sp_export_png) {
1311 g_warning ("No export filename given and no filename hint. Nothing exported.");
1312 return;
1313 }
1314 filename = sp_export_png;
1315 }
1317 if (sp_export_dpi && dpi == 0.0) {
1318 dpi = atof(sp_export_dpi);
1319 if ((dpi < 0.1) || (dpi > 10000.0)) {
1320 g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1321 return;
1322 }
1323 g_print("DPI: %g\n", dpi);
1324 }
1326 if (sp_export_area_snap) {
1327 round_rectangle_outwards(area);
1328 }
1330 // default dpi
1331 if (dpi == 0.0) {
1332 dpi = PX_PER_IN;
1333 }
1335 unsigned long int width = 0;
1336 unsigned long int height = 0;
1338 if (sp_export_width) {
1339 errno=0;
1340 width = strtoul(sp_export_width, NULL, 0);
1341 if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1342 g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1343 return;
1344 }
1345 dpi = (gdouble) width * PX_PER_IN / area.width();
1346 }
1348 if (sp_export_height) {
1349 errno=0;
1350 height = strtoul(sp_export_height, NULL, 0);
1351 if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1352 g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1353 return;
1354 }
1355 dpi = (gdouble) height * PX_PER_IN / area.height();
1356 }
1358 if (!sp_export_width) {
1359 width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1360 }
1362 if (!sp_export_height) {
1363 height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1364 }
1366 guint32 bgcolor = 0x00000000;
1367 if (sp_export_background) {
1368 // override the page color
1369 bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1370 bgcolor |= 0xff; // default is no opacity
1371 } else {
1372 // read from namedview
1373 Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1374 if (nv && nv->attribute("pagecolor"))
1375 bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1376 if (nv && nv->attribute("inkscape:pageopacity"))
1377 bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1378 }
1380 if (sp_export_background_opacity) {
1381 // override opacity
1382 gfloat value;
1383 if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1384 if (value > 1.0) {
1385 value = CLAMP (value, 1.0f, 255.0f);
1386 bgcolor &= (guint32) 0xffffff00;
1387 bgcolor |= (guint32) floor(value);
1388 } else {
1389 value = CLAMP (value, 0.0f, 1.0f);
1390 bgcolor &= (guint32) 0xffffff00;
1391 bgcolor |= SP_COLOR_F_TO_U(value);
1392 }
1393 }
1394 }
1396 g_print("Background RRGGBBAA: %08x\n", bgcolor);
1398 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);
1400 g_print("Bitmap saved as: %s\n", filename);
1402 if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1403 sp_export_png_file(doc, filename, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1404 } else {
1405 g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1406 }
1408 g_slist_free (items);
1409 }
1412 /**
1413 * Perform a PDF/PS/EPS export
1414 *
1415 * \param doc Document to export.
1416 * \param uri URI to export to.
1417 * \param mime MIME type to export as.
1418 */
1420 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1421 {
1422 Inkscape::Extension::DB::OutputList o;
1423 Inkscape::Extension::db.get_output_list(o);
1424 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1425 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1426 i++;
1427 }
1429 if (i == o.end())
1430 {
1431 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1432 return;
1433 }
1435 if (sp_export_id) {
1436 SPObject *o = doc->getObjectById(sp_export_id);
1437 if (o == NULL) {
1438 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1439 return;
1440 }
1441 (*i)->set_param_string ("exportId", sp_export_id);
1442 } else {
1443 (*i)->set_param_string ("exportId", "");
1444 }
1446 if (sp_export_area_canvas && sp_export_area_drawing) {
1447 g_warning ("You cannot use --export-area-canvas and --export-area-drawing at the same time; only the former will take effect.");
1448 sp_export_area_drawing = false;
1449 }
1451 if (sp_export_area_drawing) {
1452 (*i)->set_param_bool ("areaDrawing", TRUE);
1453 } else {
1454 (*i)->set_param_bool ("areaDrawing", FALSE);
1455 }
1457 if (sp_export_area_canvas) {
1458 if (sp_export_eps) {
1459 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.");
1460 }
1461 (*i)->set_param_bool ("areaCanvas", TRUE);
1462 } else {
1463 (*i)->set_param_bool ("areaCanvas", FALSE);
1464 }
1466 if (!sp_export_area_drawing && !sp_export_area_canvas && !sp_export_id) {
1467 // neither is set, set canvas as default for ps/pdf and drawing for eps
1468 if (sp_export_eps) {
1469 try {
1470 (*i)->set_param_bool("areaDrawing", TRUE);
1471 } catch (...) {}
1472 }
1473 }
1475 if (sp_export_text_to_path) {
1476 (*i)->set_param_bool("textToPath", TRUE);
1477 } else {
1478 (*i)->set_param_bool("textToPath", FALSE);
1479 }
1481 if (sp_export_ignore_filters) {
1482 (*i)->set_param_bool("blurToBitmap", FALSE);
1483 } else {
1484 (*i)->set_param_bool("blurToBitmap", TRUE);
1486 gdouble dpi = 90.0;
1487 if (sp_export_dpi) {
1488 dpi = atof(sp_export_dpi);
1489 if ((dpi < 1) || (dpi > 10000.0)) {
1490 g_warning("DPI value %s out of range [1 - 10000]. Using 90 dpi instead.", sp_export_dpi);
1491 dpi = 90;
1492 }
1493 }
1495 (*i)->set_param_int("resolution", (int) dpi);
1496 }
1498 (*i)->save(doc, uri);
1499 }
1501 #ifdef WIN32
1502 /**
1503 * Export a document to EMF
1504 *
1505 * \param doc Document to export.
1506 * \param uri URI to export to.
1507 * \param mime MIME type to export as (should be "image/x-emf")
1508 */
1510 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1511 {
1512 Inkscape::Extension::DB::OutputList o;
1513 Inkscape::Extension::db.get_output_list(o);
1514 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1515 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1516 i++;
1517 }
1519 if (i == o.end())
1520 {
1521 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1522 return;
1523 }
1525 (*i)->save(doc, uri);
1526 }
1527 #endif //WIN32
1529 #ifdef WIN32
1530 bool replaceArgs( int& argc, char**& argv )
1531 {
1532 bool worked = false;
1534 #ifdef REPLACEARGS_DEBUG
1535 MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1536 #endif // REPLACEARGS_DEBUG
1538 wchar_t* line = GetCommandLineW();
1539 if ( line )
1540 {
1541 #ifdef REPLACEARGS_DEBUG
1542 {
1543 gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1544 if ( utf8Line )
1545 {
1546 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1547 {
1548 char tmp[strlen(safe) + 32];
1549 snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1550 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1551 }
1552 }
1553 }
1554 #endif // REPLACEARGS_DEBUG
1556 int numArgs = 0;
1557 wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1559 #ifdef REPLACEARGS_ANSI
1560 // test code for trying things on Win95/98/ME
1561 if ( !parsed )
1562 {
1563 #ifdef REPLACEARGS_DEBUG
1564 MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1565 #endif // REPLACEARGS_DEBUG
1566 int lineLen = wcslen(line) + 1;
1567 wchar_t* lineDup = new wchar_t[lineLen];
1568 wcsncpy( lineDup, line, lineLen );
1570 int pos = 0;
1571 bool inQuotes = false;
1572 bool inWhitespace = true;
1573 std::vector<int> places;
1574 while ( lineDup[pos] )
1575 {
1576 if ( inQuotes )
1577 {
1578 if ( lineDup[pos] == L'"' )
1579 {
1580 inQuotes = false;
1581 }
1582 }
1583 else if ( lineDup[pos] == L'"' )
1584 {
1585 inQuotes = true;
1586 inWhitespace = false;
1587 places.push_back(pos);
1588 }
1589 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1590 {
1591 if ( !inWhitespace )
1592 {
1593 inWhitespace = true;
1594 lineDup[pos] = 0;
1595 }
1596 }
1597 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1598 {
1599 inWhitespace = false;
1600 places.push_back(pos);
1601 }
1602 else
1603 {
1604 // consume
1605 }
1606 pos++;
1607 }
1608 #ifdef REPLACEARGS_DEBUG
1609 {
1610 char tmp[256];
1611 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1612 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1613 }
1614 #endif // REPLACEARGS_DEBUG
1616 wchar_t** block = new wchar_t*[places.size()];
1617 int i = 0;
1618 for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1619 {
1620 block[i++] = &lineDup[*it];
1621 }
1622 parsed = block;
1623 numArgs = places.size();
1624 }
1625 #endif // REPLACEARGS_ANSI
1627 if ( parsed )
1628 {
1629 std::vector<wchar_t*>expandedArgs;
1630 if ( numArgs > 0 )
1631 {
1632 expandedArgs.push_back( parsed[0] );
1633 }
1635 for ( int i1 = 1; i1 < numArgs; i1++ )
1636 {
1637 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1638 wildcarded &= parsed[i1][0] != L'"';
1639 wildcarded &= parsed[i1][0] != L'-';
1640 if ( wildcarded )
1641 {
1642 #ifdef REPLACEARGS_ANSI
1643 WIN32_FIND_DATAA data;
1644 #else
1645 WIN32_FIND_DATAW data;
1646 #endif // REPLACEARGS_ANSI
1648 memset((void *)&data, 0, sizeof(data));
1650 int baseLen = wcslen(parsed[i1]) + 2;
1651 wchar_t* base = new wchar_t[baseLen];
1652 wcsncpy( base, parsed[i1], baseLen );
1653 wchar_t* last = wcsrchr( base, L'\\' );
1654 if ( last )
1655 {
1656 last[1] = 0;
1657 }
1658 else
1659 {
1660 base[0] = 0;
1661 }
1662 baseLen = wcslen( base );
1664 #ifdef REPLACEARGS_ANSI
1665 char target[MAX_PATH];
1666 if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1667 {
1668 HANDLE hf = FindFirstFileA( target, &data );
1669 #else
1670 HANDLE hf = FindFirstFileW( parsed[i1], &data );
1671 #endif // REPLACEARGS_ANSI
1672 if ( hf != INVALID_HANDLE_VALUE )
1673 {
1674 BOOL found = TRUE;
1675 do
1676 {
1677 #ifdef REPLACEARGS_ANSI
1678 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1679 if ( howMany > 0 )
1680 {
1681 howMany += baseLen;
1682 wchar_t* tmp = new wchar_t[howMany + 1];
1683 wcsncpy( tmp, base, howMany + 1 );
1684 MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1685 expandedArgs.push_back( tmp );
1686 found = FindNextFileA( hf, &data );
1687 }
1688 #else
1689 int howMany = wcslen(data.cFileName) + baseLen;
1690 wchar_t* tmp = new wchar_t[howMany + 1];
1691 wcsncpy( tmp, base, howMany + 1 );
1692 wcsncat( tmp, data.cFileName, howMany + 1 );
1693 expandedArgs.push_back( tmp );
1694 found = FindNextFileW( hf, &data );
1695 #endif // REPLACEARGS_ANSI
1696 } while ( found );
1698 FindClose( hf );
1699 }
1700 else
1701 {
1702 expandedArgs.push_back( parsed[i1] );
1703 }
1704 #ifdef REPLACEARGS_ANSI
1705 }
1706 #endif // REPLACEARGS_ANSI
1708 delete[] base;
1709 }
1710 else
1711 {
1712 expandedArgs.push_back( parsed[i1] );
1713 }
1714 }
1716 {
1717 wchar_t** block = new wchar_t*[expandedArgs.size()];
1718 int iz = 0;
1719 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1720 {
1721 block[iz++] = *it;
1722 }
1723 parsed = block;
1724 numArgs = expandedArgs.size();
1725 }
1727 std::vector<gchar*> newArgs;
1728 for ( int i = 0; i < numArgs; i++ )
1729 {
1730 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1731 if ( replacement )
1732 {
1733 #ifdef REPLACEARGS_DEBUG
1734 gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1736 if ( safe2 )
1737 {
1738 {
1739 char tmp[1024];
1740 snprintf( tmp, sizeof(tmp), " [%2d] = '%s'", i, safe2 );
1741 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1742 }
1743 g_free( safe2 );
1744 }
1745 #endif // REPLACEARGS_DEBUG
1747 newArgs.push_back( replacement );
1748 }
1749 else
1750 {
1751 newArgs.push_back( blankParam );
1752 }
1753 }
1755 // Now push our munged params to be the new argv and argc
1756 {
1757 char** block = new char*[newArgs.size()];
1758 int iz = 0;
1759 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1760 {
1761 block[iz++] = *it;
1762 }
1763 argv = block;
1764 argc = newArgs.size();
1765 worked = true;
1766 }
1767 }
1768 #ifdef REPLACEARGS_DEBUG
1769 else
1770 {
1771 MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1772 }
1773 #endif // REPLACEARGS_DEBUG
1774 }
1775 #ifdef REPLACEARGS_DEBUG
1776 else
1777 {
1778 {
1779 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1780 }
1782 char* line2 = GetCommandLineA();
1783 if ( line2 )
1784 {
1785 gchar *safe = Inkscape::IO::sanitizeString(line2);
1786 {
1787 {
1788 char tmp[strlen(safe) + 32];
1789 snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1790 MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1791 }
1792 }
1793 }
1794 else
1795 {
1796 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1797 }
1798 }
1799 #endif // REPLACEARGS_DEBUG
1801 return worked;
1802 }
1803 #endif // WIN32
1805 static GSList *
1806 sp_process_args(poptContext ctx)
1807 {
1808 GSList *fl = NULL;
1810 gint a;
1811 while ((a = poptGetNextOpt(ctx)) != -1) {
1812 switch (a) {
1813 case SP_ARG_FILE: {
1814 gchar const *fn = poptGetOptArg(ctx);
1815 if (fn != NULL) {
1816 fl = g_slist_append(fl, g_strdup(fn));
1817 }
1818 break;
1819 }
1820 case SP_ARG_VERSION: {
1821 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1822 exit(0);
1823 break;
1824 }
1825 case SP_ARG_EXTENSIONDIR: {
1826 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1827 exit(0);
1828 break;
1829 }
1830 case SP_ARG_VERB_LIST: {
1831 // This really shouldn't go here, we should init the app.
1832 // But, since we're just exiting in this path, there is
1833 // no harm, and this is really a better place to put
1834 // everything else.
1835 Inkscape::Extension::init();
1836 Inkscape::Verb::list();
1837 exit(0);
1838 break;
1839 }
1840 case SP_ARG_VERB:
1841 case SP_ARG_SELECT: {
1842 gchar const *arg = poptGetOptArg(ctx);
1843 if (arg != NULL) {
1844 // printf("Adding in: %s\n", arg);
1845 new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1846 }
1847 break;
1848 }
1849 case POPT_ERROR_BADOPT: {
1850 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1851 exit(1);
1852 break;
1853 }
1854 default: {
1855 break;
1856 }
1857 }
1858 }
1860 gchar const ** const args = poptGetArgs(ctx);
1861 if (args != NULL) {
1862 for (unsigned i = 0; args[i] != NULL; i++) {
1863 fl = g_slist_append(fl, g_strdup(args[i]));
1864 }
1865 }
1867 return fl;
1868 }
1871 /*
1872 Local Variables:
1873 mode:c++
1874 c-file-style:"stroustrup"
1875 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1876 indent-tabs-mode:nil
1877 fill-column:99
1878 End:
1879 */
1880 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :