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>
40 #include <popt.h>
41 #ifndef POPT_TABLEEND
42 #define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
43 #endif /* Not def: POPT_TABLEEND */
45 #include <libxml/tree.h>
46 #include <glib-object.h>
47 #include <gtk/gtkmain.h>
48 #include <gtk/gtksignal.h>
49 #include <gtk/gtkwindow.h>
50 #include <gtk/gtkbox.h>
52 #include "gc-core.h"
54 #include "macros.h"
55 #include "file.h"
56 #include "document.h"
57 #include "sp-object.h"
58 #include "interface.h"
59 #include "print.h"
60 #include "color.h"
61 #include "sp-item.h"
62 #include "sp-root.h"
63 #include "unit-constants.h"
65 #include "svg/svg.h"
66 #include "svg/svg-color.h"
67 #include "svg/stringstream.h"
69 #include "inkscape-private.h"
70 #include "inkscape-stock.h"
71 #include "inkscape_version.h"
73 #include "sp-namedview.h"
74 #include "sp-guide.h"
75 #include "sp-object-repr.h"
76 #include "xml/repr.h"
78 #include "io/sys.h"
80 #include "debug/logger.h"
81 #include "debug/log-display-config.h"
83 #include "helper/png-write.h"
85 #include <extension/extension.h>
86 #include <extension/system.h>
87 #include <extension/db.h>
88 #include <extension/output.h>
90 #ifdef WIN32
91 //#define REPLACEARGS_ANSI
92 //#define REPLACEARGS_DEBUG
94 #include "registrytool.h"
96 #include "extension/internal/win32.h"
97 using Inkscape::Extension::Internal::PrintWin32;
99 #endif // WIN32
101 #include "extension/init.h"
103 #include <glibmm/i18n.h>
104 #include <gtkmm/main.h>
106 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
107 #define bind_textdomain_codeset(p,c)
108 #endif
110 #include "application/application.h"
112 #include "main-cmdlineact.h"
114 #include <png.h>
115 #include <errno.h>
117 enum {
118 SP_ARG_NONE,
119 SP_ARG_NOGUI,
120 SP_ARG_GUI,
121 SP_ARG_FILE,
122 SP_ARG_PRINT,
123 SP_ARG_EXPORT_PNG,
124 SP_ARG_EXPORT_DPI,
125 SP_ARG_EXPORT_AREA,
126 SP_ARG_EXPORT_AREA_DRAWING,
127 SP_ARG_EXPORT_AREA_CANVAS,
128 SP_ARG_EXPORT_AREA_SNAP,
129 SP_ARG_EXPORT_WIDTH,
130 SP_ARG_EXPORT_HEIGHT,
131 SP_ARG_EXPORT_ID,
132 SP_ARG_EXPORT_ID_ONLY,
133 SP_ARG_EXPORT_USE_HINTS,
134 SP_ARG_EXPORT_BACKGROUND,
135 SP_ARG_EXPORT_BACKGROUND_OPACITY,
136 SP_ARG_EXPORT_SVG,
137 SP_ARG_EXPORT_PS,
138 SP_ARG_EXPORT_EPS,
139 SP_ARG_EXPORT_PDF,
140 #ifdef WIN32
141 SP_ARG_EXPORT_EMF,
142 #endif //WIN32
143 SP_ARG_EXPORT_TEXT_TO_PATH,
144 SP_ARG_EXPORT_FONT,
145 SP_ARG_EXPORT_BBOX_PAGE,
146 SP_ARG_EXTENSIONDIR,
147 SP_ARG_FIT_PAGE_TO_DRAWING,
148 SP_ARG_QUERY_X,
149 SP_ARG_QUERY_Y,
150 SP_ARG_QUERY_WIDTH,
151 SP_ARG_QUERY_HEIGHT,
152 SP_ARG_QUERY_ALL,
153 SP_ARG_QUERY_ID,
154 SP_ARG_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(SPDocument* doc, gchar const* uri, char const *mime);
166 static void do_export_pdf(SPDocument* doc, gchar const* uri, char const *mime);
167 #ifdef WIN32
168 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
169 #endif //WIN32
170 static void do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id);
171 static void do_query_all (SPDocument *doc);
172 static void do_query_all_recurse (SPObject *o);
174 static gchar *sp_global_printer = NULL;
175 static gchar *sp_export_png = NULL;
176 static gchar *sp_export_dpi = NULL;
177 static gchar *sp_export_area = NULL;
178 static gboolean sp_export_area_drawing = FALSE;
179 static gboolean sp_export_area_canvas = FALSE;
180 static gchar *sp_export_width = NULL;
181 static gchar *sp_export_height = NULL;
182 static gchar *sp_export_id = NULL;
183 static gchar *sp_export_background = NULL;
184 static gchar *sp_export_background_opacity = NULL;
185 static gboolean sp_export_area_snap = FALSE;
186 static gboolean sp_export_use_hints = FALSE;
187 static gboolean sp_export_id_only = FALSE;
188 static gchar *sp_export_svg = NULL;
189 static gchar *sp_export_ps = NULL;
190 static gchar *sp_export_eps = NULL;
191 static gchar *sp_export_pdf = NULL;
192 #ifdef WIN32
193 static gchar *sp_export_emf = NULL;
194 #endif //WIN32
195 static gboolean sp_export_text_to_path = FALSE;
196 static gboolean sp_export_font = FALSE;
197 static gboolean sp_export_bbox_page = FALSE;
198 static gboolean sp_query_x = FALSE;
199 static gboolean sp_query_y = FALSE;
200 static gboolean sp_query_width = FALSE;
201 static gboolean sp_query_height = FALSE;
202 static gboolean sp_query_all = FALSE;
203 static gchar *sp_query_id = NULL;
204 static int sp_new_gui = FALSE;
205 static gboolean sp_vacuum_defs = FALSE;
207 static gchar *sp_export_png_utf8 = NULL;
208 static gchar *sp_export_svg_utf8 = NULL;
209 static gchar *sp_global_printer_utf8 = NULL;
211 #ifdef WIN32
212 static bool replaceArgs( int& argc, char**& argv );
213 #endif
214 static GSList *sp_process_args(poptContext ctx);
215 struct poptOption options[] = {
216 {"version", 'V',
217 POPT_ARG_NONE, NULL, SP_ARG_VERSION,
218 N_("Print the Inkscape version number"),
219 NULL},
221 {"without-gui", 'z',
222 POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
223 N_("Do not use X server (only process files from console)"),
224 NULL},
226 {"with-gui", 'g',
227 POPT_ARG_NONE, NULL, SP_ARG_GUI,
228 N_("Try to use X server (even if $DISPLAY is not set)"),
229 NULL},
231 {"file", 'f',
232 POPT_ARG_STRING, NULL, SP_ARG_FILE,
233 N_("Open specified document(s) (option string may be excluded)"),
234 N_("FILENAME")},
236 {"print", 'p',
237 POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
238 N_("Print document(s) to specified output file (use '| program' for pipe)"),
239 N_("FILENAME")},
241 {"export-png", 'e',
242 POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
243 N_("Export document to a PNG file"),
244 N_("FILENAME")},
246 {"export-dpi", 'd',
247 POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
248 N_("The resolution used for exporting SVG into bitmap (default 90)"),
249 N_("DPI")},
251 {"export-area", 'a',
252 POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
253 N_("Exported area in SVG user units (default is the canvas; 0,0 is lower-left corner)"),
254 N_("x0:y0:x1:y1")},
256 {"export-area-drawing", 'D',
257 POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
258 N_("Exported area is the entire drawing (not canvas)"),
259 NULL},
261 {"export-area-canvas", 'C',
262 POPT_ARG_NONE, &sp_export_area_canvas, SP_ARG_EXPORT_AREA_CANVAS,
263 N_("Exported area is the entire canvas"),
264 NULL},
266 {"export-area-snap", 0,
267 POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
268 N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
269 NULL},
271 {"export-width", 'w',
272 POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
273 N_("The width of exported bitmap in pixels (overrides export-dpi)"),
274 N_("WIDTH")},
276 {"export-height", 'h',
277 POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
278 N_("The height of exported bitmap in pixels (overrides export-dpi)"),
279 N_("HEIGHT")},
281 {"export-id", 'i',
282 POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
283 N_("The ID of the object to export"),
284 N_("ID")},
286 {"export-id-only", 'j',
287 POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
288 // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
289 // See "man inkscape" for details.
290 N_("Export just the object with export-id, hide all others (only with export-id)"),
291 NULL},
293 {"export-use-hints", 't',
294 POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
295 N_("Use stored filename and DPI hints when exporting (only with export-id)"),
296 NULL},
298 {"export-background", 'b',
299 POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
300 N_("Background color of exported bitmap (any SVG-supported color string)"),
301 N_("COLOR")},
303 {"export-background-opacity", 'y',
304 POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
305 N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
306 N_("VALUE")},
308 {"export-plain-svg", 'l',
309 POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
310 N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
311 N_("FILENAME")},
313 {"export-ps", 'P',
314 POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
315 N_("Export document to a PS file"),
316 N_("FILENAME")},
318 {"export-eps", 'E',
319 POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
320 N_("Export document to an EPS file"),
321 N_("FILENAME")},
323 {"export-pdf", 'A',
324 POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
325 N_("Export document to a PDF file"),
326 N_("FILENAME")},
328 #ifdef WIN32
329 {"export-emf", 'M',
330 POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
331 N_("Export document to an Enhanced Metafile (EMF) File"),
332 N_("FILENAME")},
333 #endif //WIN32
335 {"export-text-to-path", 'T',
336 POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
337 N_("Convert text object to paths on export (EPS)"),
338 NULL},
340 {"export-embed-fonts", 'F',
341 POPT_ARG_NONE, &sp_export_font, SP_ARG_EXPORT_FONT,
342 N_("Embed fonts on export (Type 1 only) (EPS)"),
343 NULL},
345 {"export-bbox-page", 'B',
346 POPT_ARG_NONE, &sp_export_bbox_page, SP_ARG_EXPORT_BBOX_PAGE,
347 N_("Export files with the bounding box set to the page size (EPS)"),
348 NULL},
350 {"query-x", 'X',
351 POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
352 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
353 N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
354 NULL},
356 {"query-y", 'Y',
357 POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
358 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
359 N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
360 NULL},
362 {"query-width", 'W',
363 POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
364 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
365 N_("Query the width of the drawing or, if specified, of the object with --query-id"),
366 NULL},
368 {"query-height", 'H',
369 POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
370 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
371 N_("Query the height of the drawing or, if specified, of the object with --query-id"),
372 NULL},
374 {"query-all", 'S',
375 POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
376 N_("List id,x,y,w,h for all objects"),
377 NULL},
379 {"query-id", 'I',
380 POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
381 N_("The ID of the object whose dimensions are queried"),
382 N_("ID")},
384 {"extension-directory", 'x',
385 POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
386 // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
387 N_("Print out the extension directory and exit"),
388 NULL},
390 {"vacuum-defs", 0,
391 POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
392 N_("Remove unused definitions from the defs section(s) of the document"),
393 NULL},
395 {"verb-list", 0,
396 POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
397 N_("List the IDs of all the verbs in Inkscape"),
398 NULL},
400 {"verb", 0,
401 POPT_ARG_STRING, NULL, SP_ARG_VERB,
402 N_("Verb to call when Inkscape opens."),
403 N_("VERB-ID")},
405 {"select", 0,
406 POPT_ARG_STRING, NULL, SP_ARG_SELECT,
407 N_("Object ID to select when Inkscape opens."),
408 N_("OBJECT-ID")},
410 POPT_AUTOHELP POPT_TABLEEND
411 };
413 static bool needToRecodeParams = true;
414 gchar * blankParam = g_strdup("");
418 #ifdef WIN32
420 /**
421 * Return the directory of the .exe that is currently running
422 */
423 static Glib::ustring _win32_getExePath()
424 {
425 char exeName[MAX_PATH+1];
426 GetModuleFileName(NULL, exeName, MAX_PATH);
427 char *slashPos = strrchr(exeName, '\\');
428 if (slashPos)
429 *slashPos = '\0';
430 Glib::ustring s = exeName;
431 return s;
432 }
434 /**
435 * Set up the PATH and PYTHONPATH environment variables on
436 * win32
437 */
438 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
439 {
441 char *oldenv = getenv("PATH");
442 Glib::ustring tmp = "PATH=";
443 tmp += exePath;
444 tmp += ";";
445 tmp += exePath;
446 tmp += "\\python;";
447 tmp += exePath;
448 tmp += "\\python\\Scripts;"; // for uniconv.cmd
449 tmp += exePath;
450 tmp += "\\perl";
451 if(oldenv != NULL) {
452 tmp += ";";
453 tmp += oldenv;
454 }
455 _putenv(tmp.c_str());
457 oldenv = getenv("PYTHONPATH");
458 tmp = "PYTHONPATH=";
459 tmp += exePath;
460 tmp += "\\python;";
461 tmp += exePath;
462 tmp += "\\python\\Lib;";
463 tmp += exePath;
464 tmp += "\\python\\DLLs";
465 if(oldenv != NULL) {
466 tmp += ";";
467 tmp += oldenv;
468 }
469 _putenv(tmp.c_str());
471 return 0;
472 }
473 #endif
477 /**
478 * This is the classic main() entry point of the program, though on some
479 * architectures it might be called by something else.
480 */
481 int
482 main(int argc, char **argv)
483 {
484 #ifdef HAVE_FPSETMASK
485 /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__. It's probably
486 safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
487 the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
488 fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
489 #endif
491 #ifdef WIN32
492 /*
493 Set the current directory to the directory of the
494 executable. This seems redundant, but is needed for
495 when inkscape.exe is executed from another directory.
496 We use relative paths on win32.
497 HKCR\svgfile\shell\open\command is a good example
498 */
499 Glib::ustring homedir = _win32_getExePath();
500 SetCurrentDirectory(homedir.c_str());
501 _win32_set_inkscape_env(homedir);
502 RegistryTool rt;
503 rt.setPathInfo();
504 #endif
506 /**
507 * Call bindtextdomain() for various machines's paths
508 */
509 #ifdef ENABLE_NLS
510 #ifdef WIN32
511 Glib::ustring localePath = homedir;
512 localePath += "\\";
513 localePath += PACKAGE_LOCALE_DIR;
514 bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
515 #else
516 #ifdef ENABLE_BINRELOC
517 bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
518 #else
519 bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
520 #endif
521 #endif
522 // Allow the user to override the locale directory by setting
523 // the environment variable INKSCAPE_LOCALEDIR.
524 char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
525 if (inkscape_localedir != NULL) {
526 bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
527 }
528 #endif
530 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
532 #ifdef ENABLE_NLS
533 textdomain(GETTEXT_PACKAGE);
534 #endif
536 LIBXML_TEST_VERSION
538 Inkscape::GC::init();
540 Inkscape::Debug::Logger::init();
542 gboolean use_gui;
544 #ifndef WIN32
545 use_gui = (getenv("DISPLAY") != NULL);
546 #else
547 use_gui = TRUE;
548 #endif
549 /* Test whether with/without GUI is forced */
550 for (int i = 1; i < argc; i++) {
551 if (!strcmp(argv[i], "-z")
552 || !strcmp(argv[i], "--without-gui")
553 || !strcmp(argv[i], "-p")
554 || !strncmp(argv[i], "--print", 7)
555 || !strcmp(argv[i], "-e")
556 || !strncmp(argv[i], "--export-png", 12)
557 || !strcmp(argv[i], "-l")
558 || !strncmp(argv[i], "--export-plain-svg", 12)
559 || !strcmp(argv[i], "-i")
560 || !strncmp(argv[i], "--export-area-drawing", 21)
561 || !strcmp(argv[i], "-D")
562 || !strncmp(argv[i], "--export-area-canvas", 20)
563 || !strcmp(argv[i], "-C")
564 || !strncmp(argv[i], "--export-id", 12)
565 || !strcmp(argv[i], "-P")
566 || !strncmp(argv[i], "--export-ps", 11)
567 || !strcmp(argv[i], "-E")
568 || !strncmp(argv[i], "--export-eps", 12)
569 || !strcmp(argv[i], "-A")
570 || !strncmp(argv[i], "--export-pdf", 12)
571 #ifdef WIN32
572 || !strcmp(argv[i], "-M")
573 || !strncmp(argv[i], "--export-emf", 12)
574 #endif //WIN32
575 || !strcmp(argv[i], "-W")
576 || !strncmp(argv[i], "--query-width", 13)
577 || !strcmp(argv[i], "-H")
578 || !strncmp(argv[i], "--query-height", 14)
579 || !strcmp(argv[i], "-S")
580 || !strncmp(argv[i], "--query-all", 11)
581 || !strcmp(argv[i], "-X")
582 || !strncmp(argv[i], "--query-x", 13)
583 || !strcmp(argv[i], "-Y")
584 || !strncmp(argv[i], "--query-y", 14)
585 || !strcmp(argv[i], "--vacuum-defs")
586 )
587 {
588 /* main_console handles any exports -- not the gui */
589 use_gui = FALSE;
590 break;
591 } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
592 use_gui = TRUE;
593 break;
594 }
595 }
597 #ifdef WIN32
598 #ifndef REPLACEARGS_ANSI
599 if ( PrintWin32::is_os_wide() )
600 #endif // REPLACEARGS_ANSI
601 {
602 // If the call fails, we'll need to convert charsets
603 needToRecodeParams = !replaceArgs( argc, argv );
604 }
605 #endif // WIN32
607 /// \todo Should this be a static object (see inkscape.cpp)?
608 Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
610 return app.run();
611 }
616 void fixupSingleFilename( gchar **orig, gchar **spare )
617 {
618 if ( orig && *orig && **orig ) {
619 GError *error = NULL;
620 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
621 if ( newFileName )
622 {
623 *orig = newFileName;
624 if ( spare ) {
625 *spare = newFileName;
626 }
627 // g_message("Set a replacement fixup");
628 }
629 }
630 }
634 GSList *fixupFilenameEncoding( GSList* fl )
635 {
636 GSList *newFl = NULL;
637 while ( fl ) {
638 gchar *fn = static_cast<gchar*>(fl->data);
639 fl = g_slist_remove( fl, fl->data );
640 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
641 if ( newFileName ) {
643 if ( 0 )
644 {
645 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
646 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
647 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
648 "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
649 gtk_dialog_run (GTK_DIALOG (w));
650 gtk_widget_destroy (w);
651 g_free(safeNewFn);
652 g_free(safeFn);
653 }
655 g_free( fn );
656 fn = newFileName;
657 newFileName = 0;
658 }
659 else
660 if ( 0 )
661 {
662 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
663 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
664 gtk_dialog_run (GTK_DIALOG (w));
665 gtk_widget_destroy (w);
666 g_free(safeFn);
667 }
668 newFl = g_slist_append( newFl, fn );
669 }
670 return newFl;
671 }
673 int sp_common_main( int argc, char const **argv, GSList **flDest )
674 {
675 /// \todo fixme: Move these to some centralized location (Lauris)
676 sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
677 sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
680 // temporarily switch gettext encoding to locale, so that help messages can be output properly
681 gchar const *charset;
682 g_get_charset(&charset);
684 bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
686 poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
687 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
688 g_return_val_if_fail(ctx != NULL, 1);
690 /* Collect own arguments */
691 GSList *fl = sp_process_args(ctx);
692 poptFreeContext(ctx);
694 // now switch gettext back to UTF-8 (for GUI)
695 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
697 // Now let's see if the file list still holds up
698 if ( needToRecodeParams )
699 {
700 fl = fixupFilenameEncoding( fl );
701 }
703 // Check the globals for filename-fixup
704 if ( needToRecodeParams )
705 {
706 fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
707 fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
708 fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
709 }
710 else
711 {
712 if ( sp_export_png )
713 sp_export_png_utf8 = g_strdup( sp_export_png );
714 if ( sp_export_svg )
715 sp_export_svg_utf8 = g_strdup( sp_export_svg );
716 if ( sp_global_printer )
717 sp_global_printer_utf8 = g_strdup( sp_global_printer );
718 }
720 // Return the list if wanted, else free it up.
721 if ( flDest ) {
722 *flDest = fl;
723 fl = 0;
724 } else {
725 while ( fl ) {
726 g_free( fl->data );
727 fl = g_slist_remove( fl, fl->data );
728 }
729 }
730 return 0;
731 }
733 static void
734 snooper(GdkEvent *event, gpointer /*data*/) {
735 if(inkscape_mapalt()) /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
736 {
737 GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
738 switch (event->type) {
739 case GDK_MOTION_NOTIFY:
740 if(event->motion.state & mapping) {
741 event->motion.state|=GDK_MOD1_MASK;
742 }
743 break;
744 case GDK_BUTTON_PRESS:
745 if(event->button.state & mapping) {
746 event->button.state|=GDK_MOD1_MASK;
747 }
748 break;
749 case GDK_KEY_PRESS:
750 if(event->key.state & mapping) {
751 event->key.state|=GDK_MOD1_MASK;
752 }
753 break;
754 default:
755 break;
756 }
757 }
758 gtk_main_do_event (event);
759 }
761 int
762 sp_main_gui(int argc, char const **argv)
763 {
764 Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
766 GSList *fl = NULL;
767 int retVal = sp_common_main( argc, argv, &fl );
768 g_return_val_if_fail(retVal == 0, 1);
770 inkscape_gtk_stock_init();
772 gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
774 Inkscape::Debug::log_display_config();
776 /* Set default icon */
777 gchar *filename = (gchar *) g_build_filename (INKSCAPE_APPICONDIR, "inkscape.png", NULL);
778 if (Inkscape::IO::file_test(filename, (GFileTest)(G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))) {
779 gtk_window_set_default_icon_from_file(filename, NULL);
780 }
781 g_free (filename);
782 filename = 0;
784 gboolean create_new = TRUE;
786 /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
787 inkscape_application_init(argv[0], true);
789 while (fl) {
790 if (sp_file_open((gchar *)fl->data,NULL)) {
791 create_new=FALSE;
792 }
793 fl = g_slist_remove(fl, fl->data);
794 }
795 if (create_new) {
796 sp_file_new_default();
797 }
799 Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
800 main_instance.run();
802 #ifdef WIN32
803 //We might not need anything here
804 //sp_win32_finish(); <-- this is a NOP func
805 #endif
807 return 0;
808 }
810 int
811 sp_main_console(int argc, char const **argv)
812 {
813 /* We are started in text mode */
815 /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
816 * in a non-Gtk environment. Used in libnrtype's
817 * FontInstance.cpp and FontFactory.cpp.
818 * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
819 */
820 g_type_init();
821 char **argv2 = const_cast<char **>(argv);
822 gtk_init_check( &argc, &argv2 );
823 //setlocale(LC_ALL, "");
825 GSList *fl = NULL;
826 int retVal = sp_common_main( argc, argv, &fl );
827 g_return_val_if_fail(retVal == 0, 1);
829 if (fl == NULL) {
830 g_print("Nothing to do!\n");
831 exit(0);
832 }
834 inkscape_application_init(argv[0], false);
836 while (fl) {
837 SPDocument *doc;
839 doc = Inkscape::Extension::open(NULL, (gchar *)fl->data);
840 if (doc == NULL) {
841 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), (gchar *)fl->data);
842 }
843 if (doc == NULL) {
844 g_warning("Specified document %s cannot be opened (is it valid SVG file?)", (gchar *) fl->data);
845 } else {
846 if (sp_vacuum_defs) {
847 vacuum_document(doc);
848 }
849 if (sp_vacuum_defs && !sp_export_svg) {
850 // save under the name given in the command line
851 sp_repr_save_file(doc->rdoc, (gchar *)fl->data, SP_SVG_NS_URI);
852 }
853 if (sp_global_printer) {
854 sp_print_document_to_file(doc, sp_global_printer);
855 }
856 if (sp_export_png) {
857 sp_do_export_png(doc);
858 }
859 if (sp_export_svg) {
860 Inkscape::XML::Document *rdoc;
861 Inkscape::XML::Node *repr;
862 rdoc = sp_repr_document_new("svg:svg");
863 repr = rdoc->root();
864 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
865 sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
866 }
867 if (sp_export_ps) {
868 do_export_ps(doc, sp_export_ps, "image/x-postscript");
869 }
870 if (sp_export_eps) {
871 do_export_ps(doc, sp_export_eps, "image/x-e-postscript");
872 }
873 if (sp_export_pdf) {
874 do_export_pdf(doc, sp_export_pdf, "application/pdf");
875 }
876 #ifdef WIN32
877 if (sp_export_emf) {
878 do_export_emf(doc, sp_export_emf, "image/x-emf");
879 }
880 #endif //WIN32
881 if (sp_query_all) {
882 do_query_all (doc);
883 } else if (sp_query_width || sp_query_height) {
884 do_query_dimension (doc, true, sp_query_width? NR::X : NR::Y, sp_query_id);
885 } else if (sp_query_x || sp_query_y) {
886 do_query_dimension (doc, false, sp_query_x? NR::X : NR::Y, sp_query_id);
887 }
888 }
890 fl = g_slist_remove(fl, fl->data);
891 }
893 inkscape_unref();
895 return 0;
896 }
898 static void
899 do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id)
900 {
901 SPObject *o = NULL;
903 if (id) {
904 o = doc->getObjectById(id);
905 if (o) {
906 if (!SP_IS_ITEM (o)) {
907 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
908 return;
909 }
910 } else {
911 g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
912 return;
913 }
914 } else {
915 o = SP_DOCUMENT_ROOT(doc);
916 }
918 if (o) {
919 sp_document_ensure_up_to_date (doc);
920 SPItem *item = ((SPItem *) o);
922 // "true" SVG bbox for scripting
923 NR::Maybe<NR::Rect> area = item->getBounds(from_2geom(sp_item_i2doc_affine(item)));
924 if (area) {
925 Inkscape::SVGOStringStream os;
926 if (extent) {
927 os << area->extent(axis);
928 } else {
929 os << area->min()[axis];
930 }
931 g_print ("%s", os.str().c_str());
932 } else {
933 g_print("0");
934 }
935 }
936 }
938 static void
939 do_query_all (SPDocument *doc)
940 {
941 SPObject *o = NULL;
943 o = SP_DOCUMENT_ROOT(doc);
945 if (o) {
946 sp_document_ensure_up_to_date (doc);
947 do_query_all_recurse(o);
948 }
949 }
951 static void
952 do_query_all_recurse (SPObject *o)
953 {
954 SPItem *item = ((SPItem *) o);
955 if (o->id && SP_IS_ITEM(item)) {
956 NR::Maybe<NR::Rect> area = item->getBounds(from_2geom(sp_item_i2doc_affine(item)));
957 if (area) {
958 Inkscape::SVGOStringStream os;
959 os << o->id;
960 os << "," << area->min()[NR::X];
961 os << "," << area->min()[NR::Y];
962 os << "," << area->extent(NR::X);
963 os << "," << area->extent(NR::Y);
964 g_print ("%s\n", os.str().c_str());
965 }
966 }
968 SPObject *child = o->children;
969 while (child) {
970 do_query_all_recurse (child);
971 child = child->next;
972 }
973 }
976 static void
977 sp_do_export_png(SPDocument *doc)
978 {
979 const gchar *filename = NULL;
980 gdouble dpi = 0.0;
982 if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
983 g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
984 }
986 GSList *items = NULL;
988 NRRect area;
989 if (sp_export_id || sp_export_area_drawing) {
991 SPObject *o = NULL;
992 SPObject *o_area = NULL;
993 if (sp_export_id && sp_export_area_drawing) {
994 o = doc->getObjectById(sp_export_id);
995 o_area = SP_DOCUMENT_ROOT (doc);
996 } else if (sp_export_id) {
997 o = doc->getObjectById(sp_export_id);
998 o_area = o;
999 } else if (sp_export_area_drawing) {
1000 o = SP_DOCUMENT_ROOT (doc);
1001 o_area = o;
1002 }
1004 if (o) {
1005 if (!SP_IS_ITEM (o)) {
1006 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1007 return;
1008 }
1010 items = g_slist_prepend (items, SP_ITEM(o));
1012 if (sp_export_id_only) {
1013 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1014 }
1016 if (sp_export_use_hints) {
1018 // retrieve export filename hint
1019 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1020 if (fn_hint) {
1021 if (sp_export_png) {
1022 g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1023 filename = sp_export_png;
1024 } else {
1025 filename = fn_hint;
1026 }
1027 } else {
1028 g_warning ("Export filename hint not found for the object.");
1029 filename = sp_export_png;
1030 }
1032 // retrieve export dpi hints
1033 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1034 if (dpi_hint) {
1035 if (sp_export_dpi || sp_export_width || sp_export_height) {
1036 g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1037 } else {
1038 dpi = atof(dpi_hint);
1039 }
1040 } else {
1041 g_warning ("Export DPI hint not found for the object.");
1042 }
1044 }
1046 // write object bbox to area
1047 sp_document_ensure_up_to_date (doc);
1048 sp_item_invoke_bbox((SPItem *) o_area, &area, from_2geom(sp_item_i2r_affine((SPItem *) o_area)), TRUE);
1049 } else {
1050 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1051 return;
1052 }
1053 }
1055 if (sp_export_area) {
1056 /* Try to parse area (given in SVG pixels) */
1057 if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &area.x0, &area.y0, &area.x1, &area.y1) == 4) {
1058 g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1059 return;
1060 }
1061 if ((area.x0 >= area.x1) || (area.y0 >= area.y1)) {
1062 g_warning("Export area '%s' has negative width or height. Nothing exported.", sp_export_area);
1063 return;
1064 }
1065 } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1066 /* Export the whole canvas */
1067 sp_document_ensure_up_to_date (doc);
1068 area.x0 = SP_ROOT(doc->root)->x.computed;
1069 area.y0 = SP_ROOT(doc->root)->y.computed;
1070 area.x1 = area.x0 + sp_document_width (doc);
1071 area.y1 = area.y0 + sp_document_height (doc);
1072 }
1074 // set filename and dpi from options, if not yet set from the hints
1075 if (!filename) {
1076 if (!sp_export_png) {
1077 g_warning ("No export filename given and no filename hint. Nothing exported.");
1078 return;
1079 }
1080 filename = sp_export_png;
1081 }
1083 if (sp_export_dpi && dpi == 0.0) {
1084 dpi = atof(sp_export_dpi);
1085 if ((dpi < 0.1) || (dpi > 10000.0)) {
1086 g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1087 return;
1088 }
1089 g_print("DPI: %g\n", dpi);
1090 }
1092 if (sp_export_area_snap) {
1093 area.x0 = std::floor (area.x0);
1094 area.y0 = std::floor (area.y0);
1095 area.x1 = std::ceil (area.x1);
1096 area.y1 = std::ceil (area.y1);
1097 }
1099 // default dpi
1100 if (dpi == 0.0)
1101 dpi = PX_PER_IN;
1103 unsigned long int width = 0;
1104 unsigned long int height = 0;
1106 if (sp_export_width) {
1107 width = strtoul(sp_export_width, NULL, 0);
1108 if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1109 g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1110 return;
1111 }
1112 dpi = (gdouble) width * PX_PER_IN / (area.x1 - area.x0);
1113 }
1115 if (sp_export_height) {
1116 height = strtoul(sp_export_height, NULL, 0);
1117 if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1118 g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1119 return;
1120 }
1121 dpi = (gdouble) height * PX_PER_IN / (area.y1 - area.y0);
1122 }
1124 if (!sp_export_width) {
1125 width = (unsigned long int) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1126 }
1128 if (!sp_export_height) {
1129 height = (unsigned long int) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1130 }
1132 guint32 bgcolor = 0x00000000;
1133 if (sp_export_background) {
1134 // override the page color
1135 bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1136 bgcolor |= 0xff; // default is no opacity
1137 } else {
1138 // read from namedview
1139 Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1140 if (nv && nv->attribute("pagecolor"))
1141 bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1142 if (nv && nv->attribute("inkscape:pageopacity"))
1143 bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1144 }
1146 if (sp_export_background_opacity) {
1147 // override opacity
1148 gfloat value;
1149 if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1150 if (value > 1.0) {
1151 value = CLAMP (value, 1.0f, 255.0f);
1152 bgcolor &= (guint32) 0xffffff00;
1153 bgcolor |= (guint32) floor(value);
1154 } else {
1155 value = CLAMP (value, 0.0f, 1.0f);
1156 bgcolor &= (guint32) 0xffffff00;
1157 bgcolor |= SP_COLOR_F_TO_U(value);
1158 }
1159 }
1160 }
1162 g_print("Background RRGGBBAA: %08x\n", bgcolor);
1164 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);
1166 g_print("Bitmap saved as: %s\n", filename);
1168 if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1169 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);
1170 } else {
1171 g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1172 }
1174 g_slist_free (items);
1175 }
1178 /**
1179 * Perform an export of either PS or EPS.
1180 *
1181 * \param doc Document to export.
1182 * \param uri URI to export to.
1183 * \param mime MIME type to export as.
1184 */
1186 static void do_export_ps(SPDocument* doc, gchar const* uri, char const* mime)
1187 {
1188 Inkscape::Extension::DB::OutputList o;
1189 Inkscape::Extension::db.get_output_list(o);
1190 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1191 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1192 i++;
1193 }
1195 if (i == o.end())
1196 {
1197 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1198 return;
1199 }
1201 bool old_text_to_path = false;
1202 bool old_font_embedded = false;
1203 bool old_bbox_page = false;
1205 try {
1206 old_text_to_path = (*i)->get_param_bool("textToPath");
1207 (*i)->set_param_bool("textToPath", sp_export_text_to_path);
1208 }
1209 catch (...) {
1210 g_warning ("Could not set export-text-to-path option for this export.");
1211 }
1213 try {
1214 old_font_embedded = (*i)->get_param_bool("fontEmbedded");
1215 (*i)->set_param_bool("fontEmbedded", sp_export_font);
1216 }
1217 catch (...) {
1218 g_warning ("Could not set export-font option for this export.");
1219 }
1221 try {
1222 old_bbox_page = (*i)->get_param_bool("pageBoundingBox");
1223 (*i)->set_param_bool("pageBoundingBox", sp_export_bbox_page);
1224 }
1225 catch (...) {
1226 g_warning ("Could not set export-bbox-page option for this export.");
1227 }
1229 (*i)->save(doc, uri);
1231 try {
1232 (*i)->set_param_bool("textToPath", old_text_to_path);
1233 (*i)->set_param_bool("fontEmbedded", old_font_embedded);
1234 (*i)->set_param_bool("pageBoundingBox", old_bbox_page);
1235 }
1236 catch (...) {
1238 }
1239 }
1241 /**
1242 * Perform a PDF export
1243 *
1244 * \param doc Document to export.
1245 * \param uri URI to export to.
1246 * \param mime MIME type to export as.
1247 */
1249 static void do_export_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1250 {
1251 Inkscape::Extension::DB::OutputList o;
1252 Inkscape::Extension::db.get_output_list(o);
1253 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1254 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1255 i++;
1256 }
1258 if (i == o.end())
1259 {
1260 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1261 return;
1262 }
1264 if (sp_export_id) {
1265 SPObject *o = doc->getObjectById(sp_export_id);
1266 if (o == NULL) {
1267 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1268 return;
1269 }
1270 (*i)->set_param_string ("exportId", sp_export_id);
1271 } else {
1272 (*i)->set_param_string ("exportId", "");
1273 }
1275 if (sp_export_area_drawing) {
1276 (*i)->set_param_bool ("exportDrawing", TRUE);
1277 } else {
1278 (*i)->set_param_bool ("exportDrawing", FALSE);
1279 }
1281 if (sp_export_area_canvas) {
1282 (*i)->set_param_bool ("exportCanvas", TRUE);
1283 } else {
1284 (*i)->set_param_bool ("exportCanvas", FALSE);
1285 }
1287 if (sp_export_text_to_path) {
1288 (*i)->set_param_bool("textToPath", TRUE);
1289 } else {
1290 (*i)->set_param_bool("textToPath", FALSE);
1291 }
1293 (*i)->save(doc, uri);
1294 }
1296 #ifdef WIN32
1297 /**
1298 * Export a document to EMF
1299 *
1300 * \param doc Document to export.
1301 * \param uri URI to export to.
1302 * \param mime MIME type to export as (should be "image/x-emf")
1303 */
1305 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1306 {
1307 Inkscape::Extension::DB::OutputList o;
1308 Inkscape::Extension::db.get_output_list(o);
1309 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1310 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1311 i++;
1312 }
1314 if (i == o.end())
1315 {
1316 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1317 return;
1318 }
1320 (*i)->save(doc, uri);
1321 }
1322 #endif //WIN32
1324 #ifdef WIN32
1325 bool replaceArgs( int& argc, char**& argv )
1326 {
1327 bool worked = false;
1329 #ifdef REPLACEARGS_DEBUG
1330 MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1331 #endif // REPLACEARGS_DEBUG
1333 wchar_t* line = GetCommandLineW();
1334 if ( line )
1335 {
1336 #ifdef REPLACEARGS_DEBUG
1337 {
1338 gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1339 if ( utf8Line )
1340 {
1341 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1342 {
1343 char tmp[strlen(safe) + 32];
1344 snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1345 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1346 }
1347 }
1348 }
1349 #endif // REPLACEARGS_DEBUG
1351 int numArgs = 0;
1352 wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1354 #ifdef REPLACEARGS_ANSI
1355 // test code for trying things on Win95/98/ME
1356 if ( !parsed )
1357 {
1358 #ifdef REPLACEARGS_DEBUG
1359 MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1360 #endif // REPLACEARGS_DEBUG
1361 int lineLen = wcslen(line) + 1;
1362 wchar_t* lineDup = new wchar_t[lineLen];
1363 wcsncpy( lineDup, line, lineLen );
1365 int pos = 0;
1366 bool inQuotes = false;
1367 bool inWhitespace = true;
1368 std::vector<int> places;
1369 while ( lineDup[pos] )
1370 {
1371 if ( inQuotes )
1372 {
1373 if ( lineDup[pos] == L'"' )
1374 {
1375 inQuotes = false;
1376 }
1377 }
1378 else if ( lineDup[pos] == L'"' )
1379 {
1380 inQuotes = true;
1381 inWhitespace = false;
1382 places.push_back(pos);
1383 }
1384 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1385 {
1386 if ( !inWhitespace )
1387 {
1388 inWhitespace = true;
1389 lineDup[pos] = 0;
1390 }
1391 }
1392 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1393 {
1394 inWhitespace = false;
1395 places.push_back(pos);
1396 }
1397 else
1398 {
1399 // consume
1400 }
1401 pos++;
1402 }
1403 #ifdef REPLACEARGS_DEBUG
1404 {
1405 char tmp[256];
1406 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1407 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1408 }
1409 #endif // REPLACEARGS_DEBUG
1411 wchar_t** block = new wchar_t*[places.size()];
1412 int i = 0;
1413 for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1414 {
1415 block[i++] = &lineDup[*it];
1416 }
1417 parsed = block;
1418 numArgs = places.size();
1419 }
1420 #endif // REPLACEARGS_ANSI
1422 if ( parsed )
1423 {
1424 std::vector<wchar_t*>expandedArgs;
1425 if ( numArgs > 0 )
1426 {
1427 expandedArgs.push_back( parsed[0] );
1428 }
1430 for ( int i1 = 1; i1 < numArgs; i1++ )
1431 {
1432 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1433 wildcarded &= parsed[i1][0] != L'"';
1434 wildcarded &= parsed[i1][0] != L'-';
1435 if ( wildcarded )
1436 {
1437 #ifdef REPLACEARGS_ANSI
1438 WIN32_FIND_DATAA data;
1439 #else
1440 WIN32_FIND_DATAW data;
1441 #endif // REPLACEARGS_ANSI
1443 memset((void *)&data, 0, sizeof(data));
1445 int baseLen = wcslen(parsed[i1]) + 2;
1446 wchar_t* base = new wchar_t[baseLen];
1447 wcsncpy( base, parsed[i1], baseLen );
1448 wchar_t* last = wcsrchr( base, L'\\' );
1449 if ( last )
1450 {
1451 last[1] = 0;
1452 }
1453 else
1454 {
1455 base[0] = 0;
1456 }
1457 baseLen = wcslen( base );
1459 #ifdef REPLACEARGS_ANSI
1460 char target[MAX_PATH];
1461 if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1462 {
1463 HANDLE hf = FindFirstFileA( target, &data );
1464 #else
1465 HANDLE hf = FindFirstFileW( parsed[i1], &data );
1466 #endif // REPLACEARGS_ANSI
1467 if ( hf != INVALID_HANDLE_VALUE )
1468 {
1469 BOOL found = TRUE;
1470 do
1471 {
1472 #ifdef REPLACEARGS_ANSI
1473 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1474 if ( howMany > 0 )
1475 {
1476 howMany += baseLen;
1477 wchar_t* tmp = new wchar_t[howMany + 1];
1478 wcsncpy( tmp, base, howMany + 1 );
1479 MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1480 expandedArgs.push_back( tmp );
1481 found = FindNextFileA( hf, &data );
1482 }
1483 #else
1484 int howMany = wcslen(data.cFileName) + baseLen;
1485 wchar_t* tmp = new wchar_t[howMany + 1];
1486 wcsncpy( tmp, base, howMany + 1 );
1487 wcsncat( tmp, data.cFileName, howMany + 1 );
1488 expandedArgs.push_back( tmp );
1489 found = FindNextFileW( hf, &data );
1490 #endif // REPLACEARGS_ANSI
1491 } while ( found );
1493 FindClose( hf );
1494 }
1495 else
1496 {
1497 expandedArgs.push_back( parsed[i1] );
1498 }
1499 #ifdef REPLACEARGS_ANSI
1500 }
1501 #endif // REPLACEARGS_ANSI
1503 delete[] base;
1504 }
1505 else
1506 {
1507 expandedArgs.push_back( parsed[i1] );
1508 }
1509 }
1511 {
1512 wchar_t** block = new wchar_t*[expandedArgs.size()];
1513 int iz = 0;
1514 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1515 {
1516 block[iz++] = *it;
1517 }
1518 parsed = block;
1519 numArgs = expandedArgs.size();
1520 }
1522 std::vector<gchar*> newArgs;
1523 for ( int i = 0; i < numArgs; i++ )
1524 {
1525 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1526 if ( replacement )
1527 {
1528 #ifdef REPLACEARGS_DEBUG
1529 gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1531 if ( safe2 )
1532 {
1533 {
1534 char tmp[1024];
1535 snprintf( tmp, sizeof(tmp), " [%2d] = '%s'", i, safe2 );
1536 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1537 }
1538 g_free( safe2 );
1539 }
1540 #endif // REPLACEARGS_DEBUG
1542 newArgs.push_back( replacement );
1543 }
1544 else
1545 {
1546 newArgs.push_back( blankParam );
1547 }
1548 }
1550 // Now push our munged params to be the new argv and argc
1551 {
1552 char** block = new char*[newArgs.size()];
1553 int iz = 0;
1554 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1555 {
1556 block[iz++] = *it;
1557 }
1558 argv = block;
1559 argc = newArgs.size();
1560 worked = true;
1561 }
1562 }
1563 #ifdef REPLACEARGS_DEBUG
1564 else
1565 {
1566 MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1567 }
1568 #endif // REPLACEARGS_DEBUG
1569 }
1570 #ifdef REPLACEARGS_DEBUG
1571 else
1572 {
1573 {
1574 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1575 }
1577 char* line2 = GetCommandLineA();
1578 if ( line2 )
1579 {
1580 gchar *safe = Inkscape::IO::sanitizeString(line2);
1581 {
1582 {
1583 char tmp[strlen(safe) + 32];
1584 snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1585 MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1586 }
1587 }
1588 }
1589 else
1590 {
1591 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1592 }
1593 }
1594 #endif // REPLACEARGS_DEBUG
1596 return worked;
1597 }
1598 #endif // WIN32
1600 static GSList *
1601 sp_process_args(poptContext ctx)
1602 {
1603 GSList *fl = NULL;
1605 gint a;
1606 while ((a = poptGetNextOpt(ctx)) >= 0) {
1607 switch (a) {
1608 case SP_ARG_FILE: {
1609 gchar const *fn = poptGetOptArg(ctx);
1610 if (fn != NULL) {
1611 fl = g_slist_append(fl, g_strdup(fn));
1612 }
1613 break;
1614 }
1615 case SP_ARG_VERSION: {
1616 printf("Inkscape %s (%s)\n", INKSCAPE_VERSION, __DATE__);
1617 exit(0);
1618 break;
1619 }
1620 case SP_ARG_EXTENSIONDIR: {
1621 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1622 exit(0);
1623 break;
1624 }
1625 case SP_ARG_VERB_LIST: {
1626 // This really shouldn't go here, we should init the app.
1627 // But, since we're just exiting in this path, there is
1628 // no harm, and this is really a better place to put
1629 // everything else.
1630 Inkscape::Extension::init();
1631 Inkscape::Verb::list();
1632 exit(0);
1633 break;
1634 }
1635 case SP_ARG_VERB:
1636 case SP_ARG_SELECT: {
1637 gchar const *arg = poptGetOptArg(ctx);
1638 if (arg != NULL) {
1639 // printf("Adding in: %s\n", arg);
1640 new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1641 }
1642 break;
1643 }
1644 default: {
1645 break;
1646 }
1647 }
1648 }
1650 gchar const ** const args = poptGetArgs(ctx);
1651 if (args != NULL) {
1652 for (unsigned i = 0; args[i] != NULL; i++) {
1653 fl = g_slist_append(fl, g_strdup(args[i]));
1654 }
1655 }
1657 return fl;
1658 }
1661 /*
1662 Local Variables:
1663 mode:c++
1664 c-file-style:"stroustrup"
1665 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1666 indent-tabs-mode:nil
1667 fill-column:99
1668 End:
1669 */
1670 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :