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"
86 #include <extension/extension.h>
87 #include <extension/system.h>
88 #include <extension/db.h>
89 #include <extension/output.h>
91 #ifdef WIN32
92 //#define REPLACEARGS_ANSI
93 //#define REPLACEARGS_DEBUG
95 #include "registrytool.h"
97 #include "extension/internal/win32.h"
98 using Inkscape::Extension::Internal::PrintWin32;
100 #endif // WIN32
102 #include "extension/init.h"
104 #include <glibmm/i18n.h>
105 #include <gtkmm/main.h>
107 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
108 #define bind_textdomain_codeset(p,c)
109 #endif
111 #include "application/application.h"
113 #include "main-cmdlineact.h"
115 #include <png.h>
116 #include <errno.h>
118 enum {
119 SP_ARG_NONE,
120 SP_ARG_NOGUI,
121 SP_ARG_GUI,
122 SP_ARG_FILE,
123 SP_ARG_PRINT,
124 SP_ARG_EXPORT_PNG,
125 SP_ARG_EXPORT_DPI,
126 SP_ARG_EXPORT_AREA,
127 SP_ARG_EXPORT_AREA_DRAWING,
128 SP_ARG_EXPORT_AREA_CANVAS,
129 SP_ARG_EXPORT_AREA_SNAP,
130 SP_ARG_EXPORT_WIDTH,
131 SP_ARG_EXPORT_HEIGHT,
132 SP_ARG_EXPORT_ID,
133 SP_ARG_EXPORT_ID_ONLY,
134 SP_ARG_EXPORT_USE_HINTS,
135 SP_ARG_EXPORT_BACKGROUND,
136 SP_ARG_EXPORT_BACKGROUND_OPACITY,
137 SP_ARG_EXPORT_SVG,
138 SP_ARG_EXPORT_PS,
139 SP_ARG_EXPORT_EPS,
140 SP_ARG_EXPORT_PDF,
141 #ifdef WIN32
142 SP_ARG_EXPORT_EMF,
143 #endif //WIN32
144 SP_ARG_EXPORT_TEXT_TO_PATH,
145 SP_ARG_EXTENSIONDIR,
146 SP_ARG_QUERY_X,
147 SP_ARG_QUERY_Y,
148 SP_ARG_QUERY_WIDTH,
149 SP_ARG_QUERY_HEIGHT,
150 SP_ARG_QUERY_ALL,
151 SP_ARG_QUERY_ID,
152 SP_ARG_SHELL,
153 SP_ARG_VERSION,
154 SP_ARG_VACUUM_DEFS,
155 SP_ARG_VERB_LIST,
156 SP_ARG_VERB,
157 SP_ARG_SELECT,
158 SP_ARG_LAST
159 };
161 int sp_main_gui(int argc, char const **argv);
162 int sp_main_console(int argc, char const **argv);
163 static void sp_do_export_png(SPDocument *doc);
164 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime);
165 #ifdef WIN32
166 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
167 #endif //WIN32
168 static void do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id);
169 static void do_query_all (SPDocument *doc);
170 static void do_query_all_recurse (SPObject *o);
172 static gchar *sp_global_printer = NULL;
173 static gchar *sp_export_png = NULL;
174 static gchar *sp_export_dpi = NULL;
175 static gchar *sp_export_area = NULL;
176 static gboolean sp_export_area_drawing = FALSE;
177 static gboolean sp_export_area_canvas = FALSE;
178 static gchar *sp_export_width = NULL;
179 static gchar *sp_export_height = NULL;
180 static gchar *sp_export_id = NULL;
181 static gchar *sp_export_background = NULL;
182 static gchar *sp_export_background_opacity = NULL;
183 static gboolean sp_export_area_snap = FALSE;
184 static gboolean sp_export_use_hints = FALSE;
185 static gboolean sp_export_id_only = FALSE;
186 static gchar *sp_export_svg = NULL;
187 static gchar *sp_export_ps = NULL;
188 static gchar *sp_export_eps = NULL;
189 static gchar *sp_export_pdf = NULL;
190 #ifdef WIN32
191 static gchar *sp_export_emf = NULL;
192 #endif //WIN32
193 static gboolean sp_export_text_to_path = FALSE;
194 static gboolean sp_export_font = FALSE;
195 static gboolean sp_query_x = FALSE;
196 static gboolean sp_query_y = FALSE;
197 static gboolean sp_query_width = FALSE;
198 static gboolean sp_query_height = FALSE;
199 static gboolean sp_query_all = FALSE;
200 static gchar *sp_query_id = NULL;
201 static int sp_new_gui = FALSE;
202 static gboolean sp_shell = FALSE;
203 static gboolean sp_vacuum_defs = FALSE;
205 static gchar *sp_export_png_utf8 = NULL;
206 static gchar *sp_export_svg_utf8 = NULL;
207 static gchar *sp_global_printer_utf8 = NULL;
210 /**
211 * Reset variables to default values.
212 */
213 static void resetCommandlineGlobals() {
214 sp_global_printer = NULL;
215 sp_export_png = NULL;
216 sp_export_dpi = NULL;
217 sp_export_area = NULL;
218 sp_export_area_drawing = FALSE;
219 sp_export_area_canvas = FALSE;
220 sp_export_width = NULL;
221 sp_export_height = NULL;
222 sp_export_id = NULL;
223 sp_export_background = NULL;
224 sp_export_background_opacity = NULL;
225 sp_export_area_snap = FALSE;
226 sp_export_use_hints = FALSE;
227 sp_export_id_only = FALSE;
228 sp_export_svg = NULL;
229 sp_export_ps = NULL;
230 sp_export_eps = NULL;
231 sp_export_pdf = NULL;
232 #ifdef WIN32
233 sp_export_emf = NULL;
234 #endif //WIN32
235 sp_export_text_to_path = FALSE;
236 sp_export_font = FALSE;
237 sp_query_x = FALSE;
238 sp_query_y = FALSE;
239 sp_query_width = FALSE;
240 sp_query_height = FALSE;
241 sp_query_all = FALSE;
242 sp_query_id = NULL;
243 sp_vacuum_defs = FALSE;
245 sp_export_png_utf8 = NULL;
246 sp_export_svg_utf8 = NULL;
247 sp_global_printer_utf8 = NULL;
248 }
250 #ifdef WIN32
251 static bool replaceArgs( int& argc, char**& argv );
252 #endif
253 static GSList *sp_process_args(poptContext ctx);
254 struct poptOption options[] = {
255 {"version", 'V',
256 POPT_ARG_NONE, NULL, SP_ARG_VERSION,
257 N_("Print the Inkscape version number"),
258 NULL},
260 {"without-gui", 'z',
261 POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
262 N_("Do not use X server (only process files from console)"),
263 NULL},
265 {"with-gui", 'g',
266 POPT_ARG_NONE, NULL, SP_ARG_GUI,
267 N_("Try to use X server (even if $DISPLAY is not set)"),
268 NULL},
270 {"file", 'f',
271 POPT_ARG_STRING, NULL, SP_ARG_FILE,
272 N_("Open specified document(s) (option string may be excluded)"),
273 N_("FILENAME")},
275 {"print", 'p',
276 POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
277 N_("Print document(s) to specified output file (use '| program' for pipe)"),
278 N_("FILENAME")},
280 {"export-png", 'e',
281 POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
282 N_("Export document to a PNG file"),
283 N_("FILENAME")},
285 {"export-dpi", 'd',
286 POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
287 N_("The resolution used for exporting SVG into bitmap (default 90)"),
288 N_("DPI")},
290 {"export-area", 'a',
291 POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
292 N_("Exported area in SVG user units (default is the canvas; 0,0 is lower-left corner)"),
293 N_("x0:y0:x1:y1")},
295 {"export-area-drawing", 'D',
296 POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
297 N_("Exported area is the entire drawing (not canvas)"),
298 NULL},
300 {"export-area-canvas", 'C',
301 POPT_ARG_NONE, &sp_export_area_canvas, SP_ARG_EXPORT_AREA_CANVAS,
302 N_("Exported area is the entire canvas"),
303 NULL},
305 {"export-area-snap", 0,
306 POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
307 N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
308 NULL},
310 {"export-width", 'w',
311 POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
312 N_("The width of exported bitmap in pixels (overrides export-dpi)"),
313 N_("WIDTH")},
315 {"export-height", 'h',
316 POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
317 N_("The height of exported bitmap in pixels (overrides export-dpi)"),
318 N_("HEIGHT")},
320 {"export-id", 'i',
321 POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
322 N_("The ID of the object to export"),
323 N_("ID")},
325 {"export-id-only", 'j',
326 POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
327 // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
328 // See "man inkscape" for details.
329 N_("Export just the object with export-id, hide all others (only with export-id)"),
330 NULL},
332 {"export-use-hints", 't',
333 POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
334 N_("Use stored filename and DPI hints when exporting (only with export-id)"),
335 NULL},
337 {"export-background", 'b',
338 POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
339 N_("Background color of exported bitmap (any SVG-supported color string)"),
340 N_("COLOR")},
342 {"export-background-opacity", 'y',
343 POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
344 N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
345 N_("VALUE")},
347 {"export-plain-svg", 'l',
348 POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
349 N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
350 N_("FILENAME")},
352 {"export-ps", 'P',
353 POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
354 N_("Export document to a PS file"),
355 N_("FILENAME")},
357 {"export-eps", 'E',
358 POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
359 N_("Export document to an EPS file"),
360 N_("FILENAME")},
362 {"export-pdf", 'A',
363 POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
364 N_("Export document to a PDF file"),
365 N_("FILENAME")},
367 #ifdef WIN32
368 {"export-emf", 'M',
369 POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
370 N_("Export document to an Enhanced Metafile (EMF) File"),
371 N_("FILENAME")},
372 #endif //WIN32
374 {"export-text-to-path", 'T',
375 POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
376 N_("Convert text object to paths on export (EPS)"),
377 NULL},
379 {"query-x", 'X',
380 POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
381 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
382 N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
383 NULL},
385 {"query-y", 'Y',
386 POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
387 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
388 N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
389 NULL},
391 {"query-width", 'W',
392 POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
393 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
394 N_("Query the width of the drawing or, if specified, of the object with --query-id"),
395 NULL},
397 {"query-height", 'H',
398 POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
399 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
400 N_("Query the height of the drawing or, if specified, of the object with --query-id"),
401 NULL},
403 {"query-all", 'S',
404 POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
405 N_("List id,x,y,w,h for all objects"),
406 NULL},
408 {"query-id", 'I',
409 POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
410 N_("The ID of the object whose dimensions are queried"),
411 N_("ID")},
413 {"extension-directory", 'x',
414 POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
415 // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
416 N_("Print out the extension directory and exit"),
417 NULL},
419 {"vacuum-defs", 0,
420 POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
421 N_("Remove unused definitions from the defs section(s) of the document"),
422 NULL},
424 {"verb-list", 0,
425 POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
426 N_("List the IDs of all the verbs in Inkscape"),
427 NULL},
429 {"verb", 0,
430 POPT_ARG_STRING, NULL, SP_ARG_VERB,
431 N_("Verb to call when Inkscape opens."),
432 N_("VERB-ID")},
434 {"select", 0,
435 POPT_ARG_STRING, NULL, SP_ARG_SELECT,
436 N_("Object ID to select when Inkscape opens."),
437 N_("OBJECT-ID")},
439 {"shell", 0,
440 POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
441 N_("Start Inkscape in interative shell mode."),
442 NULL},
444 POPT_AUTOHELP POPT_TABLEEND
445 };
447 static bool needToRecodeParams = true;
448 gchar * blankParam = g_strdup("");
452 #ifdef WIN32
454 /**
455 * Return the directory of the .exe that is currently running
456 */
457 static Glib::ustring _win32_getExePath()
458 {
459 char exeName[MAX_PATH+1];
460 GetModuleFileName(NULL, exeName, MAX_PATH);
461 char *slashPos = strrchr(exeName, '\\');
462 if (slashPos)
463 *slashPos = '\0';
464 Glib::ustring s = exeName;
465 return s;
466 }
468 /**
469 * Set up the PATH and PYTHONPATH environment variables on
470 * win32
471 */
472 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
473 {
475 char *oldenv = getenv("PATH");
476 Glib::ustring tmp = "PATH=";
477 tmp += exePath;
478 tmp += ";";
479 tmp += exePath;
480 tmp += "\\python;";
481 tmp += exePath;
482 tmp += "\\python\\Scripts;"; // for uniconv.cmd
483 tmp += exePath;
484 tmp += "\\perl";
485 if(oldenv != NULL) {
486 tmp += ";";
487 tmp += oldenv;
488 }
489 _putenv(tmp.c_str());
491 oldenv = getenv("PYTHONPATH");
492 tmp = "PYTHONPATH=";
493 tmp += exePath;
494 tmp += "\\python;";
495 tmp += exePath;
496 tmp += "\\python\\Lib;";
497 tmp += exePath;
498 tmp += "\\python\\DLLs";
499 if(oldenv != NULL) {
500 tmp += ";";
501 tmp += oldenv;
502 }
503 _putenv(tmp.c_str());
505 return 0;
506 }
507 #endif
509 /**
510 * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
511 * can find inkex.py et al. (Bug #197475)
512 */
513 static int set_extensions_env()
514 {
515 char *oldenv = getenv("PYTHONPATH");
516 Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
517 if (oldenv != NULL) {
518 #ifdef WIN32
519 tmp += ";";
520 #else
521 tmp += ":";
522 #endif
523 tmp += oldenv;
524 }
525 #ifdef WIN32
526 /// \todo this does not work on windows, cannot find the setenv method or an equivalent.
527 //setenv("PYTHONPATH", tmp.c_str(), 1);
528 #else
529 setenv("PYTHONPATH", tmp.c_str(), 1);
530 #endif
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 // Bug #197475
566 set_extensions_env();
568 /**
569 * Call bindtextdomain() for various machines's paths
570 */
571 #ifdef ENABLE_NLS
572 #ifdef WIN32
573 Glib::ustring localePath = homedir;
574 localePath += "\\";
575 localePath += PACKAGE_LOCALE_DIR;
576 bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
577 #else
578 #ifdef ENABLE_BINRELOC
579 bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
580 #else
581 bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
582 #endif
583 #endif
584 // Allow the user to override the locale directory by setting
585 // the environment variable INKSCAPE_LOCALEDIR.
586 char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
587 if (inkscape_localedir != NULL) {
588 bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
589 }
590 #endif
592 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
594 #ifdef ENABLE_NLS
595 textdomain(GETTEXT_PACKAGE);
596 #endif
598 LIBXML_TEST_VERSION
600 Inkscape::GC::init();
602 Inkscape::Debug::Logger::init();
604 gboolean use_gui;
606 #ifndef WIN32
607 use_gui = (getenv("DISPLAY") != NULL);
608 #else
609 use_gui = TRUE;
610 #endif
611 /* Test whether with/without GUI is forced */
612 for (int i = 1; i < argc; i++) {
613 if (!strcmp(argv[i], "-z")
614 || !strcmp(argv[i], "--without-gui")
615 || !strcmp(argv[i], "-p")
616 || !strncmp(argv[i], "--print", 7)
617 || !strcmp(argv[i], "-e")
618 || !strncmp(argv[i], "--export-png", 12)
619 || !strcmp(argv[i], "-l")
620 || !strncmp(argv[i], "--export-plain-svg", 12)
621 || !strcmp(argv[i], "-i")
622 || !strncmp(argv[i], "--export-area-drawing", 21)
623 || !strcmp(argv[i], "-D")
624 || !strncmp(argv[i], "--export-area-canvas", 20)
625 || !strcmp(argv[i], "-C")
626 || !strncmp(argv[i], "--export-id", 12)
627 || !strcmp(argv[i], "-P")
628 || !strncmp(argv[i], "--export-ps", 11)
629 || !strcmp(argv[i], "-E")
630 || !strncmp(argv[i], "--export-eps", 12)
631 || !strcmp(argv[i], "-A")
632 || !strncmp(argv[i], "--export-pdf", 12)
633 #ifdef WIN32
634 || !strcmp(argv[i], "-M")
635 || !strncmp(argv[i], "--export-emf", 12)
636 #endif //WIN32
637 || !strcmp(argv[i], "-W")
638 || !strncmp(argv[i], "--query-width", 13)
639 || !strcmp(argv[i], "-H")
640 || !strncmp(argv[i], "--query-height", 14)
641 || !strcmp(argv[i], "-S")
642 || !strncmp(argv[i], "--query-all", 11)
643 || !strcmp(argv[i], "-X")
644 || !strncmp(argv[i], "--query-x", 13)
645 || !strcmp(argv[i], "-Y")
646 || !strncmp(argv[i], "--query-y", 14)
647 || !strcmp(argv[i], "--vacuum-defs")
648 || !strncmp(argv[i], "--shell", 7)
649 )
650 {
651 /* main_console handles any exports -- not the gui */
652 use_gui = FALSE;
653 break;
654 } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
655 use_gui = TRUE;
656 break;
657 }
658 }
660 #ifdef WIN32
661 #ifndef REPLACEARGS_ANSI
662 if ( PrintWin32::is_os_wide() )
663 #endif // REPLACEARGS_ANSI
664 {
665 // If the call fails, we'll need to convert charsets
666 needToRecodeParams = !replaceArgs( argc, argv );
667 }
668 #endif // WIN32
670 /// \todo Should this be a static object (see inkscape.cpp)?
671 Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
673 return app.run();
674 }
679 void fixupSingleFilename( gchar **orig, gchar **spare )
680 {
681 if ( orig && *orig && **orig ) {
682 GError *error = NULL;
683 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
684 if ( newFileName )
685 {
686 *orig = newFileName;
687 if ( spare ) {
688 *spare = newFileName;
689 }
690 // g_message("Set a replacement fixup");
691 }
692 }
693 }
697 GSList *fixupFilenameEncoding( GSList* fl )
698 {
699 GSList *newFl = NULL;
700 while ( fl ) {
701 gchar *fn = static_cast<gchar*>(fl->data);
702 fl = g_slist_remove( fl, fl->data );
703 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
704 if ( newFileName ) {
706 if ( 0 )
707 {
708 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
709 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
710 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
711 "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
712 gtk_dialog_run (GTK_DIALOG (w));
713 gtk_widget_destroy (w);
714 g_free(safeNewFn);
715 g_free(safeFn);
716 }
718 g_free( fn );
719 fn = newFileName;
720 newFileName = 0;
721 }
722 else
723 if ( 0 )
724 {
725 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
726 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
727 gtk_dialog_run (GTK_DIALOG (w));
728 gtk_widget_destroy (w);
729 g_free(safeFn);
730 }
731 newFl = g_slist_append( newFl, fn );
732 }
733 return newFl;
734 }
736 int sp_common_main( int argc, char const **argv, GSList **flDest )
737 {
738 /// \todo fixme: Move these to some centralized location (Lauris)
739 sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
740 sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
743 // temporarily switch gettext encoding to locale, so that help messages can be output properly
744 gchar const *charset;
745 g_get_charset(&charset);
747 bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
749 poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
750 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
751 g_return_val_if_fail(ctx != NULL, 1);
753 /* Collect own arguments */
754 GSList *fl = sp_process_args(ctx);
755 poptFreeContext(ctx);
757 // now switch gettext back to UTF-8 (for GUI)
758 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
760 // Now let's see if the file list still holds up
761 if ( needToRecodeParams )
762 {
763 fl = fixupFilenameEncoding( fl );
764 }
766 // Check the globals for filename-fixup
767 if ( needToRecodeParams )
768 {
769 fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
770 fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
771 fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
772 }
773 else
774 {
775 if ( sp_export_png )
776 sp_export_png_utf8 = g_strdup( sp_export_png );
777 if ( sp_export_svg )
778 sp_export_svg_utf8 = g_strdup( sp_export_svg );
779 if ( sp_global_printer )
780 sp_global_printer_utf8 = g_strdup( sp_global_printer );
781 }
783 // Return the list if wanted, else free it up.
784 if ( flDest ) {
785 *flDest = fl;
786 fl = 0;
787 } else {
788 while ( fl ) {
789 g_free( fl->data );
790 fl = g_slist_remove( fl, fl->data );
791 }
792 }
793 return 0;
794 }
796 static void
797 snooper(GdkEvent *event, gpointer /*data*/) {
798 if(inkscape_mapalt()) /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
799 {
800 GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
801 switch (event->type) {
802 case GDK_MOTION_NOTIFY:
803 if(event->motion.state & mapping) {
804 event->motion.state|=GDK_MOD1_MASK;
805 }
806 break;
807 case GDK_BUTTON_PRESS:
808 if(event->button.state & mapping) {
809 event->button.state|=GDK_MOD1_MASK;
810 }
811 break;
812 case GDK_KEY_PRESS:
813 if(event->key.state & mapping) {
814 event->key.state|=GDK_MOD1_MASK;
815 }
816 break;
817 default:
818 break;
819 }
820 }
821 gtk_main_do_event (event);
822 }
824 int
825 sp_main_gui(int argc, char const **argv)
826 {
827 Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
829 GSList *fl = NULL;
830 int retVal = sp_common_main( argc, argv, &fl );
831 g_return_val_if_fail(retVal == 0, 1);
833 inkscape_gtk_stock_init();
835 gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
837 Inkscape::Debug::log_display_config();
839 /* Set default icon */
840 gchar *filename = (gchar *) g_build_filename (INKSCAPE_APPICONDIR, "inkscape.png", NULL);
841 if (Inkscape::IO::file_test(filename, (GFileTest)(G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))) {
842 gtk_window_set_default_icon_from_file(filename, NULL);
843 }
844 g_free (filename);
845 filename = 0;
847 gboolean create_new = TRUE;
849 /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
850 inkscape_application_init(argv[0], true);
852 while (fl) {
853 if (sp_file_open((gchar *)fl->data,NULL)) {
854 create_new=FALSE;
855 }
856 fl = g_slist_remove(fl, fl->data);
857 }
858 if (create_new) {
859 sp_file_new_default();
860 }
862 Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
863 main_instance.run();
865 #ifdef WIN32
866 //We might not need anything here
867 //sp_win32_finish(); <-- this is a NOP func
868 #endif
870 return 0;
871 }
873 /**
874 * Process file list
875 */
876 void sp_process_file_list(GSList *fl)
877 {
878 while (fl) {
879 const gchar *filename = (gchar *)fl->data;
880 SPDocument *doc = Inkscape::Extension::open(NULL, filename);
881 if (doc == NULL) {
882 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
883 }
884 if (doc == NULL) {
885 g_warning("Specified document %s cannot be opened (is it a valid SVG file?)", filename);
886 } else {
887 if (sp_vacuum_defs) {
888 vacuum_document(doc);
889 }
890 if (sp_vacuum_defs && !sp_export_svg) {
891 // save under the name given in the command line
892 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
893 }
894 if (sp_global_printer) {
895 sp_print_document_to_file(doc, sp_global_printer);
896 }
897 if (sp_export_png) {
898 sp_do_export_png(doc);
899 }
900 if (sp_export_svg) {
901 Inkscape::XML::Document *rdoc;
902 Inkscape::XML::Node *repr;
903 rdoc = sp_repr_document_new("svg:svg");
904 repr = rdoc->root();
905 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
906 sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
907 }
908 if (sp_export_ps) {
909 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
910 }
911 if (sp_export_eps) {
912 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
913 }
914 if (sp_export_pdf) {
915 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
916 }
917 #ifdef WIN32
918 if (sp_export_emf) {
919 do_export_emf(doc, sp_export_emf, "image/x-emf");
920 }
921 #endif //WIN32
922 if (sp_query_all) {
923 do_query_all (doc);
924 } else if (sp_query_width || sp_query_height) {
925 do_query_dimension (doc, true, sp_query_width? NR::X : NR::Y, sp_query_id);
926 } else if (sp_query_x || sp_query_y) {
927 do_query_dimension (doc, false, sp_query_x? NR::X : NR::Y, sp_query_id);
928 }
930 delete doc;
931 }
932 fl = g_slist_remove(fl, fl->data);
933 }
934 }
936 /**
937 * Run the application as an interactive shell, parsing command lines from stdin
938 * Returns -1 on error.
939 */
940 int sp_main_shell(char const* command_name)
941 {
942 int retval = 0;
944 const unsigned int buffer_size = 4096;
945 gchar *command_line = g_strnfill(buffer_size, 0);
946 g_strlcpy(command_line, command_name, buffer_size);
947 gsize offset = g_strlcat(command_line, " ", buffer_size);
948 gsize sizeLeft = buffer_size - offset;
949 gchar *useme = command_line + offset;
951 fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", INKSCAPE_VERSION);
952 fflush(stdout);
953 char* linedata = 0;
954 do {
955 fprintf(stdout, ">");
956 fflush(stdout);
957 if ((linedata = fgets(useme, sizeLeft, stdin))) {
958 size_t len = strlen(useme);
959 if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
960 fprintf(stdout, "ERROR: Command line too long\n");
961 // Consume rest of line
962 retval = -1; // If the while loop completes, this remains -1
963 while (fgets(useme, sizeLeft, stdin) && retval) {
964 len = strlen(command_line);
965 if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
966 retval = 0;
967 }
968 }
969 } else {
970 useme[--len] = '\0'; // Strip newline
971 if (useme[len - 1] == '\r') {
972 useme[--len] = '\0';
973 }
974 if ( strcmp(useme, "quit") == 0 ) {
975 // Time to quit
976 fflush(stdout);
977 linedata = 0; // mark for exit
978 } else if ( len < 1 ) {
979 // blank string. Do nothing.
980 } else {
981 GError* parseError = 0;
982 gchar** argv = 0;
983 gint argc = 0;
984 if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
985 poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
986 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
987 if ( ctx ) {
988 GSList *fl = sp_process_args(ctx);
989 sp_process_file_list(fl);
990 poptFreeContext(ctx);
991 } else {
992 retval = 1; // not sure why. But this was the previous return value
993 }
994 resetCommandlineGlobals();
995 g_strfreev(argv);
996 } else {
997 g_warning("Cannot parse commandline: %s", useme);
998 }
999 }
1000 }
1001 } // if (linedata...
1002 } while (linedata && (retval == 0));
1004 g_free(command_line);
1005 return retval;
1006 }
1008 int sp_main_console(int argc, char const **argv)
1009 {
1010 /* We are started in text mode */
1012 /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1013 * in a non-Gtk environment. Used in libnrtype's
1014 * FontInstance.cpp and FontFactory.cpp.
1015 * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1016 */
1017 g_type_init();
1018 char **argv2 = const_cast<char **>(argv);
1019 gtk_init_check( &argc, &argv2 );
1020 //setlocale(LC_ALL, "");
1022 GSList *fl = NULL;
1023 int retVal = sp_common_main( argc, argv, &fl );
1024 g_return_val_if_fail(retVal == 0, 1);
1026 if (fl == NULL && !sp_shell) {
1027 g_print("Nothing to do!\n");
1028 exit(0);
1029 }
1031 inkscape_application_init(argv[0], false);
1033 if (sp_shell) {
1034 sp_main_shell(argv[0]); // Run as interactive shell
1035 exit(0);
1036 } else {
1037 sp_process_file_list(fl); // Normal command line invokation
1038 }
1040 return 0;
1041 }
1043 static void
1044 do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id)
1045 {
1046 SPObject *o = NULL;
1048 if (id) {
1049 o = doc->getObjectById(id);
1050 if (o) {
1051 if (!SP_IS_ITEM (o)) {
1052 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1053 return;
1054 }
1055 } else {
1056 g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1057 return;
1058 }
1059 } else {
1060 o = SP_DOCUMENT_ROOT(doc);
1061 }
1063 if (o) {
1064 sp_document_ensure_up_to_date (doc);
1065 SPItem *item = ((SPItem *) o);
1067 // "true" SVG bbox for scripting
1068 boost::optional<Geom::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
1069 if (area) {
1070 Inkscape::SVGOStringStream os;
1071 if (extent) {
1072 os << area->dimensions()[axis];
1073 } else {
1074 os << area->min()[axis];
1075 }
1076 g_print ("%s", os.str().c_str());
1077 } else {
1078 g_print("0");
1079 }
1080 }
1081 }
1083 static void
1084 do_query_all (SPDocument *doc)
1085 {
1086 SPObject *o = NULL;
1088 o = SP_DOCUMENT_ROOT(doc);
1090 if (o) {
1091 sp_document_ensure_up_to_date (doc);
1092 do_query_all_recurse(o);
1093 }
1094 }
1096 static void
1097 do_query_all_recurse (SPObject *o)
1098 {
1099 SPItem *item = ((SPItem *) o);
1100 if (o->id && SP_IS_ITEM(item)) {
1101 boost::optional<Geom::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
1102 if (area) {
1103 Inkscape::SVGOStringStream os;
1104 os << o->id;
1105 os << "," << area->min()[Geom::X];
1106 os << "," << area->min()[Geom::Y];
1107 os << "," << area->dimensions()[Geom::X];
1108 os << "," << area->dimensions()[Geom::Y];
1109 g_print ("%s\n", os.str().c_str());
1110 }
1111 }
1113 SPObject *child = o->children;
1114 while (child) {
1115 do_query_all_recurse (child);
1116 child = child->next;
1117 }
1118 }
1121 static void
1122 sp_do_export_png(SPDocument *doc)
1123 {
1124 const gchar *filename = NULL;
1125 gdouble dpi = 0.0;
1127 if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1128 g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1129 }
1131 GSList *items = NULL;
1133 NRRect area;
1134 if (sp_export_id || sp_export_area_drawing) {
1136 SPObject *o = NULL;
1137 SPObject *o_area = NULL;
1138 if (sp_export_id && sp_export_area_drawing) {
1139 o = doc->getObjectById(sp_export_id);
1140 o_area = SP_DOCUMENT_ROOT (doc);
1141 } else if (sp_export_id) {
1142 o = doc->getObjectById(sp_export_id);
1143 o_area = o;
1144 } else if (sp_export_area_drawing) {
1145 o = SP_DOCUMENT_ROOT (doc);
1146 o_area = o;
1147 }
1149 if (o) {
1150 if (!SP_IS_ITEM (o)) {
1151 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1152 return;
1153 }
1155 items = g_slist_prepend (items, SP_ITEM(o));
1157 if (sp_export_id_only) {
1158 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1159 }
1161 if (sp_export_use_hints) {
1163 // retrieve export filename hint
1164 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1165 if (fn_hint) {
1166 if (sp_export_png) {
1167 g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1168 filename = sp_export_png;
1169 } else {
1170 filename = fn_hint;
1171 }
1172 } else {
1173 g_warning ("Export filename hint not found for the object.");
1174 filename = sp_export_png;
1175 }
1177 // retrieve export dpi hints
1178 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1179 if (dpi_hint) {
1180 if (sp_export_dpi || sp_export_width || sp_export_height) {
1181 g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1182 } else {
1183 dpi = atof(dpi_hint);
1184 }
1185 } else {
1186 g_warning ("Export DPI hint not found for the object.");
1187 }
1189 }
1191 // write object bbox to area
1192 sp_document_ensure_up_to_date (doc);
1193 boost::optional<Geom::Rect> areaMaybe;
1194 sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2r_affine((SPItem *) o_area), TRUE);
1195 if (areaMaybe) {
1196 area = NRRect(areaMaybe);
1197 } else {
1198 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1199 return;
1200 }
1201 } else {
1202 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1203 return;
1204 }
1205 }
1207 if (sp_export_area) {
1208 /* Try to parse area (given in SVG pixels) */
1209 if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &area.x0, &area.y0, &area.x1, &area.y1) == 4) {
1210 g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1211 return;
1212 }
1213 if ((area.x0 >= area.x1) || (area.y0 >= area.y1)) {
1214 g_warning("Export area '%s' has negative width or height. Nothing exported.", sp_export_area);
1215 return;
1216 }
1217 } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1218 /* Export the whole canvas */
1219 sp_document_ensure_up_to_date (doc);
1220 area.x0 = SP_ROOT(doc->root)->x.computed;
1221 area.y0 = SP_ROOT(doc->root)->y.computed;
1222 area.x1 = area.x0 + sp_document_width (doc);
1223 area.y1 = area.y0 + sp_document_height (doc);
1224 }
1226 // set filename and dpi from options, if not yet set from the hints
1227 if (!filename) {
1228 if (!sp_export_png) {
1229 g_warning ("No export filename given and no filename hint. Nothing exported.");
1230 return;
1231 }
1232 filename = sp_export_png;
1233 }
1235 if (sp_export_dpi && dpi == 0.0) {
1236 dpi = atof(sp_export_dpi);
1237 if ((dpi < 0.1) || (dpi > 10000.0)) {
1238 g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1239 return;
1240 }
1241 g_print("DPI: %g\n", dpi);
1242 }
1244 if (sp_export_area_snap) {
1245 area.x0 = std::floor (area.x0);
1246 area.y0 = std::floor (area.y0);
1247 area.x1 = std::ceil (area.x1);
1248 area.y1 = std::ceil (area.y1);
1249 }
1251 // default dpi
1252 if (dpi == 0.0) {
1253 dpi = PX_PER_IN;
1254 }
1256 unsigned long int width = 0;
1257 unsigned long int height = 0;
1259 if (sp_export_width) {
1260 errno=0;
1261 width = strtoul(sp_export_width, NULL, 0);
1262 if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1263 g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1264 return;
1265 }
1266 dpi = (gdouble) width * PX_PER_IN / (area.x1 - area.x0);
1267 }
1269 if (sp_export_height) {
1270 errno=0;
1271 height = strtoul(sp_export_height, NULL, 0);
1272 if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1273 g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1274 return;
1275 }
1276 dpi = (gdouble) height * PX_PER_IN / (area.y1 - area.y0);
1277 }
1279 if (!sp_export_width) {
1280 width = (unsigned long int) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1281 }
1283 if (!sp_export_height) {
1284 height = (unsigned long int) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1285 }
1287 guint32 bgcolor = 0x00000000;
1288 if (sp_export_background) {
1289 // override the page color
1290 bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1291 bgcolor |= 0xff; // default is no opacity
1292 } else {
1293 // read from namedview
1294 Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1295 if (nv && nv->attribute("pagecolor"))
1296 bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1297 if (nv && nv->attribute("inkscape:pageopacity"))
1298 bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1299 }
1301 if (sp_export_background_opacity) {
1302 // override opacity
1303 gfloat value;
1304 if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1305 if (value > 1.0) {
1306 value = CLAMP (value, 1.0f, 255.0f);
1307 bgcolor &= (guint32) 0xffffff00;
1308 bgcolor |= (guint32) floor(value);
1309 } else {
1310 value = CLAMP (value, 0.0f, 1.0f);
1311 bgcolor &= (guint32) 0xffffff00;
1312 bgcolor |= SP_COLOR_F_TO_U(value);
1313 }
1314 }
1315 }
1317 g_print("Background RRGGBBAA: %08x\n", bgcolor);
1319 g_print("Area %g:%g:%g:%g exported to %lu x %lu pixels (%g dpi)\n", area.x0, area.y0, area.x1, area.y1, width, height, dpi);
1321 g_print("Bitmap saved as: %s\n", filename);
1323 if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1324 sp_export_png_file(doc, filename, area.x0, area.y0, area.x1, area.y1, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1325 } else {
1326 g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1327 }
1329 g_slist_free (items);
1330 }
1333 /**
1334 * Perform a PDF/PS/EPS export
1335 *
1336 * \param doc Document to export.
1337 * \param uri URI to export to.
1338 * \param mime MIME type to export as.
1339 */
1341 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1342 {
1343 Inkscape::Extension::DB::OutputList o;
1344 Inkscape::Extension::db.get_output_list(o);
1345 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1346 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1347 i++;
1348 }
1350 if (i == o.end())
1351 {
1352 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1353 return;
1354 }
1356 if (sp_export_id) {
1357 SPObject *o = doc->getObjectById(sp_export_id);
1358 if (o == NULL) {
1359 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1360 return;
1361 }
1362 (*i)->set_param_string ("exportId", sp_export_id);
1363 } else {
1364 (*i)->set_param_string ("exportId", "");
1365 }
1367 if (sp_export_area_canvas && sp_export_area_drawing) {
1368 g_warning ("You cannot use --export-area-canvas and --export-area-drawing at the same time; only the former will take effect.");
1369 sp_export_area_drawing = false;
1370 }
1372 if (sp_export_area_drawing) {
1373 (*i)->set_param_bool ("areaDrawing", TRUE);
1374 } else {
1375 (*i)->set_param_bool ("areaDrawing", FALSE);
1376 }
1378 if (sp_export_area_canvas) {
1379 if (sp_export_eps) {
1380 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.");
1381 }
1382 (*i)->set_param_bool ("areaCanvas", TRUE);
1383 } else {
1384 (*i)->set_param_bool ("areaCanvas", FALSE);
1385 }
1387 if (!sp_export_area_drawing && !sp_export_area_canvas && !sp_export_id) {
1388 // neither is set, set canvas as default for ps/pdf and drawing for eps
1389 if (sp_export_eps) {
1390 try {
1391 (*i)->set_param_bool("areaDrawing", TRUE);
1392 } catch (...) {}
1393 }
1394 }
1396 if (sp_export_text_to_path) {
1397 (*i)->set_param_bool("textToPath", TRUE);
1398 } else {
1399 (*i)->set_param_bool("textToPath", FALSE);
1400 }
1402 (*i)->save(doc, uri);
1403 }
1405 #ifdef WIN32
1406 /**
1407 * Export a document to EMF
1408 *
1409 * \param doc Document to export.
1410 * \param uri URI to export to.
1411 * \param mime MIME type to export as (should be "image/x-emf")
1412 */
1414 static void do_export_emf(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 (*i)->save(doc, uri);
1430 }
1431 #endif //WIN32
1433 #ifdef WIN32
1434 bool replaceArgs( int& argc, char**& argv )
1435 {
1436 bool worked = false;
1438 #ifdef REPLACEARGS_DEBUG
1439 MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1440 #endif // REPLACEARGS_DEBUG
1442 wchar_t* line = GetCommandLineW();
1443 if ( line )
1444 {
1445 #ifdef REPLACEARGS_DEBUG
1446 {
1447 gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1448 if ( utf8Line )
1449 {
1450 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1451 {
1452 char tmp[strlen(safe) + 32];
1453 snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1454 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1455 }
1456 }
1457 }
1458 #endif // REPLACEARGS_DEBUG
1460 int numArgs = 0;
1461 wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1463 #ifdef REPLACEARGS_ANSI
1464 // test code for trying things on Win95/98/ME
1465 if ( !parsed )
1466 {
1467 #ifdef REPLACEARGS_DEBUG
1468 MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1469 #endif // REPLACEARGS_DEBUG
1470 int lineLen = wcslen(line) + 1;
1471 wchar_t* lineDup = new wchar_t[lineLen];
1472 wcsncpy( lineDup, line, lineLen );
1474 int pos = 0;
1475 bool inQuotes = false;
1476 bool inWhitespace = true;
1477 std::vector<int> places;
1478 while ( lineDup[pos] )
1479 {
1480 if ( inQuotes )
1481 {
1482 if ( lineDup[pos] == L'"' )
1483 {
1484 inQuotes = false;
1485 }
1486 }
1487 else if ( lineDup[pos] == L'"' )
1488 {
1489 inQuotes = true;
1490 inWhitespace = false;
1491 places.push_back(pos);
1492 }
1493 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1494 {
1495 if ( !inWhitespace )
1496 {
1497 inWhitespace = true;
1498 lineDup[pos] = 0;
1499 }
1500 }
1501 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1502 {
1503 inWhitespace = false;
1504 places.push_back(pos);
1505 }
1506 else
1507 {
1508 // consume
1509 }
1510 pos++;
1511 }
1512 #ifdef REPLACEARGS_DEBUG
1513 {
1514 char tmp[256];
1515 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1516 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1517 }
1518 #endif // REPLACEARGS_DEBUG
1520 wchar_t** block = new wchar_t*[places.size()];
1521 int i = 0;
1522 for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1523 {
1524 block[i++] = &lineDup[*it];
1525 }
1526 parsed = block;
1527 numArgs = places.size();
1528 }
1529 #endif // REPLACEARGS_ANSI
1531 if ( parsed )
1532 {
1533 std::vector<wchar_t*>expandedArgs;
1534 if ( numArgs > 0 )
1535 {
1536 expandedArgs.push_back( parsed[0] );
1537 }
1539 for ( int i1 = 1; i1 < numArgs; i1++ )
1540 {
1541 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1542 wildcarded &= parsed[i1][0] != L'"';
1543 wildcarded &= parsed[i1][0] != L'-';
1544 if ( wildcarded )
1545 {
1546 #ifdef REPLACEARGS_ANSI
1547 WIN32_FIND_DATAA data;
1548 #else
1549 WIN32_FIND_DATAW data;
1550 #endif // REPLACEARGS_ANSI
1552 memset((void *)&data, 0, sizeof(data));
1554 int baseLen = wcslen(parsed[i1]) + 2;
1555 wchar_t* base = new wchar_t[baseLen];
1556 wcsncpy( base, parsed[i1], baseLen );
1557 wchar_t* last = wcsrchr( base, L'\\' );
1558 if ( last )
1559 {
1560 last[1] = 0;
1561 }
1562 else
1563 {
1564 base[0] = 0;
1565 }
1566 baseLen = wcslen( base );
1568 #ifdef REPLACEARGS_ANSI
1569 char target[MAX_PATH];
1570 if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1571 {
1572 HANDLE hf = FindFirstFileA( target, &data );
1573 #else
1574 HANDLE hf = FindFirstFileW( parsed[i1], &data );
1575 #endif // REPLACEARGS_ANSI
1576 if ( hf != INVALID_HANDLE_VALUE )
1577 {
1578 BOOL found = TRUE;
1579 do
1580 {
1581 #ifdef REPLACEARGS_ANSI
1582 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1583 if ( howMany > 0 )
1584 {
1585 howMany += baseLen;
1586 wchar_t* tmp = new wchar_t[howMany + 1];
1587 wcsncpy( tmp, base, howMany + 1 );
1588 MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1589 expandedArgs.push_back( tmp );
1590 found = FindNextFileA( hf, &data );
1591 }
1592 #else
1593 int howMany = wcslen(data.cFileName) + baseLen;
1594 wchar_t* tmp = new wchar_t[howMany + 1];
1595 wcsncpy( tmp, base, howMany + 1 );
1596 wcsncat( tmp, data.cFileName, howMany + 1 );
1597 expandedArgs.push_back( tmp );
1598 found = FindNextFileW( hf, &data );
1599 #endif // REPLACEARGS_ANSI
1600 } while ( found );
1602 FindClose( hf );
1603 }
1604 else
1605 {
1606 expandedArgs.push_back( parsed[i1] );
1607 }
1608 #ifdef REPLACEARGS_ANSI
1609 }
1610 #endif // REPLACEARGS_ANSI
1612 delete[] base;
1613 }
1614 else
1615 {
1616 expandedArgs.push_back( parsed[i1] );
1617 }
1618 }
1620 {
1621 wchar_t** block = new wchar_t*[expandedArgs.size()];
1622 int iz = 0;
1623 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1624 {
1625 block[iz++] = *it;
1626 }
1627 parsed = block;
1628 numArgs = expandedArgs.size();
1629 }
1631 std::vector<gchar*> newArgs;
1632 for ( int i = 0; i < numArgs; i++ )
1633 {
1634 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1635 if ( replacement )
1636 {
1637 #ifdef REPLACEARGS_DEBUG
1638 gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1640 if ( safe2 )
1641 {
1642 {
1643 char tmp[1024];
1644 snprintf( tmp, sizeof(tmp), " [%2d] = '%s'", i, safe2 );
1645 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1646 }
1647 g_free( safe2 );
1648 }
1649 #endif // REPLACEARGS_DEBUG
1651 newArgs.push_back( replacement );
1652 }
1653 else
1654 {
1655 newArgs.push_back( blankParam );
1656 }
1657 }
1659 // Now push our munged params to be the new argv and argc
1660 {
1661 char** block = new char*[newArgs.size()];
1662 int iz = 0;
1663 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1664 {
1665 block[iz++] = *it;
1666 }
1667 argv = block;
1668 argc = newArgs.size();
1669 worked = true;
1670 }
1671 }
1672 #ifdef REPLACEARGS_DEBUG
1673 else
1674 {
1675 MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1676 }
1677 #endif // REPLACEARGS_DEBUG
1678 }
1679 #ifdef REPLACEARGS_DEBUG
1680 else
1681 {
1682 {
1683 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1684 }
1686 char* line2 = GetCommandLineA();
1687 if ( line2 )
1688 {
1689 gchar *safe = Inkscape::IO::sanitizeString(line2);
1690 {
1691 {
1692 char tmp[strlen(safe) + 32];
1693 snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1694 MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1695 }
1696 }
1697 }
1698 else
1699 {
1700 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1701 }
1702 }
1703 #endif // REPLACEARGS_DEBUG
1705 return worked;
1706 }
1707 #endif // WIN32
1709 static GSList *
1710 sp_process_args(poptContext ctx)
1711 {
1712 GSList *fl = NULL;
1714 gint a;
1715 while ((a = poptGetNextOpt(ctx)) != -1) {
1716 switch (a) {
1717 case SP_ARG_FILE: {
1718 gchar const *fn = poptGetOptArg(ctx);
1719 if (fn != NULL) {
1720 fl = g_slist_append(fl, g_strdup(fn));
1721 }
1722 break;
1723 }
1724 case SP_ARG_VERSION: {
1725 printf("Inkscape %s (%s)\n", INKSCAPE_VERSION, __DATE__);
1726 exit(0);
1727 break;
1728 }
1729 case SP_ARG_EXTENSIONDIR: {
1730 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1731 exit(0);
1732 break;
1733 }
1734 case SP_ARG_VERB_LIST: {
1735 // This really shouldn't go here, we should init the app.
1736 // But, since we're just exiting in this path, there is
1737 // no harm, and this is really a better place to put
1738 // everything else.
1739 Inkscape::Extension::init();
1740 Inkscape::Verb::list();
1741 exit(0);
1742 break;
1743 }
1744 case SP_ARG_VERB:
1745 case SP_ARG_SELECT: {
1746 gchar const *arg = poptGetOptArg(ctx);
1747 if (arg != NULL) {
1748 // printf("Adding in: %s\n", arg);
1749 new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1750 }
1751 break;
1752 }
1753 case POPT_ERROR_BADOPT: {
1754 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1755 exit(1);
1756 break;
1757 }
1758 default: {
1759 break;
1760 }
1761 }
1762 }
1764 gchar const ** const args = poptGetArgs(ctx);
1765 if (args != NULL) {
1766 for (unsigned i = 0; args[i] != NULL; i++) {
1767 fl = g_slist_append(fl, g_strdup(args[i]));
1768 }
1769 }
1771 return fl;
1772 }
1775 /*
1776 Local Variables:
1777 mode:c++
1778 c-file-style:"stroustrup"
1779 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1780 indent-tabs-mode:nil
1781 fill-column:99
1782 End:
1783 */
1784 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :