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_("The resolution used for exporting SVG into bitmap (default 90)"),
295 N_("DPI")},
297 {"export-area", 'a',
298 POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
299 N_("Exported area in SVG user units (default is the canvas; 0,0 is lower-left corner)"),
300 N_("x0:y0:x1:y1")},
302 {"export-area-drawing", 'D',
303 POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
304 N_("Exported area is the entire drawing (not canvas)"),
305 NULL},
307 {"export-area-canvas", 'C',
308 POPT_ARG_NONE, &sp_export_area_canvas, SP_ARG_EXPORT_AREA_CANVAS,
309 N_("Exported area is the entire canvas"),
310 NULL},
312 {"export-area-snap", 0,
313 POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
314 N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
315 NULL},
317 {"export-width", 'w',
318 POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
319 N_("The width of exported bitmap in pixels (overrides export-dpi)"),
320 N_("WIDTH")},
322 {"export-height", 'h',
323 POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
324 N_("The height of exported bitmap in pixels (overrides export-dpi)"),
325 N_("HEIGHT")},
327 {"export-id", 'i',
328 POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
329 N_("The ID of the object to export"),
330 N_("ID")},
332 {"export-id-only", 'j',
333 POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
334 // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
335 // See "man inkscape" for details.
336 N_("Export just the object with export-id, hide all others (only with export-id)"),
337 NULL},
339 {"export-use-hints", 't',
340 POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
341 N_("Use stored filename and DPI hints when exporting (only with export-id)"),
342 NULL},
344 {"export-background", 'b',
345 POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
346 N_("Background color of exported bitmap (any SVG-supported color string)"),
347 N_("COLOR")},
349 {"export-background-opacity", 'y',
350 POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
351 N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
352 N_("VALUE")},
354 {"export-plain-svg", 'l',
355 POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
356 N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
357 N_("FILENAME")},
359 {"export-ps", 'P',
360 POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
361 N_("Export document to a PS file"),
362 N_("FILENAME")},
364 {"export-eps", 'E',
365 POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
366 N_("Export document to an EPS file"),
367 N_("FILENAME")},
369 {"export-pdf", 'A',
370 POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
371 N_("Export document to a PDF file"),
372 N_("FILENAME")},
374 #ifdef WIN32
375 {"export-emf", 'M',
376 POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
377 N_("Export document to an Enhanced Metafile (EMF) File"),
378 N_("FILENAME")},
379 #endif //WIN32
381 {"export-text-to-path", 'T',
382 POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
383 N_("Convert text object to paths on export (PS, EPS, PDF)"),
384 NULL},
386 {"export-ignore-filters", 0,
387 POPT_ARG_NONE, &sp_export_ignore_filters, SP_ARG_EXPORT_IGNORE_FILTERS,
388 N_("Render filtered objects without filters, instead of rasterizing (PS, EPS, PDF)"),
389 NULL},
391 {"query-x", 'X',
392 POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
393 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
394 N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
395 NULL},
397 {"query-y", 'Y',
398 POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
399 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
400 N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
401 NULL},
403 {"query-width", 'W',
404 POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
405 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
406 N_("Query the width of the drawing or, if specified, of the object with --query-id"),
407 NULL},
409 {"query-height", 'H',
410 POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
411 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
412 N_("Query the height of the drawing or, if specified, of the object with --query-id"),
413 NULL},
415 {"query-all", 'S',
416 POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
417 N_("List id,x,y,w,h for all objects"),
418 NULL},
420 {"query-id", 'I',
421 POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
422 N_("The ID of the object whose dimensions are queried"),
423 N_("ID")},
425 {"extension-directory", 'x',
426 POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
427 // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
428 N_("Print out the extension directory and exit"),
429 NULL},
431 {"vacuum-defs", 0,
432 POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
433 N_("Remove unused definitions from the defs section(s) of the document"),
434 NULL},
436 {"verb-list", 0,
437 POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
438 N_("List the IDs of all the verbs in Inkscape"),
439 NULL},
441 {"verb", 0,
442 POPT_ARG_STRING, NULL, SP_ARG_VERB,
443 N_("Verb to call when Inkscape opens."),
444 N_("VERB-ID")},
446 {"select", 0,
447 POPT_ARG_STRING, NULL, SP_ARG_SELECT,
448 N_("Object ID to select when Inkscape opens."),
449 N_("OBJECT-ID")},
451 {"shell", 0,
452 POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
453 N_("Start Inkscape in interative shell mode."),
454 NULL},
456 POPT_AUTOHELP POPT_TABLEEND
457 };
459 static bool needToRecodeParams = true;
460 gchar * blankParam = g_strdup("");
464 #ifdef WIN32
466 /**
467 * Return the directory of the .exe that is currently running
468 */
469 static Glib::ustring _win32_getExePath()
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_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
987 }
988 if (sp_export_ps) {
989 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
990 }
991 if (sp_export_eps) {
992 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
993 }
994 if (sp_export_pdf) {
995 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
996 }
997 #ifdef WIN32
998 if (sp_export_emf) {
999 do_export_emf(doc, sp_export_emf, "image/x-emf");
1000 }
1001 #endif //WIN32
1002 if (sp_query_all) {
1003 do_query_all (doc);
1004 } else if (sp_query_width || sp_query_height) {
1005 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
1006 } else if (sp_query_x || sp_query_y) {
1007 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
1008 }
1010 delete doc;
1011 }
1012 fl = g_slist_remove(fl, fl->data);
1013 }
1014 }
1016 /**
1017 * Run the application as an interactive shell, parsing command lines from stdin
1018 * Returns -1 on error.
1019 */
1020 int sp_main_shell(char const* command_name)
1021 {
1022 int retval = 0;
1024 const unsigned int buffer_size = 4096;
1025 gchar *command_line = g_strnfill(buffer_size, 0);
1026 g_strlcpy(command_line, command_name, buffer_size);
1027 gsize offset = g_strlcat(command_line, " ", buffer_size);
1028 gsize sizeLeft = buffer_size - offset;
1029 gchar *useme = command_line + offset;
1031 fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
1032 fflush(stdout);
1033 char* linedata = 0;
1034 do {
1035 fprintf(stdout, ">");
1036 fflush(stdout);
1037 if ((linedata = fgets(useme, sizeLeft, stdin))) {
1038 size_t len = strlen(useme);
1039 if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
1040 fprintf(stdout, "ERROR: Command line too long\n");
1041 // Consume rest of line
1042 retval = -1; // If the while loop completes, this remains -1
1043 while (fgets(useme, sizeLeft, stdin) && retval) {
1044 len = strlen(command_line);
1045 if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
1046 retval = 0;
1047 }
1048 }
1049 } else {
1050 useme[--len] = '\0'; // Strip newline
1051 if (useme[len - 1] == '\r') {
1052 useme[--len] = '\0';
1053 }
1054 if ( strcmp(useme, "quit") == 0 ) {
1055 // Time to quit
1056 fflush(stdout);
1057 linedata = 0; // mark for exit
1058 } else if ( len < 1 ) {
1059 // blank string. Do nothing.
1060 } else {
1061 GError* parseError = 0;
1062 gchar** argv = 0;
1063 gint argc = 0;
1064 if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
1065 poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
1066 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
1067 if ( ctx ) {
1068 GSList *fl = sp_process_args(ctx);
1069 sp_process_file_list(fl);
1070 poptFreeContext(ctx);
1071 } else {
1072 retval = 1; // not sure why. But this was the previous return value
1073 }
1074 resetCommandlineGlobals();
1075 g_strfreev(argv);
1076 } else {
1077 g_warning("Cannot parse commandline: %s", useme);
1078 }
1079 }
1080 }
1081 } // if (linedata...
1082 } while (linedata && (retval == 0));
1084 g_free(command_line);
1085 return retval;
1086 }
1088 int sp_main_console(int argc, char const **argv)
1089 {
1090 /* We are started in text mode */
1092 /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1093 * in a non-Gtk environment. Used in libnrtype's
1094 * FontInstance.cpp and FontFactory.cpp.
1095 * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1096 */
1097 g_type_init();
1098 char **argv2 = const_cast<char **>(argv);
1099 gtk_init_check( &argc, &argv2 );
1100 //setlocale(LC_ALL, "");
1102 GSList *fl = NULL;
1103 int retVal = sp_common_main( argc, argv, &fl );
1104 g_return_val_if_fail(retVal == 0, 1);
1106 if (fl == NULL && !sp_shell) {
1107 g_print("Nothing to do!\n");
1108 exit(0);
1109 }
1111 inkscape_application_init(argv[0], false);
1113 if (sp_shell) {
1114 sp_main_shell(argv[0]); // Run as interactive shell
1115 exit(0);
1116 } else {
1117 sp_process_file_list(fl); // Normal command line invokation
1118 }
1120 return 0;
1121 }
1123 static void
1124 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1125 {
1126 SPObject *o = NULL;
1128 if (id) {
1129 o = doc->getObjectById(id);
1130 if (o) {
1131 if (!SP_IS_ITEM (o)) {
1132 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1133 return;
1134 }
1135 } else {
1136 g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1137 return;
1138 }
1139 } else {
1140 o = SP_DOCUMENT_ROOT(doc);
1141 }
1143 if (o) {
1144 sp_document_ensure_up_to_date (doc);
1145 SPItem *item = ((SPItem *) o);
1147 // "true" SVG bbox for scripting
1148 Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1149 if (area) {
1150 Inkscape::SVGOStringStream os;
1151 if (extent) {
1152 os << area->dimensions()[axis];
1153 } else {
1154 os << area->min()[axis];
1155 }
1156 g_print ("%s", os.str().c_str());
1157 } else {
1158 g_print("0");
1159 }
1160 }
1161 }
1163 static void
1164 do_query_all (SPDocument *doc)
1165 {
1166 SPObject *o = NULL;
1168 o = SP_DOCUMENT_ROOT(doc);
1170 if (o) {
1171 sp_document_ensure_up_to_date (doc);
1172 do_query_all_recurse(o);
1173 }
1174 }
1176 static void
1177 do_query_all_recurse (SPObject *o)
1178 {
1179 SPItem *item = ((SPItem *) o);
1180 if (o->id && SP_IS_ITEM(item)) {
1181 Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1182 if (area) {
1183 Inkscape::SVGOStringStream os;
1184 os << o->id;
1185 os << "," << area->min()[Geom::X];
1186 os << "," << area->min()[Geom::Y];
1187 os << "," << area->dimensions()[Geom::X];
1188 os << "," << area->dimensions()[Geom::Y];
1189 g_print ("%s\n", os.str().c_str());
1190 }
1191 }
1193 SPObject *child = o->children;
1194 while (child) {
1195 do_query_all_recurse (child);
1196 child = child->next;
1197 }
1198 }
1201 static void
1202 sp_do_export_png(SPDocument *doc)
1203 {
1204 const gchar *filename = NULL;
1205 gdouble dpi = 0.0;
1207 if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1208 g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1209 }
1211 GSList *items = NULL;
1213 Geom::Rect area;
1214 if (sp_export_id || sp_export_area_drawing) {
1216 SPObject *o = NULL;
1217 SPObject *o_area = NULL;
1218 if (sp_export_id && sp_export_area_drawing) {
1219 o = doc->getObjectById(sp_export_id);
1220 o_area = SP_DOCUMENT_ROOT (doc);
1221 } else if (sp_export_id) {
1222 o = doc->getObjectById(sp_export_id);
1223 o_area = o;
1224 } else if (sp_export_area_drawing) {
1225 o = SP_DOCUMENT_ROOT (doc);
1226 o_area = o;
1227 }
1229 if (o) {
1230 if (!SP_IS_ITEM (o)) {
1231 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1232 return;
1233 }
1235 items = g_slist_prepend (items, SP_ITEM(o));
1237 if (sp_export_id_only) {
1238 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1239 }
1241 if (sp_export_use_hints) {
1243 // retrieve export filename hint
1244 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1245 if (fn_hint) {
1246 if (sp_export_png) {
1247 g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1248 filename = sp_export_png;
1249 } else {
1250 filename = fn_hint;
1251 }
1252 } else {
1253 g_warning ("Export filename hint not found for the object.");
1254 filename = sp_export_png;
1255 }
1257 // retrieve export dpi hints
1258 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1259 if (dpi_hint) {
1260 if (sp_export_dpi || sp_export_width || sp_export_height) {
1261 g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1262 } else {
1263 dpi = atof(dpi_hint);
1264 }
1265 } else {
1266 g_warning ("Export DPI hint not found for the object.");
1267 }
1269 }
1271 // write object bbox to area
1272 sp_document_ensure_up_to_date (doc);
1273 Geom::OptRect areaMaybe;
1274 sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1275 if (areaMaybe) {
1276 area = *areaMaybe;
1277 } else {
1278 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1279 return;
1280 }
1281 } else {
1282 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1283 return;
1284 }
1285 }
1287 if (sp_export_area) {
1288 /* Try to parse area (given in SVG pixels) */
1289 gdouble x0,y0,x1,y1;
1290 if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1291 g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1292 return;
1293 }
1294 area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1295 } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1296 /* Export the whole canvas */
1297 sp_document_ensure_up_to_date (doc);
1298 Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1299 area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1300 }
1302 // set filename and dpi from options, if not yet set from the hints
1303 if (!filename) {
1304 if (!sp_export_png) {
1305 g_warning ("No export filename given and no filename hint. Nothing exported.");
1306 return;
1307 }
1308 filename = sp_export_png;
1309 }
1311 if (sp_export_dpi && dpi == 0.0) {
1312 dpi = atof(sp_export_dpi);
1313 if ((dpi < 0.1) || (dpi > 10000.0)) {
1314 g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1315 return;
1316 }
1317 g_print("DPI: %g\n", dpi);
1318 }
1320 if (sp_export_area_snap) {
1321 round_rectangle_outwards(area);
1322 }
1324 // default dpi
1325 if (dpi == 0.0) {
1326 dpi = PX_PER_IN;
1327 }
1329 unsigned long int width = 0;
1330 unsigned long int height = 0;
1332 if (sp_export_width) {
1333 errno=0;
1334 width = strtoul(sp_export_width, NULL, 0);
1335 if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1336 g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1337 return;
1338 }
1339 dpi = (gdouble) width * PX_PER_IN / area.width();
1340 }
1342 if (sp_export_height) {
1343 errno=0;
1344 height = strtoul(sp_export_height, NULL, 0);
1345 if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1346 g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1347 return;
1348 }
1349 dpi = (gdouble) height * PX_PER_IN / area.height();
1350 }
1352 if (!sp_export_width) {
1353 width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1354 }
1356 if (!sp_export_height) {
1357 height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1358 }
1360 guint32 bgcolor = 0x00000000;
1361 if (sp_export_background) {
1362 // override the page color
1363 bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1364 bgcolor |= 0xff; // default is no opacity
1365 } else {
1366 // read from namedview
1367 Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1368 if (nv && nv->attribute("pagecolor"))
1369 bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1370 if (nv && nv->attribute("inkscape:pageopacity"))
1371 bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1372 }
1374 if (sp_export_background_opacity) {
1375 // override opacity
1376 gfloat value;
1377 if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1378 if (value > 1.0) {
1379 value = CLAMP (value, 1.0f, 255.0f);
1380 bgcolor &= (guint32) 0xffffff00;
1381 bgcolor |= (guint32) floor(value);
1382 } else {
1383 value = CLAMP (value, 0.0f, 1.0f);
1384 bgcolor &= (guint32) 0xffffff00;
1385 bgcolor |= SP_COLOR_F_TO_U(value);
1386 }
1387 }
1388 }
1390 g_print("Background RRGGBBAA: %08x\n", bgcolor);
1392 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);
1394 g_print("Bitmap saved as: %s\n", filename);
1396 if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1397 sp_export_png_file(doc, filename, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1398 } else {
1399 g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1400 }
1402 g_slist_free (items);
1403 }
1406 /**
1407 * Perform a PDF/PS/EPS export
1408 *
1409 * \param doc Document to export.
1410 * \param uri URI to export to.
1411 * \param mime MIME type to export as.
1412 */
1414 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1415 {
1416 Inkscape::Extension::DB::OutputList o;
1417 Inkscape::Extension::db.get_output_list(o);
1418 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1419 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1420 i++;
1421 }
1423 if (i == o.end())
1424 {
1425 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1426 return;
1427 }
1429 if (sp_export_id) {
1430 SPObject *o = doc->getObjectById(sp_export_id);
1431 if (o == NULL) {
1432 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1433 return;
1434 }
1435 (*i)->set_param_string ("exportId", sp_export_id);
1436 } else {
1437 (*i)->set_param_string ("exportId", "");
1438 }
1440 if (sp_export_area_canvas && sp_export_area_drawing) {
1441 g_warning ("You cannot use --export-area-canvas and --export-area-drawing at the same time; only the former will take effect.");
1442 sp_export_area_drawing = false;
1443 }
1445 if (sp_export_area_drawing) {
1446 (*i)->set_param_bool ("areaDrawing", TRUE);
1447 } else {
1448 (*i)->set_param_bool ("areaDrawing", FALSE);
1449 }
1451 if (sp_export_area_canvas) {
1452 if (sp_export_eps) {
1453 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.");
1454 }
1455 (*i)->set_param_bool ("areaCanvas", TRUE);
1456 } else {
1457 (*i)->set_param_bool ("areaCanvas", FALSE);
1458 }
1460 if (!sp_export_area_drawing && !sp_export_area_canvas && !sp_export_id) {
1461 // neither is set, set canvas as default for ps/pdf and drawing for eps
1462 if (sp_export_eps) {
1463 try {
1464 (*i)->set_param_bool("areaDrawing", TRUE);
1465 } catch (...) {}
1466 }
1467 }
1469 if (sp_export_text_to_path) {
1470 (*i)->set_param_bool("textToPath", TRUE);
1471 } else {
1472 (*i)->set_param_bool("textToPath", FALSE);
1473 }
1475 if (sp_export_ignore_filters) {
1476 (*i)->set_param_bool("blurToBitmap", FALSE);
1477 } else {
1478 (*i)->set_param_bool("blurToBitmap", TRUE);
1479 }
1481 (*i)->save(doc, uri);
1482 }
1484 #ifdef WIN32
1485 /**
1486 * Export a document to EMF
1487 *
1488 * \param doc Document to export.
1489 * \param uri URI to export to.
1490 * \param mime MIME type to export as (should be "image/x-emf")
1491 */
1493 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1494 {
1495 Inkscape::Extension::DB::OutputList o;
1496 Inkscape::Extension::db.get_output_list(o);
1497 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1498 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1499 i++;
1500 }
1502 if (i == o.end())
1503 {
1504 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1505 return;
1506 }
1508 (*i)->save(doc, uri);
1509 }
1510 #endif //WIN32
1512 #ifdef WIN32
1513 bool replaceArgs( int& argc, char**& argv )
1514 {
1515 bool worked = false;
1517 #ifdef REPLACEARGS_DEBUG
1518 MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1519 #endif // REPLACEARGS_DEBUG
1521 wchar_t* line = GetCommandLineW();
1522 if ( line )
1523 {
1524 #ifdef REPLACEARGS_DEBUG
1525 {
1526 gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1527 if ( utf8Line )
1528 {
1529 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1530 {
1531 char tmp[strlen(safe) + 32];
1532 snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1533 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1534 }
1535 }
1536 }
1537 #endif // REPLACEARGS_DEBUG
1539 int numArgs = 0;
1540 wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1542 #ifdef REPLACEARGS_ANSI
1543 // test code for trying things on Win95/98/ME
1544 if ( !parsed )
1545 {
1546 #ifdef REPLACEARGS_DEBUG
1547 MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1548 #endif // REPLACEARGS_DEBUG
1549 int lineLen = wcslen(line) + 1;
1550 wchar_t* lineDup = new wchar_t[lineLen];
1551 wcsncpy( lineDup, line, lineLen );
1553 int pos = 0;
1554 bool inQuotes = false;
1555 bool inWhitespace = true;
1556 std::vector<int> places;
1557 while ( lineDup[pos] )
1558 {
1559 if ( inQuotes )
1560 {
1561 if ( lineDup[pos] == L'"' )
1562 {
1563 inQuotes = false;
1564 }
1565 }
1566 else if ( lineDup[pos] == L'"' )
1567 {
1568 inQuotes = true;
1569 inWhitespace = false;
1570 places.push_back(pos);
1571 }
1572 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1573 {
1574 if ( !inWhitespace )
1575 {
1576 inWhitespace = true;
1577 lineDup[pos] = 0;
1578 }
1579 }
1580 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1581 {
1582 inWhitespace = false;
1583 places.push_back(pos);
1584 }
1585 else
1586 {
1587 // consume
1588 }
1589 pos++;
1590 }
1591 #ifdef REPLACEARGS_DEBUG
1592 {
1593 char tmp[256];
1594 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1595 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1596 }
1597 #endif // REPLACEARGS_DEBUG
1599 wchar_t** block = new wchar_t*[places.size()];
1600 int i = 0;
1601 for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1602 {
1603 block[i++] = &lineDup[*it];
1604 }
1605 parsed = block;
1606 numArgs = places.size();
1607 }
1608 #endif // REPLACEARGS_ANSI
1610 if ( parsed )
1611 {
1612 std::vector<wchar_t*>expandedArgs;
1613 if ( numArgs > 0 )
1614 {
1615 expandedArgs.push_back( parsed[0] );
1616 }
1618 for ( int i1 = 1; i1 < numArgs; i1++ )
1619 {
1620 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1621 wildcarded &= parsed[i1][0] != L'"';
1622 wildcarded &= parsed[i1][0] != L'-';
1623 if ( wildcarded )
1624 {
1625 #ifdef REPLACEARGS_ANSI
1626 WIN32_FIND_DATAA data;
1627 #else
1628 WIN32_FIND_DATAW data;
1629 #endif // REPLACEARGS_ANSI
1631 memset((void *)&data, 0, sizeof(data));
1633 int baseLen = wcslen(parsed[i1]) + 2;
1634 wchar_t* base = new wchar_t[baseLen];
1635 wcsncpy( base, parsed[i1], baseLen );
1636 wchar_t* last = wcsrchr( base, L'\\' );
1637 if ( last )
1638 {
1639 last[1] = 0;
1640 }
1641 else
1642 {
1643 base[0] = 0;
1644 }
1645 baseLen = wcslen( base );
1647 #ifdef REPLACEARGS_ANSI
1648 char target[MAX_PATH];
1649 if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1650 {
1651 HANDLE hf = FindFirstFileA( target, &data );
1652 #else
1653 HANDLE hf = FindFirstFileW( parsed[i1], &data );
1654 #endif // REPLACEARGS_ANSI
1655 if ( hf != INVALID_HANDLE_VALUE )
1656 {
1657 BOOL found = TRUE;
1658 do
1659 {
1660 #ifdef REPLACEARGS_ANSI
1661 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1662 if ( howMany > 0 )
1663 {
1664 howMany += baseLen;
1665 wchar_t* tmp = new wchar_t[howMany + 1];
1666 wcsncpy( tmp, base, howMany + 1 );
1667 MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1668 expandedArgs.push_back( tmp );
1669 found = FindNextFileA( hf, &data );
1670 }
1671 #else
1672 int howMany = wcslen(data.cFileName) + baseLen;
1673 wchar_t* tmp = new wchar_t[howMany + 1];
1674 wcsncpy( tmp, base, howMany + 1 );
1675 wcsncat( tmp, data.cFileName, howMany + 1 );
1676 expandedArgs.push_back( tmp );
1677 found = FindNextFileW( hf, &data );
1678 #endif // REPLACEARGS_ANSI
1679 } while ( found );
1681 FindClose( hf );
1682 }
1683 else
1684 {
1685 expandedArgs.push_back( parsed[i1] );
1686 }
1687 #ifdef REPLACEARGS_ANSI
1688 }
1689 #endif // REPLACEARGS_ANSI
1691 delete[] base;
1692 }
1693 else
1694 {
1695 expandedArgs.push_back( parsed[i1] );
1696 }
1697 }
1699 {
1700 wchar_t** block = new wchar_t*[expandedArgs.size()];
1701 int iz = 0;
1702 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1703 {
1704 block[iz++] = *it;
1705 }
1706 parsed = block;
1707 numArgs = expandedArgs.size();
1708 }
1710 std::vector<gchar*> newArgs;
1711 for ( int i = 0; i < numArgs; i++ )
1712 {
1713 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1714 if ( replacement )
1715 {
1716 #ifdef REPLACEARGS_DEBUG
1717 gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1719 if ( safe2 )
1720 {
1721 {
1722 char tmp[1024];
1723 snprintf( tmp, sizeof(tmp), " [%2d] = '%s'", i, safe2 );
1724 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1725 }
1726 g_free( safe2 );
1727 }
1728 #endif // REPLACEARGS_DEBUG
1730 newArgs.push_back( replacement );
1731 }
1732 else
1733 {
1734 newArgs.push_back( blankParam );
1735 }
1736 }
1738 // Now push our munged params to be the new argv and argc
1739 {
1740 char** block = new char*[newArgs.size()];
1741 int iz = 0;
1742 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1743 {
1744 block[iz++] = *it;
1745 }
1746 argv = block;
1747 argc = newArgs.size();
1748 worked = true;
1749 }
1750 }
1751 #ifdef REPLACEARGS_DEBUG
1752 else
1753 {
1754 MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1755 }
1756 #endif // REPLACEARGS_DEBUG
1757 }
1758 #ifdef REPLACEARGS_DEBUG
1759 else
1760 {
1761 {
1762 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1763 }
1765 char* line2 = GetCommandLineA();
1766 if ( line2 )
1767 {
1768 gchar *safe = Inkscape::IO::sanitizeString(line2);
1769 {
1770 {
1771 char tmp[strlen(safe) + 32];
1772 snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1773 MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1774 }
1775 }
1776 }
1777 else
1778 {
1779 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1780 }
1781 }
1782 #endif // REPLACEARGS_DEBUG
1784 return worked;
1785 }
1786 #endif // WIN32
1788 static GSList *
1789 sp_process_args(poptContext ctx)
1790 {
1791 GSList *fl = NULL;
1793 gint a;
1794 while ((a = poptGetNextOpt(ctx)) != -1) {
1795 switch (a) {
1796 case SP_ARG_FILE: {
1797 gchar const *fn = poptGetOptArg(ctx);
1798 if (fn != NULL) {
1799 fl = g_slist_append(fl, g_strdup(fn));
1800 }
1801 break;
1802 }
1803 case SP_ARG_VERSION: {
1804 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1805 exit(0);
1806 break;
1807 }
1808 case SP_ARG_EXTENSIONDIR: {
1809 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1810 exit(0);
1811 break;
1812 }
1813 case SP_ARG_VERB_LIST: {
1814 // This really shouldn't go here, we should init the app.
1815 // But, since we're just exiting in this path, there is
1816 // no harm, and this is really a better place to put
1817 // everything else.
1818 Inkscape::Extension::init();
1819 Inkscape::Verb::list();
1820 exit(0);
1821 break;
1822 }
1823 case SP_ARG_VERB:
1824 case SP_ARG_SELECT: {
1825 gchar const *arg = poptGetOptArg(ctx);
1826 if (arg != NULL) {
1827 // printf("Adding in: %s\n", arg);
1828 new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1829 }
1830 break;
1831 }
1832 case POPT_ERROR_BADOPT: {
1833 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1834 exit(1);
1835 break;
1836 }
1837 default: {
1838 break;
1839 }
1840 }
1841 }
1843 gchar const ** const args = poptGetArgs(ctx);
1844 if (args != NULL) {
1845 for (unsigned i = 0; args[i] != NULL; i++) {
1846 fl = g_slist_append(fl, g_strdup(args[i]));
1847 }
1848 }
1850 return fl;
1851 }
1854 /*
1855 Local Variables:
1856 mode:c++
1857 c-file-style:"stroustrup"
1858 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1859 indent-tabs-mode:nil
1860 fill-column:99
1861 End:
1862 */
1863 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :