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 boost::optional<NR::Rect> area = item->getBounds(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 boost::optional<NR::Rect> area = item->getBounds(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, 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 errno=0;
1108 width = strtoul(sp_export_width, NULL, 0);
1109 if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1110 g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1111 return;
1112 }
1113 dpi = (gdouble) width * PX_PER_IN / (area.x1 - area.x0);
1114 }
1116 if (sp_export_height) {
1117 errno=0;
1118 height = strtoul(sp_export_height, NULL, 0);
1119 if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1120 g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1121 return;
1122 }
1123 dpi = (gdouble) height * PX_PER_IN / (area.y1 - area.y0);
1124 }
1126 if (!sp_export_width) {
1127 width = (unsigned long int) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1128 }
1130 if (!sp_export_height) {
1131 height = (unsigned long int) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1132 }
1134 guint32 bgcolor = 0x00000000;
1135 if (sp_export_background) {
1136 // override the page color
1137 bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1138 bgcolor |= 0xff; // default is no opacity
1139 } else {
1140 // read from namedview
1141 Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1142 if (nv && nv->attribute("pagecolor"))
1143 bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1144 if (nv && nv->attribute("inkscape:pageopacity"))
1145 bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1146 }
1148 if (sp_export_background_opacity) {
1149 // override opacity
1150 gfloat value;
1151 if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1152 if (value > 1.0) {
1153 value = CLAMP (value, 1.0f, 255.0f);
1154 bgcolor &= (guint32) 0xffffff00;
1155 bgcolor |= (guint32) floor(value);
1156 } else {
1157 value = CLAMP (value, 0.0f, 1.0f);
1158 bgcolor &= (guint32) 0xffffff00;
1159 bgcolor |= SP_COLOR_F_TO_U(value);
1160 }
1161 }
1162 }
1164 g_print("Background RRGGBBAA: %08x\n", bgcolor);
1166 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);
1168 g_print("Bitmap saved as: %s\n", filename);
1170 if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1171 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);
1172 } else {
1173 g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1174 }
1176 g_slist_free (items);
1177 }
1180 /**
1181 * Perform an export of either PS or EPS.
1182 *
1183 * \param doc Document to export.
1184 * \param uri URI to export to.
1185 * \param mime MIME type to export as.
1186 */
1188 static void do_export_ps(SPDocument* doc, gchar const* uri, char const* mime)
1189 {
1190 Inkscape::Extension::DB::OutputList o;
1191 Inkscape::Extension::db.get_output_list(o);
1192 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1193 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1194 i++;
1195 }
1197 if (i == o.end())
1198 {
1199 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1200 return;
1201 }
1203 bool old_text_to_path = false;
1204 bool old_font_embedded = false;
1205 bool old_bbox_page = false;
1207 try {
1208 old_text_to_path = (*i)->get_param_bool("textToPath");
1209 (*i)->set_param_bool("textToPath", sp_export_text_to_path);
1210 }
1211 catch (...) {
1212 g_warning ("Could not set export-text-to-path option for this export.");
1213 }
1215 try {
1216 old_font_embedded = (*i)->get_param_bool("fontEmbedded");
1217 (*i)->set_param_bool("fontEmbedded", sp_export_font);
1218 }
1219 catch (...) {
1220 g_warning ("Could not set export-font option for this export.");
1221 }
1223 try {
1224 old_bbox_page = (*i)->get_param_bool("pageBoundingBox");
1225 (*i)->set_param_bool("pageBoundingBox", sp_export_bbox_page);
1226 }
1227 catch (...) {
1228 g_warning ("Could not set export-bbox-page option for this export.");
1229 }
1231 (*i)->save(doc, uri);
1233 try {
1234 (*i)->set_param_bool("textToPath", old_text_to_path);
1235 (*i)->set_param_bool("fontEmbedded", old_font_embedded);
1236 (*i)->set_param_bool("pageBoundingBox", old_bbox_page);
1237 }
1238 catch (...) {
1240 }
1241 }
1243 /**
1244 * Perform a PDF export
1245 *
1246 * \param doc Document to export.
1247 * \param uri URI to export to.
1248 * \param mime MIME type to export as.
1249 */
1251 static void do_export_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1252 {
1253 Inkscape::Extension::DB::OutputList o;
1254 Inkscape::Extension::db.get_output_list(o);
1255 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1256 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1257 i++;
1258 }
1260 if (i == o.end())
1261 {
1262 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1263 return;
1264 }
1266 if (sp_export_id) {
1267 SPObject *o = doc->getObjectById(sp_export_id);
1268 if (o == NULL) {
1269 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1270 return;
1271 }
1272 (*i)->set_param_string ("exportId", sp_export_id);
1273 } else {
1274 (*i)->set_param_string ("exportId", "");
1275 }
1277 if (sp_export_area_drawing) {
1278 (*i)->set_param_bool ("exportDrawing", TRUE);
1279 } else {
1280 (*i)->set_param_bool ("exportDrawing", FALSE);
1281 }
1283 if (sp_export_area_canvas) {
1284 (*i)->set_param_bool ("exportCanvas", TRUE);
1285 } else {
1286 (*i)->set_param_bool ("exportCanvas", FALSE);
1287 }
1289 if (sp_export_text_to_path) {
1290 (*i)->set_param_bool("textToPath", TRUE);
1291 } else {
1292 (*i)->set_param_bool("textToPath", FALSE);
1293 }
1295 (*i)->save(doc, uri);
1296 }
1298 #ifdef WIN32
1299 /**
1300 * Export a document to EMF
1301 *
1302 * \param doc Document to export.
1303 * \param uri URI to export to.
1304 * \param mime MIME type to export as (should be "image/x-emf")
1305 */
1307 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1308 {
1309 Inkscape::Extension::DB::OutputList o;
1310 Inkscape::Extension::db.get_output_list(o);
1311 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1312 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1313 i++;
1314 }
1316 if (i == o.end())
1317 {
1318 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1319 return;
1320 }
1322 (*i)->save(doc, uri);
1323 }
1324 #endif //WIN32
1326 #ifdef WIN32
1327 bool replaceArgs( int& argc, char**& argv )
1328 {
1329 bool worked = false;
1331 #ifdef REPLACEARGS_DEBUG
1332 MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1333 #endif // REPLACEARGS_DEBUG
1335 wchar_t* line = GetCommandLineW();
1336 if ( line )
1337 {
1338 #ifdef REPLACEARGS_DEBUG
1339 {
1340 gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1341 if ( utf8Line )
1342 {
1343 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1344 {
1345 char tmp[strlen(safe) + 32];
1346 snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1347 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1348 }
1349 }
1350 }
1351 #endif // REPLACEARGS_DEBUG
1353 int numArgs = 0;
1354 wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1356 #ifdef REPLACEARGS_ANSI
1357 // test code for trying things on Win95/98/ME
1358 if ( !parsed )
1359 {
1360 #ifdef REPLACEARGS_DEBUG
1361 MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1362 #endif // REPLACEARGS_DEBUG
1363 int lineLen = wcslen(line) + 1;
1364 wchar_t* lineDup = new wchar_t[lineLen];
1365 wcsncpy( lineDup, line, lineLen );
1367 int pos = 0;
1368 bool inQuotes = false;
1369 bool inWhitespace = true;
1370 std::vector<int> places;
1371 while ( lineDup[pos] )
1372 {
1373 if ( inQuotes )
1374 {
1375 if ( lineDup[pos] == L'"' )
1376 {
1377 inQuotes = false;
1378 }
1379 }
1380 else if ( lineDup[pos] == L'"' )
1381 {
1382 inQuotes = true;
1383 inWhitespace = false;
1384 places.push_back(pos);
1385 }
1386 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1387 {
1388 if ( !inWhitespace )
1389 {
1390 inWhitespace = true;
1391 lineDup[pos] = 0;
1392 }
1393 }
1394 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1395 {
1396 inWhitespace = false;
1397 places.push_back(pos);
1398 }
1399 else
1400 {
1401 // consume
1402 }
1403 pos++;
1404 }
1405 #ifdef REPLACEARGS_DEBUG
1406 {
1407 char tmp[256];
1408 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1409 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1410 }
1411 #endif // REPLACEARGS_DEBUG
1413 wchar_t** block = new wchar_t*[places.size()];
1414 int i = 0;
1415 for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1416 {
1417 block[i++] = &lineDup[*it];
1418 }
1419 parsed = block;
1420 numArgs = places.size();
1421 }
1422 #endif // REPLACEARGS_ANSI
1424 if ( parsed )
1425 {
1426 std::vector<wchar_t*>expandedArgs;
1427 if ( numArgs > 0 )
1428 {
1429 expandedArgs.push_back( parsed[0] );
1430 }
1432 for ( int i1 = 1; i1 < numArgs; i1++ )
1433 {
1434 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1435 wildcarded &= parsed[i1][0] != L'"';
1436 wildcarded &= parsed[i1][0] != L'-';
1437 if ( wildcarded )
1438 {
1439 #ifdef REPLACEARGS_ANSI
1440 WIN32_FIND_DATAA data;
1441 #else
1442 WIN32_FIND_DATAW data;
1443 #endif // REPLACEARGS_ANSI
1445 memset((void *)&data, 0, sizeof(data));
1447 int baseLen = wcslen(parsed[i1]) + 2;
1448 wchar_t* base = new wchar_t[baseLen];
1449 wcsncpy( base, parsed[i1], baseLen );
1450 wchar_t* last = wcsrchr( base, L'\\' );
1451 if ( last )
1452 {
1453 last[1] = 0;
1454 }
1455 else
1456 {
1457 base[0] = 0;
1458 }
1459 baseLen = wcslen( base );
1461 #ifdef REPLACEARGS_ANSI
1462 char target[MAX_PATH];
1463 if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1464 {
1465 HANDLE hf = FindFirstFileA( target, &data );
1466 #else
1467 HANDLE hf = FindFirstFileW( parsed[i1], &data );
1468 #endif // REPLACEARGS_ANSI
1469 if ( hf != INVALID_HANDLE_VALUE )
1470 {
1471 BOOL found = TRUE;
1472 do
1473 {
1474 #ifdef REPLACEARGS_ANSI
1475 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1476 if ( howMany > 0 )
1477 {
1478 howMany += baseLen;
1479 wchar_t* tmp = new wchar_t[howMany + 1];
1480 wcsncpy( tmp, base, howMany + 1 );
1481 MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1482 expandedArgs.push_back( tmp );
1483 found = FindNextFileA( hf, &data );
1484 }
1485 #else
1486 int howMany = wcslen(data.cFileName) + baseLen;
1487 wchar_t* tmp = new wchar_t[howMany + 1];
1488 wcsncpy( tmp, base, howMany + 1 );
1489 wcsncat( tmp, data.cFileName, howMany + 1 );
1490 expandedArgs.push_back( tmp );
1491 found = FindNextFileW( hf, &data );
1492 #endif // REPLACEARGS_ANSI
1493 } while ( found );
1495 FindClose( hf );
1496 }
1497 else
1498 {
1499 expandedArgs.push_back( parsed[i1] );
1500 }
1501 #ifdef REPLACEARGS_ANSI
1502 }
1503 #endif // REPLACEARGS_ANSI
1505 delete[] base;
1506 }
1507 else
1508 {
1509 expandedArgs.push_back( parsed[i1] );
1510 }
1511 }
1513 {
1514 wchar_t** block = new wchar_t*[expandedArgs.size()];
1515 int iz = 0;
1516 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1517 {
1518 block[iz++] = *it;
1519 }
1520 parsed = block;
1521 numArgs = expandedArgs.size();
1522 }
1524 std::vector<gchar*> newArgs;
1525 for ( int i = 0; i < numArgs; i++ )
1526 {
1527 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1528 if ( replacement )
1529 {
1530 #ifdef REPLACEARGS_DEBUG
1531 gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1533 if ( safe2 )
1534 {
1535 {
1536 char tmp[1024];
1537 snprintf( tmp, sizeof(tmp), " [%2d] = '%s'", i, safe2 );
1538 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1539 }
1540 g_free( safe2 );
1541 }
1542 #endif // REPLACEARGS_DEBUG
1544 newArgs.push_back( replacement );
1545 }
1546 else
1547 {
1548 newArgs.push_back( blankParam );
1549 }
1550 }
1552 // Now push our munged params to be the new argv and argc
1553 {
1554 char** block = new char*[newArgs.size()];
1555 int iz = 0;
1556 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1557 {
1558 block[iz++] = *it;
1559 }
1560 argv = block;
1561 argc = newArgs.size();
1562 worked = true;
1563 }
1564 }
1565 #ifdef REPLACEARGS_DEBUG
1566 else
1567 {
1568 MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1569 }
1570 #endif // REPLACEARGS_DEBUG
1571 }
1572 #ifdef REPLACEARGS_DEBUG
1573 else
1574 {
1575 {
1576 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1577 }
1579 char* line2 = GetCommandLineA();
1580 if ( line2 )
1581 {
1582 gchar *safe = Inkscape::IO::sanitizeString(line2);
1583 {
1584 {
1585 char tmp[strlen(safe) + 32];
1586 snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1587 MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1588 }
1589 }
1590 }
1591 else
1592 {
1593 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1594 }
1595 }
1596 #endif // REPLACEARGS_DEBUG
1598 return worked;
1599 }
1600 #endif // WIN32
1602 static GSList *
1603 sp_process_args(poptContext ctx)
1604 {
1605 GSList *fl = NULL;
1607 gint a;
1608 while ((a = poptGetNextOpt(ctx)) >= 0) {
1609 switch (a) {
1610 case SP_ARG_FILE: {
1611 gchar const *fn = poptGetOptArg(ctx);
1612 if (fn != NULL) {
1613 fl = g_slist_append(fl, g_strdup(fn));
1614 }
1615 break;
1616 }
1617 case SP_ARG_VERSION: {
1618 printf("Inkscape %s (%s)\n", INKSCAPE_VERSION, __DATE__);
1619 exit(0);
1620 break;
1621 }
1622 case SP_ARG_EXTENSIONDIR: {
1623 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1624 exit(0);
1625 break;
1626 }
1627 case SP_ARG_VERB_LIST: {
1628 // This really shouldn't go here, we should init the app.
1629 // But, since we're just exiting in this path, there is
1630 // no harm, and this is really a better place to put
1631 // everything else.
1632 Inkscape::Extension::init();
1633 Inkscape::Verb::list();
1634 exit(0);
1635 break;
1636 }
1637 case SP_ARG_VERB:
1638 case SP_ARG_SELECT: {
1639 gchar const *arg = poptGetOptArg(ctx);
1640 if (arg != NULL) {
1641 // printf("Adding in: %s\n", arg);
1642 new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1643 }
1644 break;
1645 }
1646 default: {
1647 break;
1648 }
1649 }
1650 }
1652 gchar const ** const args = poptGetArgs(ctx);
1653 if (args != NULL) {
1654 for (unsigned i = 0; args[i] != NULL; i++) {
1655 fl = g_slist_append(fl, g_strdup(args[i]));
1656 }
1657 }
1659 return fl;
1660 }
1663 /*
1664 Local Variables:
1665 mode:c++
1666 c-file-style:"stroustrup"
1667 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1668 indent-tabs-mode:nil
1669 fill-column:99
1670 End:
1671 */
1672 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :