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