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