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