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