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 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
841 gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
842 Inkscape::Debug::log_display_config();
844 // Set default window icon. Obeys the theme.
845 gtk_window_set_default_icon_name("inkscape");
846 // Do things that were previously in inkscape_gtk_stock_init().
847 sp_icon_get_phys_size(GTK_ICON_SIZE_MENU);
848 Inkscape::UI::Widget::Panel::prep();
850 gboolean create_new = TRUE;
852 /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
853 inkscape_application_init(argv[0], true);
855 while (fl) {
856 if (sp_file_open((gchar *)fl->data,NULL)) {
857 create_new=FALSE;
858 }
859 fl = g_slist_remove(fl, fl->data);
860 }
861 if (create_new) {
862 sp_file_new_default();
863 }
865 Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
866 main_instance.run();
868 #ifdef WIN32
869 //We might not need anything here
870 //sp_win32_finish(); <-- this is a NOP func
871 #endif
873 return 0;
874 }
876 /**
877 * Process file list
878 */
879 void sp_process_file_list(GSList *fl)
880 {
881 while (fl) {
882 const gchar *filename = (gchar *)fl->data;
883 SPDocument *doc = Inkscape::Extension::open(NULL, filename);
884 if (doc == NULL) {
885 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
886 }
887 if (doc == NULL) {
888 g_warning("Specified document %s cannot be opened (is it a valid SVG file?)", filename);
889 } else {
890 if (sp_vacuum_defs) {
891 vacuum_document(doc);
892 }
893 if (sp_vacuum_defs && !sp_export_svg) {
894 // save under the name given in the command line
895 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
896 }
897 if (sp_global_printer) {
898 sp_print_document_to_file(doc, sp_global_printer);
899 }
900 if (sp_export_png) {
901 sp_do_export_png(doc);
902 }
903 if (sp_export_svg) {
904 Inkscape::XML::Document *rdoc;
905 Inkscape::XML::Node *repr;
906 rdoc = sp_repr_document_new("svg:svg");
907 repr = rdoc->root();
908 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
909 sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
910 }
911 if (sp_export_ps) {
912 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
913 }
914 if (sp_export_eps) {
915 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
916 }
917 if (sp_export_pdf) {
918 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
919 }
920 #ifdef WIN32
921 if (sp_export_emf) {
922 do_export_emf(doc, sp_export_emf, "image/x-emf");
923 }
924 #endif //WIN32
925 if (sp_query_all) {
926 do_query_all (doc);
927 } else if (sp_query_width || sp_query_height) {
928 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
929 } else if (sp_query_x || sp_query_y) {
930 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
931 }
933 delete doc;
934 }
935 fl = g_slist_remove(fl, fl->data);
936 }
937 }
939 /**
940 * Run the application as an interactive shell, parsing command lines from stdin
941 * Returns -1 on error.
942 */
943 int sp_main_shell(char const* command_name)
944 {
945 int retval = 0;
947 const unsigned int buffer_size = 4096;
948 gchar *command_line = g_strnfill(buffer_size, 0);
949 g_strlcpy(command_line, command_name, buffer_size);
950 gsize offset = g_strlcat(command_line, " ", buffer_size);
951 gsize sizeLeft = buffer_size - offset;
952 gchar *useme = command_line + offset;
954 fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
955 fflush(stdout);
956 char* linedata = 0;
957 do {
958 fprintf(stdout, ">");
959 fflush(stdout);
960 if ((linedata = fgets(useme, sizeLeft, stdin))) {
961 size_t len = strlen(useme);
962 if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
963 fprintf(stdout, "ERROR: Command line too long\n");
964 // Consume rest of line
965 retval = -1; // If the while loop completes, this remains -1
966 while (fgets(useme, sizeLeft, stdin) && retval) {
967 len = strlen(command_line);
968 if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
969 retval = 0;
970 }
971 }
972 } else {
973 useme[--len] = '\0'; // Strip newline
974 if (useme[len - 1] == '\r') {
975 useme[--len] = '\0';
976 }
977 if ( strcmp(useme, "quit") == 0 ) {
978 // Time to quit
979 fflush(stdout);
980 linedata = 0; // mark for exit
981 } else if ( len < 1 ) {
982 // blank string. Do nothing.
983 } else {
984 GError* parseError = 0;
985 gchar** argv = 0;
986 gint argc = 0;
987 if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
988 poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
989 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
990 if ( ctx ) {
991 GSList *fl = sp_process_args(ctx);
992 sp_process_file_list(fl);
993 poptFreeContext(ctx);
994 } else {
995 retval = 1; // not sure why. But this was the previous return value
996 }
997 resetCommandlineGlobals();
998 g_strfreev(argv);
999 } else {
1000 g_warning("Cannot parse commandline: %s", useme);
1001 }
1002 }
1003 }
1004 } // if (linedata...
1005 } while (linedata && (retval == 0));
1007 g_free(command_line);
1008 return retval;
1009 }
1011 int sp_main_console(int argc, char const **argv)
1012 {
1013 /* We are started in text mode */
1015 /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1016 * in a non-Gtk environment. Used in libnrtype's
1017 * FontInstance.cpp and FontFactory.cpp.
1018 * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1019 */
1020 g_type_init();
1021 char **argv2 = const_cast<char **>(argv);
1022 gtk_init_check( &argc, &argv2 );
1023 //setlocale(LC_ALL, "");
1025 GSList *fl = NULL;
1026 int retVal = sp_common_main( argc, argv, &fl );
1027 g_return_val_if_fail(retVal == 0, 1);
1029 if (fl == NULL && !sp_shell) {
1030 g_print("Nothing to do!\n");
1031 exit(0);
1032 }
1034 inkscape_application_init(argv[0], false);
1036 if (sp_shell) {
1037 sp_main_shell(argv[0]); // Run as interactive shell
1038 exit(0);
1039 } else {
1040 sp_process_file_list(fl); // Normal command line invokation
1041 }
1043 return 0;
1044 }
1046 static void
1047 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1048 {
1049 SPObject *o = NULL;
1051 if (id) {
1052 o = doc->getObjectById(id);
1053 if (o) {
1054 if (!SP_IS_ITEM (o)) {
1055 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1056 return;
1057 }
1058 } else {
1059 g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1060 return;
1061 }
1062 } else {
1063 o = SP_DOCUMENT_ROOT(doc);
1064 }
1066 if (o) {
1067 sp_document_ensure_up_to_date (doc);
1068 SPItem *item = ((SPItem *) o);
1070 // "true" SVG bbox for scripting
1071 Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1072 if (area) {
1073 Inkscape::SVGOStringStream os;
1074 if (extent) {
1075 os << area->dimensions()[axis];
1076 } else {
1077 os << area->min()[axis];
1078 }
1079 g_print ("%s", os.str().c_str());
1080 } else {
1081 g_print("0");
1082 }
1083 }
1084 }
1086 static void
1087 do_query_all (SPDocument *doc)
1088 {
1089 SPObject *o = NULL;
1091 o = SP_DOCUMENT_ROOT(doc);
1093 if (o) {
1094 sp_document_ensure_up_to_date (doc);
1095 do_query_all_recurse(o);
1096 }
1097 }
1099 static void
1100 do_query_all_recurse (SPObject *o)
1101 {
1102 SPItem *item = ((SPItem *) o);
1103 if (o->id && SP_IS_ITEM(item)) {
1104 Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1105 if (area) {
1106 Inkscape::SVGOStringStream os;
1107 os << o->id;
1108 os << "," << area->min()[Geom::X];
1109 os << "," << area->min()[Geom::Y];
1110 os << "," << area->dimensions()[Geom::X];
1111 os << "," << area->dimensions()[Geom::Y];
1112 g_print ("%s\n", os.str().c_str());
1113 }
1114 }
1116 SPObject *child = o->children;
1117 while (child) {
1118 do_query_all_recurse (child);
1119 child = child->next;
1120 }
1121 }
1124 static void
1125 sp_do_export_png(SPDocument *doc)
1126 {
1127 const gchar *filename = NULL;
1128 gdouble dpi = 0.0;
1130 if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1131 g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1132 }
1134 GSList *items = NULL;
1136 Geom::Rect area;
1137 if (sp_export_id || sp_export_area_drawing) {
1139 SPObject *o = NULL;
1140 SPObject *o_area = NULL;
1141 if (sp_export_id && sp_export_area_drawing) {
1142 o = doc->getObjectById(sp_export_id);
1143 o_area = SP_DOCUMENT_ROOT (doc);
1144 } else if (sp_export_id) {
1145 o = doc->getObjectById(sp_export_id);
1146 o_area = o;
1147 } else if (sp_export_area_drawing) {
1148 o = SP_DOCUMENT_ROOT (doc);
1149 o_area = o;
1150 }
1152 if (o) {
1153 if (!SP_IS_ITEM (o)) {
1154 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1155 return;
1156 }
1158 items = g_slist_prepend (items, SP_ITEM(o));
1160 if (sp_export_id_only) {
1161 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1162 }
1164 if (sp_export_use_hints) {
1166 // retrieve export filename hint
1167 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1168 if (fn_hint) {
1169 if (sp_export_png) {
1170 g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1171 filename = sp_export_png;
1172 } else {
1173 filename = fn_hint;
1174 }
1175 } else {
1176 g_warning ("Export filename hint not found for the object.");
1177 filename = sp_export_png;
1178 }
1180 // retrieve export dpi hints
1181 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1182 if (dpi_hint) {
1183 if (sp_export_dpi || sp_export_width || sp_export_height) {
1184 g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1185 } else {
1186 dpi = atof(dpi_hint);
1187 }
1188 } else {
1189 g_warning ("Export DPI hint not found for the object.");
1190 }
1192 }
1194 // write object bbox to area
1195 sp_document_ensure_up_to_date (doc);
1196 Geom::OptRect areaMaybe;
1197 sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1198 if (areaMaybe) {
1199 area = *areaMaybe;
1200 } else {
1201 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1202 return;
1203 }
1204 } else {
1205 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1206 return;
1207 }
1208 }
1210 if (sp_export_area) {
1211 /* Try to parse area (given in SVG pixels) */
1212 gdouble x0,y0,x1,y1;
1213 if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1214 g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1215 return;
1216 }
1217 area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1218 } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1219 /* Export the whole canvas */
1220 sp_document_ensure_up_to_date (doc);
1221 Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1222 area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1223 }
1225 // set filename and dpi from options, if not yet set from the hints
1226 if (!filename) {
1227 if (!sp_export_png) {
1228 g_warning ("No export filename given and no filename hint. Nothing exported.");
1229 return;
1230 }
1231 filename = sp_export_png;
1232 }
1234 if (sp_export_dpi && dpi == 0.0) {
1235 dpi = atof(sp_export_dpi);
1236 if ((dpi < 0.1) || (dpi > 10000.0)) {
1237 g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1238 return;
1239 }
1240 g_print("DPI: %g\n", dpi);
1241 }
1243 if (sp_export_area_snap) {
1244 round_rectangle_outwards(area);
1245 }
1247 // default dpi
1248 if (dpi == 0.0) {
1249 dpi = PX_PER_IN;
1250 }
1252 unsigned long int width = 0;
1253 unsigned long int height = 0;
1255 if (sp_export_width) {
1256 errno=0;
1257 width = strtoul(sp_export_width, NULL, 0);
1258 if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1259 g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1260 return;
1261 }
1262 dpi = (gdouble) width * PX_PER_IN / area.width();
1263 }
1265 if (sp_export_height) {
1266 errno=0;
1267 height = strtoul(sp_export_height, NULL, 0);
1268 if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1269 g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1270 return;
1271 }
1272 dpi = (gdouble) height * PX_PER_IN / area.height();
1273 }
1275 if (!sp_export_width) {
1276 width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1277 }
1279 if (!sp_export_height) {
1280 height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1281 }
1283 guint32 bgcolor = 0x00000000;
1284 if (sp_export_background) {
1285 // override the page color
1286 bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1287 bgcolor |= 0xff; // default is no opacity
1288 } else {
1289 // read from namedview
1290 Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1291 if (nv && nv->attribute("pagecolor"))
1292 bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1293 if (nv && nv->attribute("inkscape:pageopacity"))
1294 bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1295 }
1297 if (sp_export_background_opacity) {
1298 // override opacity
1299 gfloat value;
1300 if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1301 if (value > 1.0) {
1302 value = CLAMP (value, 1.0f, 255.0f);
1303 bgcolor &= (guint32) 0xffffff00;
1304 bgcolor |= (guint32) floor(value);
1305 } else {
1306 value = CLAMP (value, 0.0f, 1.0f);
1307 bgcolor &= (guint32) 0xffffff00;
1308 bgcolor |= SP_COLOR_F_TO_U(value);
1309 }
1310 }
1311 }
1313 g_print("Background RRGGBBAA: %08x\n", bgcolor);
1315 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);
1317 g_print("Bitmap saved as: %s\n", filename);
1319 if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1320 sp_export_png_file(doc, filename, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1321 } else {
1322 g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1323 }
1325 g_slist_free (items);
1326 }
1329 /**
1330 * Perform a PDF/PS/EPS export
1331 *
1332 * \param doc Document to export.
1333 * \param uri URI to export to.
1334 * \param mime MIME type to export as.
1335 */
1337 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1338 {
1339 Inkscape::Extension::DB::OutputList o;
1340 Inkscape::Extension::db.get_output_list(o);
1341 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1342 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1343 i++;
1344 }
1346 if (i == o.end())
1347 {
1348 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1349 return;
1350 }
1352 if (sp_export_id) {
1353 SPObject *o = doc->getObjectById(sp_export_id);
1354 if (o == NULL) {
1355 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1356 return;
1357 }
1358 (*i)->set_param_string ("exportId", sp_export_id);
1359 } else {
1360 (*i)->set_param_string ("exportId", "");
1361 }
1363 if (sp_export_area_canvas && sp_export_area_drawing) {
1364 g_warning ("You cannot use --export-area-canvas and --export-area-drawing at the same time; only the former will take effect.");
1365 sp_export_area_drawing = false;
1366 }
1368 if (sp_export_area_drawing) {
1369 (*i)->set_param_bool ("areaDrawing", TRUE);
1370 } else {
1371 (*i)->set_param_bool ("areaDrawing", FALSE);
1372 }
1374 if (sp_export_area_canvas) {
1375 if (sp_export_eps) {
1376 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.");
1377 }
1378 (*i)->set_param_bool ("areaCanvas", TRUE);
1379 } else {
1380 (*i)->set_param_bool ("areaCanvas", FALSE);
1381 }
1383 if (!sp_export_area_drawing && !sp_export_area_canvas && !sp_export_id) {
1384 // neither is set, set canvas as default for ps/pdf and drawing for eps
1385 if (sp_export_eps) {
1386 try {
1387 (*i)->set_param_bool("areaDrawing", TRUE);
1388 } catch (...) {}
1389 }
1390 }
1392 if (sp_export_text_to_path) {
1393 (*i)->set_param_bool("textToPath", TRUE);
1394 } else {
1395 (*i)->set_param_bool("textToPath", FALSE);
1396 }
1398 if (sp_export_ignore_filters) {
1399 (*i)->set_param_bool("blurToBitmap", FALSE);
1400 } else {
1401 (*i)->set_param_bool("blurToBitmap", TRUE);
1402 }
1404 (*i)->save(doc, uri);
1405 }
1407 #ifdef WIN32
1408 /**
1409 * Export a document to EMF
1410 *
1411 * \param doc Document to export.
1412 * \param uri URI to export to.
1413 * \param mime MIME type to export as (should be "image/x-emf")
1414 */
1416 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1417 {
1418 Inkscape::Extension::DB::OutputList o;
1419 Inkscape::Extension::db.get_output_list(o);
1420 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1421 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1422 i++;
1423 }
1425 if (i == o.end())
1426 {
1427 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1428 return;
1429 }
1431 (*i)->save(doc, uri);
1432 }
1433 #endif //WIN32
1435 #ifdef WIN32
1436 bool replaceArgs( int& argc, char**& argv )
1437 {
1438 bool worked = false;
1440 #ifdef REPLACEARGS_DEBUG
1441 MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1442 #endif // REPLACEARGS_DEBUG
1444 wchar_t* line = GetCommandLineW();
1445 if ( line )
1446 {
1447 #ifdef REPLACEARGS_DEBUG
1448 {
1449 gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1450 if ( utf8Line )
1451 {
1452 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1453 {
1454 char tmp[strlen(safe) + 32];
1455 snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1456 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1457 }
1458 }
1459 }
1460 #endif // REPLACEARGS_DEBUG
1462 int numArgs = 0;
1463 wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1465 #ifdef REPLACEARGS_ANSI
1466 // test code for trying things on Win95/98/ME
1467 if ( !parsed )
1468 {
1469 #ifdef REPLACEARGS_DEBUG
1470 MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1471 #endif // REPLACEARGS_DEBUG
1472 int lineLen = wcslen(line) + 1;
1473 wchar_t* lineDup = new wchar_t[lineLen];
1474 wcsncpy( lineDup, line, lineLen );
1476 int pos = 0;
1477 bool inQuotes = false;
1478 bool inWhitespace = true;
1479 std::vector<int> places;
1480 while ( lineDup[pos] )
1481 {
1482 if ( inQuotes )
1483 {
1484 if ( lineDup[pos] == L'"' )
1485 {
1486 inQuotes = false;
1487 }
1488 }
1489 else if ( lineDup[pos] == L'"' )
1490 {
1491 inQuotes = true;
1492 inWhitespace = false;
1493 places.push_back(pos);
1494 }
1495 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1496 {
1497 if ( !inWhitespace )
1498 {
1499 inWhitespace = true;
1500 lineDup[pos] = 0;
1501 }
1502 }
1503 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1504 {
1505 inWhitespace = false;
1506 places.push_back(pos);
1507 }
1508 else
1509 {
1510 // consume
1511 }
1512 pos++;
1513 }
1514 #ifdef REPLACEARGS_DEBUG
1515 {
1516 char tmp[256];
1517 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1518 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1519 }
1520 #endif // REPLACEARGS_DEBUG
1522 wchar_t** block = new wchar_t*[places.size()];
1523 int i = 0;
1524 for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1525 {
1526 block[i++] = &lineDup[*it];
1527 }
1528 parsed = block;
1529 numArgs = places.size();
1530 }
1531 #endif // REPLACEARGS_ANSI
1533 if ( parsed )
1534 {
1535 std::vector<wchar_t*>expandedArgs;
1536 if ( numArgs > 0 )
1537 {
1538 expandedArgs.push_back( parsed[0] );
1539 }
1541 for ( int i1 = 1; i1 < numArgs; i1++ )
1542 {
1543 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1544 wildcarded &= parsed[i1][0] != L'"';
1545 wildcarded &= parsed[i1][0] != L'-';
1546 if ( wildcarded )
1547 {
1548 #ifdef REPLACEARGS_ANSI
1549 WIN32_FIND_DATAA data;
1550 #else
1551 WIN32_FIND_DATAW data;
1552 #endif // REPLACEARGS_ANSI
1554 memset((void *)&data, 0, sizeof(data));
1556 int baseLen = wcslen(parsed[i1]) + 2;
1557 wchar_t* base = new wchar_t[baseLen];
1558 wcsncpy( base, parsed[i1], baseLen );
1559 wchar_t* last = wcsrchr( base, L'\\' );
1560 if ( last )
1561 {
1562 last[1] = 0;
1563 }
1564 else
1565 {
1566 base[0] = 0;
1567 }
1568 baseLen = wcslen( base );
1570 #ifdef REPLACEARGS_ANSI
1571 char target[MAX_PATH];
1572 if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1573 {
1574 HANDLE hf = FindFirstFileA( target, &data );
1575 #else
1576 HANDLE hf = FindFirstFileW( parsed[i1], &data );
1577 #endif // REPLACEARGS_ANSI
1578 if ( hf != INVALID_HANDLE_VALUE )
1579 {
1580 BOOL found = TRUE;
1581 do
1582 {
1583 #ifdef REPLACEARGS_ANSI
1584 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1585 if ( howMany > 0 )
1586 {
1587 howMany += baseLen;
1588 wchar_t* tmp = new wchar_t[howMany + 1];
1589 wcsncpy( tmp, base, howMany + 1 );
1590 MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1591 expandedArgs.push_back( tmp );
1592 found = FindNextFileA( hf, &data );
1593 }
1594 #else
1595 int howMany = wcslen(data.cFileName) + baseLen;
1596 wchar_t* tmp = new wchar_t[howMany + 1];
1597 wcsncpy( tmp, base, howMany + 1 );
1598 wcsncat( tmp, data.cFileName, howMany + 1 );
1599 expandedArgs.push_back( tmp );
1600 found = FindNextFileW( hf, &data );
1601 #endif // REPLACEARGS_ANSI
1602 } while ( found );
1604 FindClose( hf );
1605 }
1606 else
1607 {
1608 expandedArgs.push_back( parsed[i1] );
1609 }
1610 #ifdef REPLACEARGS_ANSI
1611 }
1612 #endif // REPLACEARGS_ANSI
1614 delete[] base;
1615 }
1616 else
1617 {
1618 expandedArgs.push_back( parsed[i1] );
1619 }
1620 }
1622 {
1623 wchar_t** block = new wchar_t*[expandedArgs.size()];
1624 int iz = 0;
1625 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1626 {
1627 block[iz++] = *it;
1628 }
1629 parsed = block;
1630 numArgs = expandedArgs.size();
1631 }
1633 std::vector<gchar*> newArgs;
1634 for ( int i = 0; i < numArgs; i++ )
1635 {
1636 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1637 if ( replacement )
1638 {
1639 #ifdef REPLACEARGS_DEBUG
1640 gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1642 if ( safe2 )
1643 {
1644 {
1645 char tmp[1024];
1646 snprintf( tmp, sizeof(tmp), " [%2d] = '%s'", i, safe2 );
1647 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1648 }
1649 g_free( safe2 );
1650 }
1651 #endif // REPLACEARGS_DEBUG
1653 newArgs.push_back( replacement );
1654 }
1655 else
1656 {
1657 newArgs.push_back( blankParam );
1658 }
1659 }
1661 // Now push our munged params to be the new argv and argc
1662 {
1663 char** block = new char*[newArgs.size()];
1664 int iz = 0;
1665 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1666 {
1667 block[iz++] = *it;
1668 }
1669 argv = block;
1670 argc = newArgs.size();
1671 worked = true;
1672 }
1673 }
1674 #ifdef REPLACEARGS_DEBUG
1675 else
1676 {
1677 MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1678 }
1679 #endif // REPLACEARGS_DEBUG
1680 }
1681 #ifdef REPLACEARGS_DEBUG
1682 else
1683 {
1684 {
1685 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1686 }
1688 char* line2 = GetCommandLineA();
1689 if ( line2 )
1690 {
1691 gchar *safe = Inkscape::IO::sanitizeString(line2);
1692 {
1693 {
1694 char tmp[strlen(safe) + 32];
1695 snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1696 MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1697 }
1698 }
1699 }
1700 else
1701 {
1702 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1703 }
1704 }
1705 #endif // REPLACEARGS_DEBUG
1707 return worked;
1708 }
1709 #endif // WIN32
1711 static GSList *
1712 sp_process_args(poptContext ctx)
1713 {
1714 GSList *fl = NULL;
1716 gint a;
1717 while ((a = poptGetNextOpt(ctx)) != -1) {
1718 switch (a) {
1719 case SP_ARG_FILE: {
1720 gchar const *fn = poptGetOptArg(ctx);
1721 if (fn != NULL) {
1722 fl = g_slist_append(fl, g_strdup(fn));
1723 }
1724 break;
1725 }
1726 case SP_ARG_VERSION: {
1727 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1728 exit(0);
1729 break;
1730 }
1731 case SP_ARG_EXTENSIONDIR: {
1732 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1733 exit(0);
1734 break;
1735 }
1736 case SP_ARG_VERB_LIST: {
1737 // This really shouldn't go here, we should init the app.
1738 // But, since we're just exiting in this path, there is
1739 // no harm, and this is really a better place to put
1740 // everything else.
1741 Inkscape::Extension::init();
1742 Inkscape::Verb::list();
1743 exit(0);
1744 break;
1745 }
1746 case SP_ARG_VERB:
1747 case SP_ARG_SELECT: {
1748 gchar const *arg = poptGetOptArg(ctx);
1749 if (arg != NULL) {
1750 // printf("Adding in: %s\n", arg);
1751 new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1752 }
1753 break;
1754 }
1755 case POPT_ERROR_BADOPT: {
1756 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1757 exit(1);
1758 break;
1759 }
1760 default: {
1761 break;
1762 }
1763 }
1764 }
1766 gchar const ** const args = poptGetArgs(ctx);
1767 if (args != NULL) {
1768 for (unsigned i = 0; args[i] != NULL; i++) {
1769 fl = g_slist_append(fl, g_strdup(args[i]));
1770 }
1771 }
1773 return fl;
1774 }
1777 /*
1778 Local Variables:
1779 mode:c++
1780 c-file-style:"stroustrup"
1781 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1782 indent-tabs-mode:nil
1783 fill-column:99
1784 End:
1785 */
1786 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :