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 #include <gtk/gtkmessagedialog.h>
33 #ifdef HAVE_IEEEFP_H
34 #include <ieeefp.h>
35 #endif
36 #include <cstring>
37 #include <string>
38 #include <locale.h>
39 #include <stdlib.h>
41 #include <popt.h>
42 #ifndef POPT_TABLEEND
43 #define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
44 #endif /* Not def: POPT_TABLEEND */
46 #include <libxml/tree.h>
47 #include <glib-object.h>
48 #include <gtk/gtkmain.h>
49 #include <gtk/gtksignal.h>
50 #include <gtk/gtkwindow.h>
51 #include <gtk/gtkbox.h>
53 #include "gc-core.h"
55 #include "macros.h"
56 #include "file.h"
57 #include "document.h"
58 #include "sp-object.h"
59 #include "interface.h"
60 #include "print.h"
61 #include "color.h"
62 #include "sp-item.h"
63 #include "sp-root.h"
64 #include "unit-constants.h"
66 #include "svg/svg.h"
67 #include "svg/svg-color.h"
68 #include "svg/stringstream.h"
70 #include "inkscape-private.h"
71 #include "inkscape-stock.h"
72 #include "inkscape-version.h"
74 #include "sp-namedview.h"
75 #include "sp-guide.h"
76 #include "sp-object-repr.h"
77 #include "xml/repr.h"
79 #include "io/sys.h"
81 #include "debug/logger.h"
82 #include "debug/log-display-config.h"
84 #include "helper/png-write.h"
85 #include "helper/geom.h"
87 #include <extension/extension.h>
88 #include <extension/system.h>
89 #include <extension/db.h>
90 #include <extension/output.h>
92 #ifdef WIN32
93 //#define REPLACEARGS_ANSI
94 //#define REPLACEARGS_DEBUG
96 #include "registrytool.h"
98 #include "extension/internal/win32.h"
99 using Inkscape::Extension::Internal::PrintWin32;
101 #endif // WIN32
103 #include "extension/init.h"
105 #include <glibmm/i18n.h>
106 #include <gtkmm/main.h>
108 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
109 #define bind_textdomain_codeset(p,c)
110 #endif
112 #include "application/application.h"
114 #include "main-cmdlineact.h"
116 #include <png.h>
117 #include <errno.h>
119 enum {
120 SP_ARG_NONE,
121 SP_ARG_NOGUI,
122 SP_ARG_GUI,
123 SP_ARG_FILE,
124 SP_ARG_PRINT,
125 SP_ARG_EXPORT_PNG,
126 SP_ARG_EXPORT_DPI,
127 SP_ARG_EXPORT_AREA,
128 SP_ARG_EXPORT_AREA_DRAWING,
129 SP_ARG_EXPORT_AREA_CANVAS,
130 SP_ARG_EXPORT_AREA_SNAP,
131 SP_ARG_EXPORT_WIDTH,
132 SP_ARG_EXPORT_HEIGHT,
133 SP_ARG_EXPORT_ID,
134 SP_ARG_EXPORT_ID_ONLY,
135 SP_ARG_EXPORT_USE_HINTS,
136 SP_ARG_EXPORT_BACKGROUND,
137 SP_ARG_EXPORT_BACKGROUND_OPACITY,
138 SP_ARG_EXPORT_SVG,
139 SP_ARG_EXPORT_PS,
140 SP_ARG_EXPORT_EPS,
141 SP_ARG_EXPORT_PDF,
142 #ifdef WIN32
143 SP_ARG_EXPORT_EMF,
144 #endif //WIN32
145 SP_ARG_EXPORT_TEXT_TO_PATH,
146 SP_ARG_EXPORT_IGNORE_FILTERS,
147 SP_ARG_EXTENSIONDIR,
148 SP_ARG_QUERY_X,
149 SP_ARG_QUERY_Y,
150 SP_ARG_QUERY_WIDTH,
151 SP_ARG_QUERY_HEIGHT,
152 SP_ARG_QUERY_ALL,
153 SP_ARG_QUERY_ID,
154 SP_ARG_SHELL,
155 SP_ARG_VERSION,
156 SP_ARG_VACUUM_DEFS,
157 SP_ARG_VERB_LIST,
158 SP_ARG_VERB,
159 SP_ARG_SELECT,
160 SP_ARG_LAST
161 };
163 int sp_main_gui(int argc, char const **argv);
164 int sp_main_console(int argc, char const **argv);
165 static void sp_do_export_png(SPDocument *doc);
166 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime);
167 #ifdef WIN32
168 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
169 #endif //WIN32
170 static void do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id);
171 static void do_query_all (SPDocument *doc);
172 static void do_query_all_recurse (SPObject *o);
174 static gchar *sp_global_printer = NULL;
175 static gchar *sp_export_png = NULL;
176 static gchar *sp_export_dpi = NULL;
177 static gchar *sp_export_area = NULL;
178 static gboolean sp_export_area_drawing = FALSE;
179 static gboolean sp_export_area_canvas = FALSE;
180 static gchar *sp_export_width = NULL;
181 static gchar *sp_export_height = NULL;
182 static gchar *sp_export_id = NULL;
183 static gchar *sp_export_background = NULL;
184 static gchar *sp_export_background_opacity = NULL;
185 static gboolean sp_export_area_snap = FALSE;
186 static gboolean sp_export_use_hints = FALSE;
187 static gboolean sp_export_id_only = FALSE;
188 static gchar *sp_export_svg = NULL;
189 static gchar *sp_export_ps = NULL;
190 static gchar *sp_export_eps = NULL;
191 static gchar *sp_export_pdf = NULL;
192 #ifdef WIN32
193 static gchar *sp_export_emf = NULL;
194 #endif //WIN32
195 static gboolean sp_export_text_to_path = FALSE;
196 static gboolean sp_export_ignore_filters = FALSE;
197 static gboolean sp_export_font = FALSE;
198 static gboolean sp_query_x = FALSE;
199 static gboolean sp_query_y = FALSE;
200 static gboolean sp_query_width = FALSE;
201 static gboolean sp_query_height = FALSE;
202 static gboolean sp_query_all = FALSE;
203 static gchar *sp_query_id = NULL;
204 static int sp_new_gui = FALSE;
205 static gboolean sp_shell = FALSE;
206 static gboolean sp_vacuum_defs = FALSE;
208 static gchar *sp_export_png_utf8 = NULL;
209 static gchar *sp_export_svg_utf8 = NULL;
210 static gchar *sp_global_printer_utf8 = NULL;
213 /**
214 * Reset variables to default values.
215 */
216 static void resetCommandlineGlobals() {
217 sp_global_printer = NULL;
218 sp_export_png = NULL;
219 sp_export_dpi = NULL;
220 sp_export_area = NULL;
221 sp_export_area_drawing = FALSE;
222 sp_export_area_canvas = FALSE;
223 sp_export_width = NULL;
224 sp_export_height = NULL;
225 sp_export_id = NULL;
226 sp_export_background = NULL;
227 sp_export_background_opacity = NULL;
228 sp_export_area_snap = FALSE;
229 sp_export_use_hints = FALSE;
230 sp_export_id_only = FALSE;
231 sp_export_svg = NULL;
232 sp_export_ps = NULL;
233 sp_export_eps = NULL;
234 sp_export_pdf = NULL;
235 #ifdef WIN32
236 sp_export_emf = NULL;
237 #endif //WIN32
238 sp_export_text_to_path = FALSE;
239 sp_export_ignore_filters = FALSE;
240 sp_export_font = FALSE;
241 sp_query_x = FALSE;
242 sp_query_y = FALSE;
243 sp_query_width = FALSE;
244 sp_query_height = FALSE;
245 sp_query_all = FALSE;
246 sp_query_id = NULL;
247 sp_vacuum_defs = FALSE;
249 sp_export_png_utf8 = NULL;
250 sp_export_svg_utf8 = NULL;
251 sp_global_printer_utf8 = NULL;
252 }
254 #ifdef WIN32
255 static bool replaceArgs( int& argc, char**& argv );
256 #endif
257 static GSList *sp_process_args(poptContext ctx);
258 struct poptOption options[] = {
259 {"version", 'V',
260 POPT_ARG_NONE, NULL, SP_ARG_VERSION,
261 N_("Print the Inkscape version number"),
262 NULL},
264 {"without-gui", 'z',
265 POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
266 N_("Do not use X server (only process files from console)"),
267 NULL},
269 {"with-gui", 'g',
270 POPT_ARG_NONE, NULL, SP_ARG_GUI,
271 N_("Try to use X server (even if $DISPLAY is not set)"),
272 NULL},
274 {"file", 'f',
275 POPT_ARG_STRING, NULL, SP_ARG_FILE,
276 N_("Open specified document(s) (option string may be excluded)"),
277 N_("FILENAME")},
279 {"print", 'p',
280 POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
281 N_("Print document(s) to specified output file (use '| program' for pipe)"),
282 N_("FILENAME")},
284 {"export-png", 'e',
285 POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
286 N_("Export document to a PNG file"),
287 N_("FILENAME")},
289 {"export-dpi", 'd',
290 POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
291 N_("The resolution used for exporting SVG into bitmap (default 90)"),
292 N_("DPI")},
294 {"export-area", 'a',
295 POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
296 N_("Exported area in SVG user units (default is the canvas; 0,0 is lower-left corner)"),
297 N_("x0:y0:x1:y1")},
299 {"export-area-drawing", 'D',
300 POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
301 N_("Exported area is the entire drawing (not canvas)"),
302 NULL},
304 {"export-area-canvas", 'C',
305 POPT_ARG_NONE, &sp_export_area_canvas, SP_ARG_EXPORT_AREA_CANVAS,
306 N_("Exported area is the entire canvas"),
307 NULL},
309 {"export-area-snap", 0,
310 POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
311 N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
312 NULL},
314 {"export-width", 'w',
315 POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
316 N_("The width of exported bitmap in pixels (overrides export-dpi)"),
317 N_("WIDTH")},
319 {"export-height", 'h',
320 POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
321 N_("The height of exported bitmap in pixels (overrides export-dpi)"),
322 N_("HEIGHT")},
324 {"export-id", 'i',
325 POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
326 N_("The ID of the object to export"),
327 N_("ID")},
329 {"export-id-only", 'j',
330 POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
331 // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
332 // See "man inkscape" for details.
333 N_("Export just the object with export-id, hide all others (only with export-id)"),
334 NULL},
336 {"export-use-hints", 't',
337 POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
338 N_("Use stored filename and DPI hints when exporting (only with export-id)"),
339 NULL},
341 {"export-background", 'b',
342 POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
343 N_("Background color of exported bitmap (any SVG-supported color string)"),
344 N_("COLOR")},
346 {"export-background-opacity", 'y',
347 POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
348 N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
349 N_("VALUE")},
351 {"export-plain-svg", 'l',
352 POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
353 N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
354 N_("FILENAME")},
356 {"export-ps", 'P',
357 POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
358 N_("Export document to a PS file"),
359 N_("FILENAME")},
361 {"export-eps", 'E',
362 POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
363 N_("Export document to an EPS file"),
364 N_("FILENAME")},
366 {"export-pdf", 'A',
367 POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
368 N_("Export document to a PDF file"),
369 N_("FILENAME")},
371 #ifdef WIN32
372 {"export-emf", 'M',
373 POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
374 N_("Export document to an Enhanced Metafile (EMF) File"),
375 N_("FILENAME")},
376 #endif //WIN32
378 {"export-text-to-path", 'T',
379 POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
380 N_("Convert text object to paths on export (PS, EPS, PDF)"),
381 NULL},
383 {"export-ignore-filters", 0,
384 POPT_ARG_NONE, &sp_export_ignore_filters, SP_ARG_EXPORT_IGNORE_FILTERS,
385 N_("Render filtered objects without filters, instead of rasterizing (PS, EPS, PDF)"),
386 NULL},
388 {"query-x", 'X',
389 POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
390 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
391 N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
392 NULL},
394 {"query-y", 'Y',
395 POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
396 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
397 N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
398 NULL},
400 {"query-width", 'W',
401 POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
402 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
403 N_("Query the width of the drawing or, if specified, of the object with --query-id"),
404 NULL},
406 {"query-height", 'H',
407 POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
408 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
409 N_("Query the height of the drawing or, if specified, of the object with --query-id"),
410 NULL},
412 {"query-all", 'S',
413 POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
414 N_("List id,x,y,w,h for all objects"),
415 NULL},
417 {"query-id", 'I',
418 POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
419 N_("The ID of the object whose dimensions are queried"),
420 N_("ID")},
422 {"extension-directory", 'x',
423 POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
424 // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
425 N_("Print out the extension directory and exit"),
426 NULL},
428 {"vacuum-defs", 0,
429 POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
430 N_("Remove unused definitions from the defs section(s) of the document"),
431 NULL},
433 {"verb-list", 0,
434 POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
435 N_("List the IDs of all the verbs in Inkscape"),
436 NULL},
438 {"verb", 0,
439 POPT_ARG_STRING, NULL, SP_ARG_VERB,
440 N_("Verb to call when Inkscape opens."),
441 N_("VERB-ID")},
443 {"select", 0,
444 POPT_ARG_STRING, NULL, SP_ARG_SELECT,
445 N_("Object ID to select when Inkscape opens."),
446 N_("OBJECT-ID")},
448 {"shell", 0,
449 POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
450 N_("Start Inkscape in interative shell mode."),
451 NULL},
453 POPT_AUTOHELP POPT_TABLEEND
454 };
456 static bool needToRecodeParams = true;
457 gchar * blankParam = g_strdup("");
461 #ifdef WIN32
463 /**
464 * Return the directory of the .exe that is currently running
465 */
466 static Glib::ustring _win32_getExePath()
467 {
468 char exeName[MAX_PATH+1];
469 GetModuleFileName(NULL, exeName, MAX_PATH);
470 char *slashPos = strrchr(exeName, '\\');
471 if (slashPos)
472 *slashPos = '\0';
473 Glib::ustring s = exeName;
474 return s;
475 }
477 /**
478 * Set up the PATH and PYTHONPATH environment variables on
479 * win32
480 */
481 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
482 {
484 char *oldenv = getenv("PATH");
485 Glib::ustring tmp = "PATH=";
486 tmp += exePath;
487 tmp += ";";
488 tmp += exePath;
489 tmp += "\\python;";
490 tmp += exePath;
491 tmp += "\\python\\Scripts;"; // for uniconv.cmd
492 tmp += exePath;
493 tmp += "\\perl";
494 if(oldenv != NULL) {
495 tmp += ";";
496 tmp += oldenv;
497 }
498 _putenv(tmp.c_str());
500 oldenv = getenv("PYTHONPATH");
501 tmp = "PYTHONPATH=";
502 tmp += exePath;
503 tmp += "\\python;";
504 tmp += exePath;
505 tmp += "\\python\\Lib;";
506 tmp += exePath;
507 tmp += "\\python\\DLLs";
508 if(oldenv != NULL) {
509 tmp += ";";
510 tmp += oldenv;
511 }
512 _putenv(tmp.c_str());
514 return 0;
515 }
516 #endif
518 /**
519 * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
520 * can find inkex.py et al. (Bug #197475)
521 */
522 static int set_extensions_env()
523 {
524 char *oldenv = getenv("PYTHONPATH");
525 Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
526 if (oldenv != NULL) {
527 tmp += G_SEARCHPATH_SEPARATOR;
528 tmp += oldenv;
529 }
530 g_setenv("PYTHONPATH", tmp.c_str(), TRUE);
532 return 0;
533 }
536 /**
537 * This is the classic main() entry point of the program, though on some
538 * architectures it might be called by something else.
539 */
540 int
541 main(int argc, char **argv)
542 {
543 #ifdef HAVE_FPSETMASK
544 /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__. It's probably
545 safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
546 the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
547 fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
548 #endif
550 #ifdef WIN32
551 /*
552 Set the current directory to the directory of the
553 executable. This seems redundant, but is needed for
554 when inkscape.exe is executed from another directory.
555 We use relative paths on win32.
556 HKCR\svgfile\shell\open\command is a good example
557 */
558 Glib::ustring homedir = _win32_getExePath();
559 SetCurrentDirectory(homedir.c_str());
560 _win32_set_inkscape_env(homedir);
561 RegistryTool rt;
562 rt.setPathInfo();
563 #endif
565 // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
566 Gtk::Main::init_gtkmm_internals();
568 // Bug #197475
569 set_extensions_env();
571 /**
572 * Call bindtextdomain() for various machines's paths
573 */
574 #ifdef ENABLE_NLS
575 #ifdef WIN32
576 Glib::ustring localePath = homedir;
577 localePath += "\\";
578 localePath += PACKAGE_LOCALE_DIR;
579 bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
580 #else
581 #ifdef ENABLE_BINRELOC
582 bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
583 #else
584 bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
585 #endif
586 #endif
587 // Allow the user to override the locale directory by setting
588 // the environment variable INKSCAPE_LOCALEDIR.
589 char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
590 if (inkscape_localedir != NULL) {
591 bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
592 }
593 #endif
595 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
597 #ifdef ENABLE_NLS
598 textdomain(GETTEXT_PACKAGE);
599 #endif
601 LIBXML_TEST_VERSION
603 Inkscape::GC::init();
605 Inkscape::Debug::Logger::init();
607 gboolean use_gui;
609 #ifndef WIN32
610 use_gui = (getenv("DISPLAY") != NULL);
611 #else
612 use_gui = TRUE;
613 #endif
614 /* Test whether with/without GUI is forced */
615 for (int i = 1; i < argc; i++) {
616 if (!strcmp(argv[i], "-z")
617 || !strcmp(argv[i], "--without-gui")
618 || !strcmp(argv[i], "-p")
619 || !strncmp(argv[i], "--print", 7)
620 || !strcmp(argv[i], "-e")
621 || !strncmp(argv[i], "--export-png", 12)
622 || !strcmp(argv[i], "-l")
623 || !strncmp(argv[i], "--export-plain-svg", 12)
624 || !strcmp(argv[i], "-i")
625 || !strncmp(argv[i], "--export-area-drawing", 21)
626 || !strcmp(argv[i], "-D")
627 || !strncmp(argv[i], "--export-area-canvas", 20)
628 || !strcmp(argv[i], "-C")
629 || !strncmp(argv[i], "--export-id", 12)
630 || !strcmp(argv[i], "-P")
631 || !strncmp(argv[i], "--export-ps", 11)
632 || !strcmp(argv[i], "-E")
633 || !strncmp(argv[i], "--export-eps", 12)
634 || !strcmp(argv[i], "-A")
635 || !strncmp(argv[i], "--export-pdf", 12)
636 #ifdef WIN32
637 || !strcmp(argv[i], "-M")
638 || !strncmp(argv[i], "--export-emf", 12)
639 #endif //WIN32
640 || !strcmp(argv[i], "-W")
641 || !strncmp(argv[i], "--query-width", 13)
642 || !strcmp(argv[i], "-H")
643 || !strncmp(argv[i], "--query-height", 14)
644 || !strcmp(argv[i], "-S")
645 || !strncmp(argv[i], "--query-all", 11)
646 || !strcmp(argv[i], "-X")
647 || !strncmp(argv[i], "--query-x", 13)
648 || !strcmp(argv[i], "-Y")
649 || !strncmp(argv[i], "--query-y", 14)
650 || !strcmp(argv[i], "--vacuum-defs")
651 || !strncmp(argv[i], "--shell", 7)
652 )
653 {
654 /* main_console handles any exports -- not the gui */
655 use_gui = FALSE;
656 break;
657 } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
658 use_gui = TRUE;
659 break;
660 }
661 }
663 #ifdef WIN32
664 #ifndef REPLACEARGS_ANSI
665 if ( PrintWin32::is_os_wide() )
666 #endif // REPLACEARGS_ANSI
667 {
668 // If the call fails, we'll need to convert charsets
669 needToRecodeParams = !replaceArgs( argc, argv );
670 }
671 #endif // WIN32
673 /// \todo Should this be a static object (see inkscape.cpp)?
674 Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
676 return app.run();
677 }
682 void fixupSingleFilename( gchar **orig, gchar **spare )
683 {
684 if ( orig && *orig && **orig ) {
685 GError *error = NULL;
686 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
687 if ( newFileName )
688 {
689 *orig = newFileName;
690 if ( spare ) {
691 *spare = newFileName;
692 }
693 // g_message("Set a replacement fixup");
694 }
695 }
696 }
700 GSList *fixupFilenameEncoding( GSList* fl )
701 {
702 GSList *newFl = NULL;
703 while ( fl ) {
704 gchar *fn = static_cast<gchar*>(fl->data);
705 fl = g_slist_remove( fl, fl->data );
706 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
707 if ( newFileName ) {
709 if ( 0 )
710 {
711 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
712 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
713 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
714 "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
715 gtk_dialog_run (GTK_DIALOG (w));
716 gtk_widget_destroy (w);
717 g_free(safeNewFn);
718 g_free(safeFn);
719 }
721 g_free( fn );
722 fn = newFileName;
723 newFileName = 0;
724 }
725 else
726 if ( 0 )
727 {
728 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
729 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
730 gtk_dialog_run (GTK_DIALOG (w));
731 gtk_widget_destroy (w);
732 g_free(safeFn);
733 }
734 newFl = g_slist_append( newFl, fn );
735 }
736 return newFl;
737 }
739 int sp_common_main( int argc, char const **argv, GSList **flDest )
740 {
741 /// \todo fixme: Move these to some centralized location (Lauris)
742 sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
743 sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
746 // temporarily switch gettext encoding to locale, so that help messages can be output properly
747 gchar const *charset;
748 g_get_charset(&charset);
750 bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
752 poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
753 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
754 g_return_val_if_fail(ctx != NULL, 1);
756 /* Collect own arguments */
757 GSList *fl = sp_process_args(ctx);
758 poptFreeContext(ctx);
760 // now switch gettext back to UTF-8 (for GUI)
761 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
763 // Now let's see if the file list still holds up
764 if ( needToRecodeParams )
765 {
766 fl = fixupFilenameEncoding( fl );
767 }
769 // Check the globals for filename-fixup
770 if ( needToRecodeParams )
771 {
772 fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
773 fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
774 fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
775 }
776 else
777 {
778 if ( sp_export_png )
779 sp_export_png_utf8 = g_strdup( sp_export_png );
780 if ( sp_export_svg )
781 sp_export_svg_utf8 = g_strdup( sp_export_svg );
782 if ( sp_global_printer )
783 sp_global_printer_utf8 = g_strdup( sp_global_printer );
784 }
786 // Return the list if wanted, else free it up.
787 if ( flDest ) {
788 *flDest = fl;
789 fl = 0;
790 } else {
791 while ( fl ) {
792 g_free( fl->data );
793 fl = g_slist_remove( fl, fl->data );
794 }
795 }
796 return 0;
797 }
799 static void
800 snooper(GdkEvent *event, gpointer /*data*/) {
801 if(inkscape_mapalt()) /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
802 {
803 GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
804 switch (event->type) {
805 case GDK_MOTION_NOTIFY:
806 if(event->motion.state & mapping) {
807 event->motion.state|=GDK_MOD1_MASK;
808 }
809 break;
810 case GDK_BUTTON_PRESS:
811 if(event->button.state & mapping) {
812 event->button.state|=GDK_MOD1_MASK;
813 }
814 break;
815 case GDK_KEY_PRESS:
816 if(event->key.state & mapping) {
817 event->key.state|=GDK_MOD1_MASK;
818 }
819 break;
820 default:
821 break;
822 }
823 }
824 gtk_main_do_event (event);
825 }
827 int
828 sp_main_gui(int argc, char const **argv)
829 {
830 Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
832 GSList *fl = NULL;
833 int retVal = sp_common_main( argc, argv, &fl );
834 g_return_val_if_fail(retVal == 0, 1);
836 inkscape_gtk_stock_init();
838 gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
840 Inkscape::Debug::log_display_config();
842 /* Set default icon */
843 gchar *filename = (gchar *) g_build_filename (INKSCAPE_APPICONDIR, "inkscape.png", NULL);
844 if (Inkscape::IO::file_test(filename, (GFileTest)(G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))) {
845 gtk_window_set_default_icon_from_file(filename, NULL);
846 }
847 g_free (filename);
848 filename = 0;
850 gboolean create_new = TRUE;
852 /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
853 inkscape_application_init(argv[0], true);
855 while (fl) {
856 if (sp_file_open((gchar *)fl->data,NULL)) {
857 create_new=FALSE;
858 }
859 fl = g_slist_remove(fl, fl->data);
860 }
861 if (create_new) {
862 sp_file_new_default();
863 }
865 Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
866 main_instance.run();
868 #ifdef WIN32
869 //We might not need anything here
870 //sp_win32_finish(); <-- this is a NOP func
871 #endif
873 return 0;
874 }
876 /**
877 * Process file list
878 */
879 void sp_process_file_list(GSList *fl)
880 {
881 while (fl) {
882 const gchar *filename = (gchar *)fl->data;
883 SPDocument *doc = Inkscape::Extension::open(NULL, filename);
884 if (doc == NULL) {
885 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
886 }
887 if (doc == NULL) {
888 g_warning("Specified document %s cannot be opened (is it a valid SVG file?)", filename);
889 } else {
890 if (sp_vacuum_defs) {
891 vacuum_document(doc);
892 }
893 if (sp_vacuum_defs && !sp_export_svg) {
894 // save under the name given in the command line
895 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
896 }
897 if (sp_global_printer) {
898 sp_print_document_to_file(doc, sp_global_printer);
899 }
900 if (sp_export_png) {
901 sp_do_export_png(doc);
902 }
903 if (sp_export_svg) {
904 Inkscape::XML::Document *rdoc;
905 Inkscape::XML::Node *repr;
906 rdoc = sp_repr_document_new("svg:svg");
907 repr = rdoc->root();
908 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
909 sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
910 }
911 if (sp_export_ps) {
912 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
913 }
914 if (sp_export_eps) {
915 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
916 }
917 if (sp_export_pdf) {
918 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
919 }
920 #ifdef WIN32
921 if (sp_export_emf) {
922 do_export_emf(doc, sp_export_emf, "image/x-emf");
923 }
924 #endif //WIN32
925 if (sp_query_all) {
926 do_query_all (doc);
927 } else if (sp_query_width || sp_query_height) {
928 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
929 } else if (sp_query_x || sp_query_y) {
930 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
931 }
933 delete doc;
934 }
935 fl = g_slist_remove(fl, fl->data);
936 }
937 }
939 /**
940 * Run the application as an interactive shell, parsing command lines from stdin
941 * Returns -1 on error.
942 */
943 int sp_main_shell(char const* command_name)
944 {
945 int retval = 0;
947 const unsigned int buffer_size = 4096;
948 gchar *command_line = g_strnfill(buffer_size, 0);
949 g_strlcpy(command_line, command_name, buffer_size);
950 gsize offset = g_strlcat(command_line, " ", buffer_size);
951 gsize sizeLeft = buffer_size - offset;
952 gchar *useme = command_line + offset;
954 fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
955 fflush(stdout);
956 char* linedata = 0;
957 do {
958 fprintf(stdout, ">");
959 fflush(stdout);
960 if ((linedata = fgets(useme, sizeLeft, stdin))) {
961 size_t len = strlen(useme);
962 if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
963 fprintf(stdout, "ERROR: Command line too long\n");
964 // Consume rest of line
965 retval = -1; // If the while loop completes, this remains -1
966 while (fgets(useme, sizeLeft, stdin) && retval) {
967 len = strlen(command_line);
968 if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
969 retval = 0;
970 }
971 }
972 } else {
973 useme[--len] = '\0'; // Strip newline
974 if (useme[len - 1] == '\r') {
975 useme[--len] = '\0';
976 }
977 if ( strcmp(useme, "quit") == 0 ) {
978 // Time to quit
979 fflush(stdout);
980 linedata = 0; // mark for exit
981 } else if ( len < 1 ) {
982 // blank string. Do nothing.
983 } else {
984 GError* parseError = 0;
985 gchar** argv = 0;
986 gint argc = 0;
987 if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
988 poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
989 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
990 if ( ctx ) {
991 GSList *fl = sp_process_args(ctx);
992 sp_process_file_list(fl);
993 poptFreeContext(ctx);
994 } else {
995 retval = 1; // not sure why. But this was the previous return value
996 }
997 resetCommandlineGlobals();
998 g_strfreev(argv);
999 } else {
1000 g_warning("Cannot parse commandline: %s", useme);
1001 }
1002 }
1003 }
1004 } // if (linedata...
1005 } while (linedata && (retval == 0));
1007 g_free(command_line);
1008 return retval;
1009 }
1011 int sp_main_console(int argc, char const **argv)
1012 {
1013 /* We are started in text mode */
1015 /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1016 * in a non-Gtk environment. Used in libnrtype's
1017 * FontInstance.cpp and FontFactory.cpp.
1018 * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1019 */
1020 g_type_init();
1021 char **argv2 = const_cast<char **>(argv);
1022 gtk_init_check( &argc, &argv2 );
1023 //setlocale(LC_ALL, "");
1025 GSList *fl = NULL;
1026 int retVal = sp_common_main( argc, argv, &fl );
1027 g_return_val_if_fail(retVal == 0, 1);
1029 if (fl == NULL && !sp_shell) {
1030 g_print("Nothing to do!\n");
1031 exit(0);
1032 }
1034 inkscape_application_init(argv[0], false);
1036 if (sp_shell) {
1037 sp_main_shell(argv[0]); // Run as interactive shell
1038 exit(0);
1039 } else {
1040 sp_process_file_list(fl); // Normal command line invokation
1041 }
1043 return 0;
1044 }
1046 static void
1047 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1048 {
1049 SPObject *o = NULL;
1051 if (id) {
1052 o = doc->getObjectById(id);
1053 if (o) {
1054 if (!SP_IS_ITEM (o)) {
1055 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1056 return;
1057 }
1058 } else {
1059 g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1060 return;
1061 }
1062 } else {
1063 o = SP_DOCUMENT_ROOT(doc);
1064 }
1066 if (o) {
1067 sp_document_ensure_up_to_date (doc);
1068 SPItem *item = ((SPItem *) o);
1070 // "true" SVG bbox for scripting
1071 Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1072 if (area) {
1073 Inkscape::SVGOStringStream os;
1074 if (extent) {
1075 os << area->dimensions()[axis];
1076 } else {
1077 os << area->min()[axis];
1078 }
1079 g_print ("%s", os.str().c_str());
1080 } else {
1081 g_print("0");
1082 }
1083 }
1084 }
1086 static void
1087 do_query_all (SPDocument *doc)
1088 {
1089 SPObject *o = NULL;
1091 o = SP_DOCUMENT_ROOT(doc);
1093 if (o) {
1094 sp_document_ensure_up_to_date (doc);
1095 do_query_all_recurse(o);
1096 }
1097 }
1099 static void
1100 do_query_all_recurse (SPObject *o)
1101 {
1102 SPItem *item = ((SPItem *) o);
1103 if (o->id && SP_IS_ITEM(item)) {
1104 Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1105 if (area) {
1106 Inkscape::SVGOStringStream os;
1107 os << o->id;
1108 os << "," << area->min()[Geom::X];
1109 os << "," << area->min()[Geom::Y];
1110 os << "," << area->dimensions()[Geom::X];
1111 os << "," << area->dimensions()[Geom::Y];
1112 g_print ("%s\n", os.str().c_str());
1113 }
1114 }
1116 SPObject *child = o->children;
1117 while (child) {
1118 do_query_all_recurse (child);
1119 child = child->next;
1120 }
1121 }
1124 static void
1125 sp_do_export_png(SPDocument *doc)
1126 {
1127 const gchar *filename = NULL;
1128 gdouble dpi = 0.0;
1130 if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1131 g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1132 }
1134 GSList *items = NULL;
1136 Geom::Rect area;
1137 if (sp_export_id || sp_export_area_drawing) {
1139 SPObject *o = NULL;
1140 SPObject *o_area = NULL;
1141 if (sp_export_id && sp_export_area_drawing) {
1142 o = doc->getObjectById(sp_export_id);
1143 o_area = SP_DOCUMENT_ROOT (doc);
1144 } else if (sp_export_id) {
1145 o = doc->getObjectById(sp_export_id);
1146 o_area = o;
1147 } else if (sp_export_area_drawing) {
1148 o = SP_DOCUMENT_ROOT (doc);
1149 o_area = o;
1150 }
1152 if (o) {
1153 if (!SP_IS_ITEM (o)) {
1154 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1155 return;
1156 }
1158 items = g_slist_prepend (items, SP_ITEM(o));
1160 if (sp_export_id_only) {
1161 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1162 }
1164 if (sp_export_use_hints) {
1166 // retrieve export filename hint
1167 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1168 if (fn_hint) {
1169 if (sp_export_png) {
1170 g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1171 filename = sp_export_png;
1172 } else {
1173 filename = fn_hint;
1174 }
1175 } else {
1176 g_warning ("Export filename hint not found for the object.");
1177 filename = sp_export_png;
1178 }
1180 // retrieve export dpi hints
1181 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1182 if (dpi_hint) {
1183 if (sp_export_dpi || sp_export_width || sp_export_height) {
1184 g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1185 } else {
1186 dpi = atof(dpi_hint);
1187 }
1188 } else {
1189 g_warning ("Export DPI hint not found for the object.");
1190 }
1192 }
1194 // write object bbox to area
1195 sp_document_ensure_up_to_date (doc);
1196 Geom::OptRect areaMaybe;
1197 sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1198 if (areaMaybe) {
1199 area = *areaMaybe;
1200 } else {
1201 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1202 return;
1203 }
1204 } else {
1205 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1206 return;
1207 }
1208 }
1210 if (sp_export_area) {
1211 /* Try to parse area (given in SVG pixels) */
1212 gdouble x0,y0,x1,y1;
1213 if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1214 g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1215 return;
1216 }
1217 area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1218 } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1219 /* Export the whole canvas */
1220 sp_document_ensure_up_to_date (doc);
1221 Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1222 area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1223 }
1225 // set filename and dpi from options, if not yet set from the hints
1226 if (!filename) {
1227 if (!sp_export_png) {
1228 g_warning ("No export filename given and no filename hint. Nothing exported.");
1229 return;
1230 }
1231 filename = sp_export_png;
1232 }
1234 if (sp_export_dpi && dpi == 0.0) {
1235 dpi = atof(sp_export_dpi);
1236 if ((dpi < 0.1) || (dpi > 10000.0)) {
1237 g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1238 return;
1239 }
1240 g_print("DPI: %g\n", dpi);
1241 }
1243 if (sp_export_area_snap) {
1244 round_rectangle_outwards(area);
1245 }
1247 // default dpi
1248 if (dpi == 0.0) {
1249 dpi = PX_PER_IN;
1250 }
1252 unsigned long int width = 0;
1253 unsigned long int height = 0;
1255 if (sp_export_width) {
1256 errno=0;
1257 width = strtoul(sp_export_width, NULL, 0);
1258 if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1259 g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1260 return;
1261 }
1262 dpi = (gdouble) width * PX_PER_IN / area.width();
1263 }
1265 if (sp_export_height) {
1266 errno=0;
1267 height = strtoul(sp_export_height, NULL, 0);
1268 if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1269 g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1270 return;
1271 }
1272 dpi = (gdouble) height * PX_PER_IN / area.height();
1273 }
1275 if (!sp_export_width) {
1276 width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1277 }
1279 if (!sp_export_height) {
1280 height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1281 }
1283 guint32 bgcolor = 0x00000000;
1284 if (sp_export_background) {
1285 // override the page color
1286 bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1287 bgcolor |= 0xff; // default is no opacity
1288 } else {
1289 // read from namedview
1290 Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1291 if (nv && nv->attribute("pagecolor"))
1292 bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1293 if (nv && nv->attribute("inkscape:pageopacity"))
1294 bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1295 }
1297 if (sp_export_background_opacity) {
1298 // override opacity
1299 gfloat value;
1300 if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1301 if (value > 1.0) {
1302 value = CLAMP (value, 1.0f, 255.0f);
1303 bgcolor &= (guint32) 0xffffff00;
1304 bgcolor |= (guint32) floor(value);
1305 } else {
1306 value = CLAMP (value, 0.0f, 1.0f);
1307 bgcolor &= (guint32) 0xffffff00;
1308 bgcolor |= SP_COLOR_F_TO_U(value);
1309 }
1310 }
1311 }
1313 g_print("Background RRGGBBAA: %08x\n", bgcolor);
1315 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);
1317 g_print("Bitmap saved as: %s\n", filename);
1319 if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1320 sp_export_png_file(doc, filename, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1321 } else {
1322 g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1323 }
1325 g_slist_free (items);
1326 }
1329 /**
1330 * Perform a PDF/PS/EPS export
1331 *
1332 * \param doc Document to export.
1333 * \param uri URI to export to.
1334 * \param mime MIME type to export as.
1335 */
1337 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1338 {
1339 Inkscape::Extension::DB::OutputList o;
1340 Inkscape::Extension::db.get_output_list(o);
1341 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1342 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1343 i++;
1344 }
1346 if (i == o.end())
1347 {
1348 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1349 return;
1350 }
1352 if (sp_export_id) {
1353 SPObject *o = doc->getObjectById(sp_export_id);
1354 if (o == NULL) {
1355 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1356 return;
1357 }
1358 (*i)->set_param_string ("exportId", sp_export_id);
1359 } else {
1360 (*i)->set_param_string ("exportId", "");
1361 }
1363 if (sp_export_area_canvas && sp_export_area_drawing) {
1364 g_warning ("You cannot use --export-area-canvas and --export-area-drawing at the same time; only the former will take effect.");
1365 sp_export_area_drawing = false;
1366 }
1368 if (sp_export_area_drawing) {
1369 (*i)->set_param_bool ("areaDrawing", TRUE);
1370 } else {
1371 (*i)->set_param_bool ("areaDrawing", FALSE);
1372 }
1374 if (sp_export_area_canvas) {
1375 if (sp_export_eps) {
1376 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.");
1377 }
1378 (*i)->set_param_bool ("areaCanvas", TRUE);
1379 } else {
1380 (*i)->set_param_bool ("areaCanvas", FALSE);
1381 }
1383 if (!sp_export_area_drawing && !sp_export_area_canvas && !sp_export_id) {
1384 // neither is set, set canvas as default for ps/pdf and drawing for eps
1385 if (sp_export_eps) {
1386 try {
1387 (*i)->set_param_bool("areaDrawing", TRUE);
1388 } catch (...) {}
1389 }
1390 }
1392 if (sp_export_text_to_path) {
1393 (*i)->set_param_bool("textToPath", TRUE);
1394 } else {
1395 (*i)->set_param_bool("textToPath", FALSE);
1396 }
1398 if (sp_export_ignore_filters) {
1399 (*i)->set_param_bool("blurToBitmap", FALSE);
1400 } else {
1401 (*i)->set_param_bool("blurToBitmap", TRUE);
1402 }
1404 (*i)->save(doc, uri);
1405 }
1407 #ifdef WIN32
1408 /**
1409 * Export a document to EMF
1410 *
1411 * \param doc Document to export.
1412 * \param uri URI to export to.
1413 * \param mime MIME type to export as (should be "image/x-emf")
1414 */
1416 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1417 {
1418 Inkscape::Extension::DB::OutputList o;
1419 Inkscape::Extension::db.get_output_list(o);
1420 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1421 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1422 i++;
1423 }
1425 if (i == o.end())
1426 {
1427 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1428 return;
1429 }
1431 (*i)->save(doc, uri);
1432 }
1433 #endif //WIN32
1435 #ifdef WIN32
1436 bool replaceArgs( int& argc, char**& argv )
1437 {
1438 bool worked = false;
1440 #ifdef REPLACEARGS_DEBUG
1441 MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1442 #endif // REPLACEARGS_DEBUG
1444 wchar_t* line = GetCommandLineW();
1445 if ( line )
1446 {
1447 #ifdef REPLACEARGS_DEBUG
1448 {
1449 gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1450 if ( utf8Line )
1451 {
1452 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1453 {
1454 char tmp[strlen(safe) + 32];
1455 snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1456 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1457 }
1458 }
1459 }
1460 #endif // REPLACEARGS_DEBUG
1462 int numArgs = 0;
1463 wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1465 #ifdef REPLACEARGS_ANSI
1466 // test code for trying things on Win95/98/ME
1467 if ( !parsed )
1468 {
1469 #ifdef REPLACEARGS_DEBUG
1470 MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1471 #endif // REPLACEARGS_DEBUG
1472 int lineLen = wcslen(line) + 1;
1473 wchar_t* lineDup = new wchar_t[lineLen];
1474 wcsncpy( lineDup, line, lineLen );
1476 int pos = 0;
1477 bool inQuotes = false;
1478 bool inWhitespace = true;
1479 std::vector<int> places;
1480 while ( lineDup[pos] )
1481 {
1482 if ( inQuotes )
1483 {
1484 if ( lineDup[pos] == L'"' )
1485 {
1486 inQuotes = false;
1487 }
1488 }
1489 else if ( lineDup[pos] == L'"' )
1490 {
1491 inQuotes = true;
1492 inWhitespace = false;
1493 places.push_back(pos);
1494 }
1495 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1496 {
1497 if ( !inWhitespace )
1498 {
1499 inWhitespace = true;
1500 lineDup[pos] = 0;
1501 }
1502 }
1503 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1504 {
1505 inWhitespace = false;
1506 places.push_back(pos);
1507 }
1508 else
1509 {
1510 // consume
1511 }
1512 pos++;
1513 }
1514 #ifdef REPLACEARGS_DEBUG
1515 {
1516 char tmp[256];
1517 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1518 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1519 }
1520 #endif // REPLACEARGS_DEBUG
1522 wchar_t** block = new wchar_t*[places.size()];
1523 int i = 0;
1524 for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1525 {
1526 block[i++] = &lineDup[*it];
1527 }
1528 parsed = block;
1529 numArgs = places.size();
1530 }
1531 #endif // REPLACEARGS_ANSI
1533 if ( parsed )
1534 {
1535 std::vector<wchar_t*>expandedArgs;
1536 if ( numArgs > 0 )
1537 {
1538 expandedArgs.push_back( parsed[0] );
1539 }
1541 for ( int i1 = 1; i1 < numArgs; i1++ )
1542 {
1543 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1544 wildcarded &= parsed[i1][0] != L'"';
1545 wildcarded &= parsed[i1][0] != L'-';
1546 if ( wildcarded )
1547 {
1548 #ifdef REPLACEARGS_ANSI
1549 WIN32_FIND_DATAA data;
1550 #else
1551 WIN32_FIND_DATAW data;
1552 #endif // REPLACEARGS_ANSI
1554 memset((void *)&data, 0, sizeof(data));
1556 int baseLen = wcslen(parsed[i1]) + 2;
1557 wchar_t* base = new wchar_t[baseLen];
1558 wcsncpy( base, parsed[i1], baseLen );
1559 wchar_t* last = wcsrchr( base, L'\\' );
1560 if ( last )
1561 {
1562 last[1] = 0;
1563 }
1564 else
1565 {
1566 base[0] = 0;
1567 }
1568 baseLen = wcslen( base );
1570 #ifdef REPLACEARGS_ANSI
1571 char target[MAX_PATH];
1572 if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1573 {
1574 HANDLE hf = FindFirstFileA( target, &data );
1575 #else
1576 HANDLE hf = FindFirstFileW( parsed[i1], &data );
1577 #endif // REPLACEARGS_ANSI
1578 if ( hf != INVALID_HANDLE_VALUE )
1579 {
1580 BOOL found = TRUE;
1581 do
1582 {
1583 #ifdef REPLACEARGS_ANSI
1584 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1585 if ( howMany > 0 )
1586 {
1587 howMany += baseLen;
1588 wchar_t* tmp = new wchar_t[howMany + 1];
1589 wcsncpy( tmp, base, howMany + 1 );
1590 MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1591 expandedArgs.push_back( tmp );
1592 found = FindNextFileA( hf, &data );
1593 }
1594 #else
1595 int howMany = wcslen(data.cFileName) + baseLen;
1596 wchar_t* tmp = new wchar_t[howMany + 1];
1597 wcsncpy( tmp, base, howMany + 1 );
1598 wcsncat( tmp, data.cFileName, howMany + 1 );
1599 expandedArgs.push_back( tmp );
1600 found = FindNextFileW( hf, &data );
1601 #endif // REPLACEARGS_ANSI
1602 } while ( found );
1604 FindClose( hf );
1605 }
1606 else
1607 {
1608 expandedArgs.push_back( parsed[i1] );
1609 }
1610 #ifdef REPLACEARGS_ANSI
1611 }
1612 #endif // REPLACEARGS_ANSI
1614 delete[] base;
1615 }
1616 else
1617 {
1618 expandedArgs.push_back( parsed[i1] );
1619 }
1620 }
1622 {
1623 wchar_t** block = new wchar_t*[expandedArgs.size()];
1624 int iz = 0;
1625 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1626 {
1627 block[iz++] = *it;
1628 }
1629 parsed = block;
1630 numArgs = expandedArgs.size();
1631 }
1633 std::vector<gchar*> newArgs;
1634 for ( int i = 0; i < numArgs; i++ )
1635 {
1636 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1637 if ( replacement )
1638 {
1639 #ifdef REPLACEARGS_DEBUG
1640 gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1642 if ( safe2 )
1643 {
1644 {
1645 char tmp[1024];
1646 snprintf( tmp, sizeof(tmp), " [%2d] = '%s'", i, safe2 );
1647 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1648 }
1649 g_free( safe2 );
1650 }
1651 #endif // REPLACEARGS_DEBUG
1653 newArgs.push_back( replacement );
1654 }
1655 else
1656 {
1657 newArgs.push_back( blankParam );
1658 }
1659 }
1661 // Now push our munged params to be the new argv and argc
1662 {
1663 char** block = new char*[newArgs.size()];
1664 int iz = 0;
1665 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1666 {
1667 block[iz++] = *it;
1668 }
1669 argv = block;
1670 argc = newArgs.size();
1671 worked = true;
1672 }
1673 }
1674 #ifdef REPLACEARGS_DEBUG
1675 else
1676 {
1677 MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1678 }
1679 #endif // REPLACEARGS_DEBUG
1680 }
1681 #ifdef REPLACEARGS_DEBUG
1682 else
1683 {
1684 {
1685 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1686 }
1688 char* line2 = GetCommandLineA();
1689 if ( line2 )
1690 {
1691 gchar *safe = Inkscape::IO::sanitizeString(line2);
1692 {
1693 {
1694 char tmp[strlen(safe) + 32];
1695 snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1696 MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1697 }
1698 }
1699 }
1700 else
1701 {
1702 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1703 }
1704 }
1705 #endif // REPLACEARGS_DEBUG
1707 return worked;
1708 }
1709 #endif // WIN32
1711 static GSList *
1712 sp_process_args(poptContext ctx)
1713 {
1714 GSList *fl = NULL;
1716 gint a;
1717 while ((a = poptGetNextOpt(ctx)) != -1) {
1718 switch (a) {
1719 case SP_ARG_FILE: {
1720 gchar const *fn = poptGetOptArg(ctx);
1721 if (fn != NULL) {
1722 fl = g_slist_append(fl, g_strdup(fn));
1723 }
1724 break;
1725 }
1726 case SP_ARG_VERSION: {
1727 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1728 exit(0);
1729 break;
1730 }
1731 case SP_ARG_EXTENSIONDIR: {
1732 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1733 exit(0);
1734 break;
1735 }
1736 case SP_ARG_VERB_LIST: {
1737 // This really shouldn't go here, we should init the app.
1738 // But, since we're just exiting in this path, there is
1739 // no harm, and this is really a better place to put
1740 // everything else.
1741 Inkscape::Extension::init();
1742 Inkscape::Verb::list();
1743 exit(0);
1744 break;
1745 }
1746 case SP_ARG_VERB:
1747 case SP_ARG_SELECT: {
1748 gchar const *arg = poptGetOptArg(ctx);
1749 if (arg != NULL) {
1750 // printf("Adding in: %s\n", arg);
1751 new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1752 }
1753 break;
1754 }
1755 case POPT_ERROR_BADOPT: {
1756 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1757 exit(1);
1758 break;
1759 }
1760 default: {
1761 break;
1762 }
1763 }
1764 }
1766 gchar const ** const args = poptGetArgs(ctx);
1767 if (args != NULL) {
1768 for (unsigned i = 0; args[i] != NULL; i++) {
1769 fl = g_slist_append(fl, g_strdup(args[i]));
1770 }
1771 }
1773 return fl;
1774 }
1777 /*
1778 Local Variables:
1779 mode:c++
1780 c-file-style:"stroustrup"
1781 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1782 indent-tabs-mode:nil
1783 fill-column:99
1784 End:
1785 */
1786 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :