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