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>
94 #include <extension/input.h>
96 #ifdef WIN32
97 //#define REPLACEARGS_ANSI
98 //#define REPLACEARGS_DEBUG
100 #include "registrytool.h"
102 #include "extension/internal/win32.h"
103 using Inkscape::Extension::Internal::PrintWin32;
105 #endif // WIN32
107 #include "extension/init.h"
109 #include <glibmm/i18n.h>
110 #include <gtkmm/main.h>
112 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
113 #define bind_textdomain_codeset(p,c)
114 #endif
116 #include "application/application.h"
117 #include "main-cmdlineact.h"
118 #include "widgets/icon.h"
119 #include "ui/widget/panel.h"
121 #include <errno.h>
123 enum {
124 SP_ARG_NONE,
125 SP_ARG_NOGUI,
126 SP_ARG_GUI,
127 SP_ARG_FILE,
128 SP_ARG_PRINT,
129 SP_ARG_EXPORT_PNG,
130 SP_ARG_EXPORT_DPI,
131 SP_ARG_EXPORT_AREA,
132 SP_ARG_EXPORT_AREA_DRAWING,
133 SP_ARG_EXPORT_AREA_PAGE,
134 SP_ARG_EXPORT_AREA_SNAP,
135 SP_ARG_EXPORT_WIDTH,
136 SP_ARG_EXPORT_HEIGHT,
137 SP_ARG_EXPORT_ID,
138 SP_ARG_EXPORT_ID_ONLY,
139 SP_ARG_EXPORT_USE_HINTS,
140 SP_ARG_EXPORT_BACKGROUND,
141 SP_ARG_EXPORT_BACKGROUND_OPACITY,
142 SP_ARG_EXPORT_SVG,
143 SP_ARG_EXPORT_PS,
144 SP_ARG_EXPORT_EPS,
145 SP_ARG_EXPORT_PDF,
146 #ifdef WIN32
147 SP_ARG_EXPORT_EMF,
148 #endif //WIN32
149 SP_ARG_EXPORT_TEXT_TO_PATH,
150 SP_ARG_EXPORT_IGNORE_FILTERS,
151 SP_ARG_EXTENSIONDIR,
152 SP_ARG_QUERY_X,
153 SP_ARG_QUERY_Y,
154 SP_ARG_QUERY_WIDTH,
155 SP_ARG_QUERY_HEIGHT,
156 SP_ARG_QUERY_ALL,
157 SP_ARG_QUERY_ID,
158 SP_ARG_SHELL,
159 SP_ARG_VERSION,
160 SP_ARG_VACUUM_DEFS,
161 SP_ARG_VERB_LIST,
162 SP_ARG_VERB,
163 SP_ARG_SELECT,
164 SP_ARG_LAST
165 };
167 int sp_main_gui(int argc, char const **argv);
168 int sp_main_console(int argc, char const **argv);
169 static void sp_do_export_png(SPDocument *doc);
170 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime);
171 #ifdef WIN32
172 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
173 #endif //WIN32
174 static void do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id);
175 static void do_query_all (SPDocument *doc);
176 static void do_query_all_recurse (SPObject *o);
178 static gchar *sp_global_printer = NULL;
179 static gchar *sp_export_png = NULL;
180 static gchar *sp_export_dpi = NULL;
181 static gchar *sp_export_area = NULL;
182 static gboolean sp_export_area_drawing = FALSE;
183 static gboolean sp_export_area_page = FALSE;
184 static gchar *sp_export_width = NULL;
185 static gchar *sp_export_height = NULL;
186 static gchar *sp_export_id = NULL;
187 static gchar *sp_export_background = NULL;
188 static gchar *sp_export_background_opacity = NULL;
189 static gboolean sp_export_area_snap = FALSE;
190 static gboolean sp_export_use_hints = FALSE;
191 static gboolean sp_export_id_only = FALSE;
192 static gchar *sp_export_svg = NULL;
193 static gchar *sp_export_ps = NULL;
194 static gchar *sp_export_eps = NULL;
195 static gchar *sp_export_pdf = NULL;
196 #ifdef WIN32
197 static gchar *sp_export_emf = NULL;
198 #endif //WIN32
199 static gboolean sp_export_text_to_path = FALSE;
200 static gboolean sp_export_ignore_filters = FALSE;
201 static gboolean sp_export_font = FALSE;
202 static gboolean sp_query_x = FALSE;
203 static gboolean sp_query_y = FALSE;
204 static gboolean sp_query_width = FALSE;
205 static gboolean sp_query_height = FALSE;
206 static gboolean sp_query_all = FALSE;
207 static gchar *sp_query_id = NULL;
208 static int sp_new_gui = FALSE;
209 static gboolean sp_shell = FALSE;
210 static gboolean sp_vacuum_defs = FALSE;
212 static gchar *sp_export_png_utf8 = NULL;
213 static gchar *sp_export_svg_utf8 = NULL;
214 static gchar *sp_global_printer_utf8 = NULL;
217 /**
218 * Reset variables to default values.
219 */
220 static void resetCommandlineGlobals() {
221 sp_global_printer = NULL;
222 sp_export_png = NULL;
223 sp_export_dpi = NULL;
224 sp_export_area = NULL;
225 sp_export_area_drawing = FALSE;
226 sp_export_area_page = FALSE;
227 sp_export_width = NULL;
228 sp_export_height = NULL;
229 sp_export_id = NULL;
230 sp_export_background = NULL;
231 sp_export_background_opacity = NULL;
232 sp_export_area_snap = FALSE;
233 sp_export_use_hints = FALSE;
234 sp_export_id_only = FALSE;
235 sp_export_svg = NULL;
236 sp_export_ps = NULL;
237 sp_export_eps = NULL;
238 sp_export_pdf = NULL;
239 #ifdef WIN32
240 sp_export_emf = NULL;
241 #endif //WIN32
242 sp_export_text_to_path = FALSE;
243 sp_export_ignore_filters = FALSE;
244 sp_export_font = FALSE;
245 sp_query_x = FALSE;
246 sp_query_y = FALSE;
247 sp_query_width = FALSE;
248 sp_query_height = FALSE;
249 sp_query_all = FALSE;
250 sp_query_id = NULL;
251 sp_vacuum_defs = FALSE;
253 sp_export_png_utf8 = NULL;
254 sp_export_svg_utf8 = NULL;
255 sp_global_printer_utf8 = NULL;
256 }
258 #ifdef WIN32
259 static bool replaceArgs( int& argc, char**& argv );
260 #endif
261 static GSList *sp_process_args(poptContext ctx);
262 struct poptOption options[] = {
263 {"version", 'V',
264 POPT_ARG_NONE, NULL, SP_ARG_VERSION,
265 N_("Print the Inkscape version number"),
266 NULL},
268 {"without-gui", 'z',
269 POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
270 N_("Do not use X server (only process files from console)"),
271 NULL},
273 {"with-gui", 'g',
274 POPT_ARG_NONE, NULL, SP_ARG_GUI,
275 N_("Try to use X server (even if $DISPLAY is not set)"),
276 NULL},
278 {"file", 'f',
279 POPT_ARG_STRING, NULL, SP_ARG_FILE,
280 N_("Open specified document(s) (option string may be excluded)"),
281 N_("FILENAME")},
283 {"print", 'p',
284 POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
285 N_("Print document(s) to specified output file (use '| program' for pipe)"),
286 N_("FILENAME")},
288 {"export-png", 'e',
289 POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
290 N_("Export document to a PNG file"),
291 N_("FILENAME")},
293 {"export-dpi", 'd',
294 POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
295 N_("Resolution for exporting to bitmap and for rasterization of filters in PS/EPS/PDF (default 90)"),
296 N_("DPI")},
298 {"export-area", 'a',
299 POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
300 N_("Exported area in SVG user units (default is the page; 0,0 is lower-left corner)"),
301 N_("x0:y0:x1:y1")},
303 {"export-area-drawing", 'D',
304 POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
305 N_("Exported area is the entire drawing (not page)"),
306 NULL},
308 {"export-area-page", 'C',
309 POPT_ARG_NONE, &sp_export_area_page, SP_ARG_EXPORT_AREA_PAGE,
310 N_("Exported area is the entire page"),
311 NULL},
313 {"export-area-snap", 0,
314 POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
315 N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
316 NULL},
318 {"export-width", 'w',
319 POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
320 N_("The width of exported bitmap in pixels (overrides export-dpi)"),
321 N_("WIDTH")},
323 {"export-height", 'h',
324 POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
325 N_("The height of exported bitmap in pixels (overrides export-dpi)"),
326 N_("HEIGHT")},
328 {"export-id", 'i',
329 POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
330 N_("The ID of the object to export"),
331 N_("ID")},
333 {"export-id-only", 'j',
334 POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
335 // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
336 // See "man inkscape" for details.
337 N_("Export just the object with export-id, hide all others (only with export-id)"),
338 NULL},
340 {"export-use-hints", 't',
341 POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
342 N_("Use stored filename and DPI hints when exporting (only with export-id)"),
343 NULL},
345 {"export-background", 'b',
346 POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
347 N_("Background color of exported bitmap (any SVG-supported color string)"),
348 N_("COLOR")},
350 {"export-background-opacity", 'y',
351 POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
352 N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
353 N_("VALUE")},
355 {"export-plain-svg", 'l',
356 POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
357 N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
358 N_("FILENAME")},
360 {"export-ps", 'P',
361 POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
362 N_("Export document to a PS file"),
363 N_("FILENAME")},
365 {"export-eps", 'E',
366 POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
367 N_("Export document to an EPS file"),
368 N_("FILENAME")},
370 {"export-pdf", 'A',
371 POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
372 N_("Export document to a PDF file"),
373 N_("FILENAME")},
375 #ifdef WIN32
376 {"export-emf", 'M',
377 POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
378 N_("Export document to an Enhanced Metafile (EMF) File"),
379 N_("FILENAME")},
380 #endif //WIN32
382 {"export-text-to-path", 'T',
383 POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
384 N_("Convert text object to paths on export (PS, EPS, PDF)"),
385 NULL},
387 {"export-ignore-filters", 0,
388 POPT_ARG_NONE, &sp_export_ignore_filters, SP_ARG_EXPORT_IGNORE_FILTERS,
389 N_("Render filtered objects without filters, instead of rasterizing (PS, EPS, PDF)"),
390 NULL},
392 {"query-x", 'X',
393 POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
394 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
395 N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
396 NULL},
398 {"query-y", 'Y',
399 POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
400 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
401 N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
402 NULL},
404 {"query-width", 'W',
405 POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
406 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
407 N_("Query the width of the drawing or, if specified, of the object with --query-id"),
408 NULL},
410 {"query-height", 'H',
411 POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
412 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
413 N_("Query the height of the drawing or, if specified, of the object with --query-id"),
414 NULL},
416 {"query-all", 'S',
417 POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
418 N_("List id,x,y,w,h for all objects"),
419 NULL},
421 {"query-id", 'I',
422 POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
423 N_("The ID of the object whose dimensions are queried"),
424 N_("ID")},
426 {"extension-directory", 'x',
427 POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
428 // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
429 N_("Print out the extension directory and exit"),
430 NULL},
432 {"vacuum-defs", 0,
433 POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
434 N_("Remove unused definitions from the defs section(s) of the document"),
435 NULL},
437 {"verb-list", 0,
438 POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
439 N_("List the IDs of all the verbs in Inkscape"),
440 NULL},
442 {"verb", 0,
443 POPT_ARG_STRING, NULL, SP_ARG_VERB,
444 N_("Verb to call when Inkscape opens."),
445 N_("VERB-ID")},
447 {"select", 0,
448 POPT_ARG_STRING, NULL, SP_ARG_SELECT,
449 N_("Object ID to select when Inkscape opens."),
450 N_("OBJECT-ID")},
452 {"shell", 0,
453 POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
454 N_("Start Inkscape in interactive shell mode."),
455 NULL},
457 POPT_AUTOHELP POPT_TABLEEND
458 };
460 static bool needToRecodeParams = true;
461 gchar * blankParam = g_strdup("");
465 #ifdef WIN32
467 /**
468 * Return the directory of the .exe that is currently running
469 */
470 static Glib::ustring _win32_getExePath()
471 {
472 char exeName[MAX_PATH+1];
473 // TODO these should use xxxW() calls explicitly and convert UTF-16 <--> UTF-8
474 GetModuleFileName(NULL, exeName, MAX_PATH);
475 char *slashPos = strrchr(exeName, '\\');
476 if (slashPos) {
477 *slashPos = '\0';
478 }
479 Glib::ustring s = exeName;
480 return s;
481 }
483 /**
484 * Set up the PATH and PYTHONPATH environment variables on
485 * win32
486 */
487 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
488 {
489 // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
491 char *oldenv = getenv("PATH");
492 Glib::ustring tmp = "PATH=";
493 tmp += exePath;
494 tmp += ";";
495 tmp += exePath;
496 tmp += "\\python;";
497 tmp += exePath;
498 tmp += "\\python\\Scripts;"; // for uniconv.cmd
499 tmp += exePath;
500 tmp += "\\perl";
501 if(oldenv != NULL) {
502 tmp += ";";
503 tmp += oldenv;
504 }
505 _putenv(tmp.c_str());
507 oldenv = getenv("PYTHONPATH");
508 tmp = "PYTHONPATH=";
509 tmp += exePath;
510 tmp += "\\python;";
511 tmp += exePath;
512 tmp += "\\python\\Lib;";
513 tmp += exePath;
514 tmp += "\\python\\DLLs";
515 if(oldenv != NULL) {
516 tmp += ";";
517 tmp += oldenv;
518 }
519 _putenv(tmp.c_str());
521 return 0;
522 }
523 #endif
525 /**
526 * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
527 * can find inkex.py et al. (Bug #197475)
528 */
529 static int set_extensions_env()
530 {
531 char *oldenv = getenv("PYTHONPATH");
532 Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
533 if (oldenv != NULL) {
534 tmp += G_SEARCHPATH_SEPARATOR;
535 tmp += oldenv;
536 }
537 g_setenv("PYTHONPATH", tmp.c_str(), TRUE);
539 return 0;
540 }
543 /**
544 * This is the classic main() entry point of the program, though on some
545 * architectures it might be called by something else.
546 */
547 int
548 main(int argc, char **argv)
549 {
550 #ifdef HAVE_FPSETMASK
551 /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__. It's probably
552 safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
553 the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
554 fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
555 #endif
557 #ifdef WIN32
558 /*
559 Set the current directory to the directory of the
560 executable. This seems redundant, but is needed for
561 when inkscape.exe is executed from another directory.
562 We use relative paths on win32.
563 HKCR\svgfile\shell\open\command is a good example
564 */
565 Glib::ustring homedir = _win32_getExePath();
566 // TODO these should use xxxW() calls explicitly and convert UTF-16 <--> UTF-8
567 SetCurrentDirectory(homedir.c_str());
568 _win32_set_inkscape_env(homedir);
569 RegistryTool rt;
570 rt.setPathInfo();
571 #endif
573 // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
574 Gtk::Main::init_gtkmm_internals();
576 // Bug #197475
577 set_extensions_env();
579 /**
580 * Call bindtextdomain() for various machines's paths
581 */
582 #ifdef ENABLE_NLS
583 #ifdef WIN32
584 Glib::ustring localePath = homedir;
585 localePath += "\\";
586 localePath += PACKAGE_LOCALE_DIR;
587 bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
588 #else
589 #ifdef ENABLE_BINRELOC
590 bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
591 #else
592 bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
593 #endif
594 #endif
595 // Allow the user to override the locale directory by setting
596 // the environment variable INKSCAPE_LOCALEDIR.
597 char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
598 if (inkscape_localedir != NULL) {
599 bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
600 }
601 #endif
603 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
605 #ifdef ENABLE_NLS
606 textdomain(GETTEXT_PACKAGE);
607 #endif
609 LIBXML_TEST_VERSION
611 Inkscape::GC::init();
613 Inkscape::Debug::Logger::init();
615 gboolean use_gui;
617 #ifndef WIN32
618 // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
619 use_gui = (getenv("DISPLAY") != NULL);
620 #else
621 use_gui = TRUE;
622 #endif
623 /* Test whether with/without GUI is forced */
624 for (int i = 1; i < argc; i++) {
625 if (!strcmp(argv[i], "-z")
626 || !strcmp(argv[i], "--without-gui")
627 || !strcmp(argv[i], "-p")
628 || !strncmp(argv[i], "--print", 7)
629 || !strcmp(argv[i], "-e")
630 || !strncmp(argv[i], "--export-png", 12)
631 || !strcmp(argv[i], "-l")
632 || !strncmp(argv[i], "--export-plain-svg", 18)
633 || !strcmp(argv[i], "-i")
634 || !strncmp(argv[i], "--export-area-drawing", 21)
635 || !strcmp(argv[i], "-D")
636 || !strncmp(argv[i], "--export-area-page", 18)
637 || !strcmp(argv[i], "-C")
638 || !strncmp(argv[i], "--export-id", 11)
639 || !strcmp(argv[i], "-P")
640 || !strncmp(argv[i], "--export-ps", 11)
641 || !strcmp(argv[i], "-E")
642 || !strncmp(argv[i], "--export-eps", 12)
643 || !strcmp(argv[i], "-A")
644 || !strncmp(argv[i], "--export-pdf", 12)
645 #ifdef WIN32
646 || !strcmp(argv[i], "-M")
647 || !strncmp(argv[i], "--export-emf", 12)
648 #endif //WIN32
649 || !strcmp(argv[i], "-W")
650 || !strncmp(argv[i], "--query-width", 13)
651 || !strcmp(argv[i], "-H")
652 || !strncmp(argv[i], "--query-height", 14)
653 || !strcmp(argv[i], "-S")
654 || !strncmp(argv[i], "--query-all", 11)
655 || !strcmp(argv[i], "-X")
656 || !strncmp(argv[i], "--query-x", 9)
657 || !strcmp(argv[i], "-Y")
658 || !strncmp(argv[i], "--query-y", 9)
659 || !strcmp(argv[i], "--vacuum-defs")
660 || !strcmp(argv[i], "--shell")
661 )
662 {
663 /* main_console handles any exports -- not the gui */
664 use_gui = FALSE;
665 break;
666 } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
667 use_gui = TRUE;
668 break;
669 }
670 }
672 #ifdef WIN32
673 #ifndef REPLACEARGS_ANSI
674 if ( PrintWin32::is_os_wide() )
675 #endif // REPLACEARGS_ANSI
676 {
677 // If the call fails, we'll need to convert charsets
678 needToRecodeParams = !replaceArgs( argc, argv );
679 }
680 #endif // WIN32
682 /// \todo Should this be a static object (see inkscape.cpp)?
683 Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
685 return app.run();
686 }
691 void fixupSingleFilename( gchar **orig, gchar **spare )
692 {
693 if ( orig && *orig && **orig ) {
694 GError *error = NULL;
695 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
696 if ( newFileName )
697 {
698 *orig = newFileName;
699 if ( spare ) {
700 *spare = newFileName;
701 }
702 // g_message("Set a replacement fixup");
703 }
704 }
705 }
709 GSList *fixupFilenameEncoding( GSList* fl )
710 {
711 GSList *newFl = NULL;
712 while ( fl ) {
713 gchar *fn = static_cast<gchar*>(fl->data);
714 fl = g_slist_remove( fl, fl->data );
715 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
716 if ( newFileName ) {
718 if ( 0 )
719 {
720 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
721 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
722 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
723 "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
724 gtk_dialog_run (GTK_DIALOG (w));
725 gtk_widget_destroy (w);
726 g_free(safeNewFn);
727 g_free(safeFn);
728 }
730 g_free( fn );
731 fn = newFileName;
732 newFileName = 0;
733 }
734 else
735 if ( 0 )
736 {
737 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
738 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
739 gtk_dialog_run (GTK_DIALOG (w));
740 gtk_widget_destroy (w);
741 g_free(safeFn);
742 }
743 newFl = g_slist_append( newFl, fn );
744 }
745 return newFl;
746 }
748 int sp_common_main( int argc, char const **argv, GSList **flDest )
749 {
750 /// \todo fixme: Move these to some centralized location (Lauris)
751 sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
752 sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
755 // temporarily switch gettext encoding to locale, so that help messages can be output properly
756 gchar const *charset;
757 g_get_charset(&charset);
759 bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
761 poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
762 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
763 g_return_val_if_fail(ctx != NULL, 1);
765 /* Collect own arguments */
766 GSList *fl = sp_process_args(ctx);
767 poptFreeContext(ctx);
769 // now switch gettext back to UTF-8 (for GUI)
770 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
772 // Now let's see if the file list still holds up
773 if ( needToRecodeParams )
774 {
775 fl = fixupFilenameEncoding( fl );
776 }
778 // Check the globals for filename-fixup
779 if ( needToRecodeParams )
780 {
781 fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
782 fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
783 fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
784 }
785 else
786 {
787 if ( sp_export_png )
788 sp_export_png_utf8 = g_strdup( sp_export_png );
789 if ( sp_export_svg )
790 sp_export_svg_utf8 = g_strdup( sp_export_svg );
791 if ( sp_global_printer )
792 sp_global_printer_utf8 = g_strdup( sp_global_printer );
793 }
795 // Return the list if wanted, else free it up.
796 if ( flDest ) {
797 *flDest = fl;
798 fl = 0;
799 } else {
800 while ( fl ) {
801 g_free( fl->data );
802 fl = g_slist_remove( fl, fl->data );
803 }
804 }
805 return 0;
806 }
808 static void
809 snooper(GdkEvent *event, gpointer /*data*/) {
810 if (inkscape_mapalt()) /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
811 {
812 GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
813 switch (event->type) {
814 case GDK_MOTION_NOTIFY:
815 if(event->motion.state & mapping) {
816 event->motion.state|=GDK_MOD1_MASK;
817 }
818 break;
819 case GDK_BUTTON_PRESS:
820 if(event->button.state & mapping) {
821 event->button.state|=GDK_MOD1_MASK;
822 }
823 break;
824 case GDK_KEY_PRESS:
825 if(event->key.state & mapping) {
826 event->key.state|=GDK_MOD1_MASK;
827 }
828 break;
829 default:
830 break;
831 }
832 }
834 if (inkscape_trackalt()) {
835 // MacOS X with X11 has some problem with the default
836 // xmodmapping. A ~/.xmodmap solution does not work reliably due
837 // to the way we package our executable in a .app that can launch
838 // X11 or use an already-running X11. The same problem has been
839 // reported on Linux but there is no .app/X11 to get in the way
840 // of ~/.xmodmap fixes. So we make this a preference.
841 //
842 // For some reason, Gdk senses changes in Alt (Mod1) state for
843 // many message types, but not for keystrokes! So this ugly hack
844 // tracks what the state of Alt-pressing is, and ensures
845 // GDK_MOD1_MASK is in the event->key.state as appropriate.
846 //
847 static gboolean altL_pressed = FALSE;
848 static gboolean altR_pressed = FALSE;
849 static gboolean alt_pressed = FALSE;
850 guint get_group0_keyval(GdkEventKey* event);
851 guint keyval = 0;
852 switch (event->type) {
853 case GDK_MOTION_NOTIFY:
854 alt_pressed = TRUE && (event->motion.state & GDK_MOD1_MASK);
855 break;
856 case GDK_BUTTON_PRESS:
857 alt_pressed = TRUE && (event->button.state & GDK_MOD1_MASK);
858 break;
859 case GDK_KEY_PRESS:
860 keyval = get_group0_keyval(&event->key);
861 if (keyval == GDK_Alt_L) altL_pressed = TRUE;
862 if (keyval == GDK_Alt_R) altR_pressed = TRUE;
863 alt_pressed = alt_pressed || altL_pressed || altR_pressed;
864 alt_pressed = alt_pressed || (event->button.state & GDK_MOD1_MASK);
865 if (alt_pressed)
866 event->key.state |= GDK_MOD1_MASK;
867 else
868 event->key.state &= ~GDK_MOD1_MASK;
869 break;
870 case GDK_KEY_RELEASE:
871 keyval = get_group0_keyval(&event->key);
872 if (keyval == GDK_Alt_L) altL_pressed = FALSE;
873 if (keyval == GDK_Alt_R) altR_pressed = FALSE;
874 if (!altL_pressed && !altR_pressed)
875 alt_pressed = FALSE;
876 break;
877 default:
878 break;
879 }
880 //printf("alt_pressed: %s\n", alt_pressed? "+" : "-");
881 }
883 gtk_main_do_event (event);
884 }
886 static std::vector<Glib::ustring> getDirectorySet(const gchar* userDir, const gchar* const * systemDirs) {
887 std::vector<Glib::ustring> listing;
888 listing.push_back(userDir);
889 for ( const char* const* cur = systemDirs; *cur; cur++ )
890 {
891 listing.push_back(*cur);
892 }
893 return listing;
894 }
896 int
897 sp_main_gui(int argc, char const **argv)
898 {
899 Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
901 GSList *fl = NULL;
902 int retVal = sp_common_main( argc, argv, &fl );
903 g_return_val_if_fail(retVal == 0, 1);
905 // Add possible icon entry directories
906 std::vector<Glib::ustring> dataDirs = getDirectorySet( g_get_user_data_dir(),
907 g_get_system_data_dirs() );
908 for (std::vector<Glib::ustring>::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
909 {
910 std::vector<Glib::ustring> listing;
911 listing.push_back(*it);
912 listing.push_back("inkscape");
913 listing.push_back("icons");
914 Glib::ustring dir = Glib::build_filename(listing);
915 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir.c_str());
916 }
918 // Add our icon directory to the search path for icon theme lookups.
919 gchar *usericondir = profile_path("icons");
920 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), usericondir);
921 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
922 g_free(usericondir);
924 gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
925 Inkscape::Debug::log_display_config();
927 // Set default window icon. Obeys the theme.
928 gtk_window_set_default_icon_name("inkscape");
929 // Do things that were previously in inkscape_gtk_stock_init().
930 sp_icon_get_phys_size(GTK_ICON_SIZE_MENU);
931 Inkscape::UI::Widget::Panel::prep();
933 gboolean create_new = TRUE;
935 /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
936 inkscape_application_init(argv[0], true);
938 while (fl) {
939 if (sp_file_open((gchar *)fl->data,NULL)) {
940 create_new=FALSE;
941 }
942 fl = g_slist_remove(fl, fl->data);
943 }
944 if (create_new) {
945 sp_file_new_default();
946 }
948 Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
949 main_instance.run();
951 #ifdef WIN32
952 //We might not need anything here
953 //sp_win32_finish(); <-- this is a NOP func
954 #endif
956 return 0;
957 }
959 /**
960 * Process file list
961 */
962 void sp_process_file_list(GSList *fl)
963 {
964 while (fl) {
965 const gchar *filename = (gchar *)fl->data;
967 SPDocument *doc = NULL;
968 try {
969 doc = Inkscape::Extension::open(NULL, filename);
970 } catch (Inkscape::Extension::Input::no_extension_found &e) {
971 doc = NULL;
972 } catch (Inkscape::Extension::Input::open_failed &e) {
973 doc = NULL;
974 }
976 if (doc == NULL) {
977 try {
978 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
979 } catch (Inkscape::Extension::Input::no_extension_found &e) {
980 doc = NULL;
981 } catch (Inkscape::Extension::Input::open_failed &e) {
982 doc = NULL;
983 }
984 }
985 if (doc == NULL) {
986 g_warning("Specified document %s cannot be opened (does not exist or not a valid SVG file)", filename);
987 } else {
988 if (sp_vacuum_defs) {
989 vacuum_document(doc);
990 }
991 if (sp_vacuum_defs && !sp_export_svg) {
992 // save under the name given in the command line
993 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
994 }
995 if (sp_global_printer) {
996 sp_print_document_to_file(doc, sp_global_printer);
997 }
998 if (sp_export_png || (sp_export_id && sp_export_use_hints)) {
999 sp_do_export_png(doc);
1000 }
1001 if (sp_export_svg) {
1002 Inkscape::XML::Document *rdoc;
1003 Inkscape::XML::Node *repr;
1004 rdoc = sp_repr_document_new("svg:svg");
1005 repr = rdoc->root();
1006 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
1007 sp_repr_save_rebased_file(repr->document(), sp_export_svg, SP_SVG_NS_URI,
1008 doc->base, sp_export_svg);
1009 }
1010 if (sp_export_ps) {
1011 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
1012 }
1013 if (sp_export_eps) {
1014 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
1015 }
1016 if (sp_export_pdf) {
1017 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
1018 }
1019 #ifdef WIN32
1020 if (sp_export_emf) {
1021 do_export_emf(doc, sp_export_emf, "image/x-emf");
1022 }
1023 #endif //WIN32
1024 if (sp_query_all) {
1025 do_query_all (doc);
1026 } else if (sp_query_width || sp_query_height) {
1027 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
1028 } else if (sp_query_x || sp_query_y) {
1029 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
1030 }
1032 delete doc;
1033 }
1034 fl = g_slist_remove(fl, fl->data);
1035 }
1036 }
1038 /**
1039 * Run the application as an interactive shell, parsing command lines from stdin
1040 * Returns -1 on error.
1041 */
1042 int sp_main_shell(char const* command_name)
1043 {
1044 int retval = 0;
1046 const unsigned int buffer_size = 4096;
1047 gchar *command_line = g_strnfill(buffer_size, 0);
1048 g_strlcpy(command_line, command_name, buffer_size);
1049 gsize offset = g_strlcat(command_line, " ", buffer_size);
1050 gsize sizeLeft = buffer_size - offset;
1051 gchar *useme = command_line + offset;
1053 fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
1054 fflush(stdout);
1055 char* linedata = 0;
1056 do {
1057 fprintf(stdout, ">");
1058 fflush(stdout);
1059 if ((linedata = fgets(useme, sizeLeft, stdin))) {
1060 size_t len = strlen(useme);
1061 if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
1062 fprintf(stdout, "ERROR: Command line too long\n");
1063 // Consume rest of line
1064 retval = -1; // If the while loop completes, this remains -1
1065 while (fgets(useme, sizeLeft, stdin) && retval) {
1066 len = strlen(command_line);
1067 if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
1068 retval = 0;
1069 }
1070 }
1071 } else {
1072 useme[--len] = '\0'; // Strip newline
1073 if (useme[len - 1] == '\r') {
1074 useme[--len] = '\0';
1075 }
1076 if ( strcmp(useme, "quit") == 0 ) {
1077 // Time to quit
1078 fflush(stdout);
1079 linedata = 0; // mark for exit
1080 } else if ( len < 1 ) {
1081 // blank string. Do nothing.
1082 } else {
1083 GError* parseError = 0;
1084 gchar** argv = 0;
1085 gint argc = 0;
1086 if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
1087 poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
1088 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
1089 if ( ctx ) {
1090 GSList *fl = sp_process_args(ctx);
1091 sp_process_file_list(fl);
1092 poptFreeContext(ctx);
1093 } else {
1094 retval = 1; // not sure why. But this was the previous return value
1095 }
1096 resetCommandlineGlobals();
1097 g_strfreev(argv);
1098 } else {
1099 g_warning("Cannot parse commandline: %s", useme);
1100 }
1101 }
1102 }
1103 } // if (linedata...
1104 } while (linedata && (retval == 0));
1106 g_free(command_line);
1107 return retval;
1108 }
1110 int sp_main_console(int argc, char const **argv)
1111 {
1112 /* We are started in text mode */
1114 /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1115 * in a non-Gtk environment. Used in libnrtype's
1116 * FontInstance.cpp and FontFactory.cpp.
1117 * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1118 */
1119 g_type_init();
1120 char **argv2 = const_cast<char **>(argv);
1121 gtk_init_check( &argc, &argv2 );
1122 //setlocale(LC_ALL, "");
1124 GSList *fl = NULL;
1125 int retVal = sp_common_main( argc, argv, &fl );
1126 g_return_val_if_fail(retVal == 0, 1);
1128 if (fl == NULL && !sp_shell) {
1129 g_print("Nothing to do!\n");
1130 exit(0);
1131 }
1133 inkscape_application_init(argv[0], false);
1135 if (sp_shell) {
1136 sp_main_shell(argv[0]); // Run as interactive shell
1137 exit(0);
1138 } else {
1139 sp_process_file_list(fl); // Normal command line invokation
1140 }
1142 return 0;
1143 }
1145 static void
1146 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1147 {
1148 SPObject *o = NULL;
1150 if (id) {
1151 o = doc->getObjectById(id);
1152 if (o) {
1153 if (!SP_IS_ITEM (o)) {
1154 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1155 return;
1156 }
1157 } else {
1158 g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1159 return;
1160 }
1161 } else {
1162 o = SP_DOCUMENT_ROOT(doc);
1163 }
1165 if (o) {
1166 sp_document_ensure_up_to_date (doc);
1167 SPItem *item = ((SPItem *) o);
1169 // "true" SVG bbox for scripting
1170 Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1171 if (area) {
1172 Inkscape::SVGOStringStream os;
1173 if (extent) {
1174 os << area->dimensions()[axis];
1175 } else {
1176 os << area->min()[axis];
1177 }
1178 g_print ("%s", os.str().c_str());
1179 } else {
1180 g_print("0");
1181 }
1182 }
1183 }
1185 static void
1186 do_query_all (SPDocument *doc)
1187 {
1188 SPObject *o = NULL;
1190 o = SP_DOCUMENT_ROOT(doc);
1192 if (o) {
1193 sp_document_ensure_up_to_date (doc);
1194 do_query_all_recurse(o);
1195 }
1196 }
1198 static void
1199 do_query_all_recurse (SPObject *o)
1200 {
1201 SPItem *item = ((SPItem *) o);
1202 if (o->id && SP_IS_ITEM(item)) {
1203 Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1204 if (area) {
1205 Inkscape::SVGOStringStream os;
1206 os << o->id;
1207 os << "," << area->min()[Geom::X];
1208 os << "," << area->min()[Geom::Y];
1209 os << "," << area->dimensions()[Geom::X];
1210 os << "," << area->dimensions()[Geom::Y];
1211 g_print ("%s\n", os.str().c_str());
1212 }
1213 }
1215 SPObject *child = o->children;
1216 while (child) {
1217 do_query_all_recurse (child);
1218 child = child->next;
1219 }
1220 }
1223 static void
1224 sp_do_export_png(SPDocument *doc)
1225 {
1226 const gchar *filename = NULL;
1227 bool filename_from_hint = false;
1228 gdouble dpi = 0.0;
1230 if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1231 g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1232 }
1234 GSList *items = NULL;
1236 Geom::Rect area;
1237 if (sp_export_id || sp_export_area_drawing) {
1239 SPObject *o = NULL;
1240 SPObject *o_area = NULL;
1241 if (sp_export_id && sp_export_area_drawing) {
1242 o = doc->getObjectById(sp_export_id);
1243 o_area = SP_DOCUMENT_ROOT (doc);
1244 } else if (sp_export_id) {
1245 o = doc->getObjectById(sp_export_id);
1246 o_area = o;
1247 } else if (sp_export_area_drawing) {
1248 o = SP_DOCUMENT_ROOT (doc);
1249 o_area = o;
1250 }
1252 if (o) {
1253 if (!SP_IS_ITEM (o)) {
1254 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1255 return;
1256 }
1258 items = g_slist_prepend (items, SP_ITEM(o));
1260 if (sp_export_id_only) {
1261 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1262 }
1264 if (sp_export_use_hints) {
1266 // retrieve export filename hint
1267 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1268 if (fn_hint) {
1269 if (sp_export_png) {
1270 g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1271 filename = sp_export_png;
1272 } else {
1273 filename = fn_hint;
1274 filename_from_hint = true;
1275 }
1276 } else {
1277 g_warning ("Export filename hint not found for the object.");
1278 filename = sp_export_png;
1279 }
1281 // retrieve export dpi hints
1282 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1283 if (dpi_hint) {
1284 if (sp_export_dpi || sp_export_width || sp_export_height) {
1285 g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1286 } else {
1287 dpi = atof(dpi_hint);
1288 }
1289 } else {
1290 g_warning ("Export DPI hint not found for the object.");
1291 }
1293 }
1295 // write object bbox to area
1296 sp_document_ensure_up_to_date (doc);
1297 Geom::OptRect areaMaybe;
1298 sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1299 if (areaMaybe) {
1300 area = *areaMaybe;
1301 } else {
1302 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1303 return;
1304 }
1305 } else {
1306 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1307 return;
1308 }
1309 }
1311 if (sp_export_area) {
1312 /* Try to parse area (given in SVG pixels) */
1313 gdouble x0,y0,x1,y1;
1314 if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1315 g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1316 return;
1317 }
1318 area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1319 } else if (sp_export_area_page || !(sp_export_id || sp_export_area_drawing)) {
1320 /* Export the whole page: note: Inkscape uses 'page' in all menus and dialogs, not 'canvas' */
1321 sp_document_ensure_up_to_date (doc);
1322 Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1323 area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1324 }
1326 // set filename and dpi from options, if not yet set from the hints
1327 if (!filename) {
1328 if (!sp_export_png) {
1329 g_warning ("No export filename given and no filename hint. Nothing exported.");
1330 return;
1331 }
1332 filename = sp_export_png;
1333 }
1335 if (sp_export_dpi && dpi == 0.0) {
1336 dpi = atof(sp_export_dpi);
1337 if ((dpi < 0.1) || (dpi > 10000.0)) {
1338 g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1339 return;
1340 }
1341 g_print("DPI: %g\n", dpi);
1342 }
1344 if (sp_export_area_snap) {
1345 round_rectangle_outwards(area);
1346 }
1348 // default dpi
1349 if (dpi == 0.0) {
1350 dpi = PX_PER_IN;
1351 }
1353 unsigned long int width = 0;
1354 unsigned long int height = 0;
1356 if (sp_export_width) {
1357 errno=0;
1358 width = strtoul(sp_export_width, NULL, 0);
1359 if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1360 g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1361 return;
1362 }
1363 dpi = (gdouble) width * PX_PER_IN / area.width();
1364 }
1366 if (sp_export_height) {
1367 errno=0;
1368 height = strtoul(sp_export_height, NULL, 0);
1369 if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1370 g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1371 return;
1372 }
1373 dpi = (gdouble) height * PX_PER_IN / area.height();
1374 }
1376 if (!sp_export_width) {
1377 width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1378 }
1380 if (!sp_export_height) {
1381 height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1382 }
1384 guint32 bgcolor = 0x00000000;
1385 if (sp_export_background) {
1386 // override the page color
1387 bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1388 bgcolor |= 0xff; // default is no opacity
1389 } else {
1390 // read from namedview
1391 Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1392 if (nv && nv->attribute("pagecolor"))
1393 bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1394 if (nv && nv->attribute("inkscape:pageopacity"))
1395 bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1396 }
1398 if (sp_export_background_opacity) {
1399 // override opacity
1400 gfloat value;
1401 if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1402 if (value > 1.0) {
1403 value = CLAMP (value, 1.0f, 255.0f);
1404 bgcolor &= (guint32) 0xffffff00;
1405 bgcolor |= (guint32) floor(value);
1406 } else {
1407 value = CLAMP (value, 0.0f, 1.0f);
1408 bgcolor &= (guint32) 0xffffff00;
1409 bgcolor |= SP_COLOR_F_TO_U(value);
1410 }
1411 }
1412 }
1414 gchar *path = 0;
1415 if (filename_from_hint) {
1416 //Make relative paths go from the document location, if possible:
1417 if (!g_path_is_absolute(filename) && doc->uri) {
1418 gchar *dirname = g_path_get_dirname(doc->uri);
1419 if (dirname) {
1420 path = g_build_filename(dirname, filename, NULL);
1421 g_free(dirname);
1422 }
1423 }
1424 if (!path) {
1425 path = g_strdup(filename);
1426 }
1427 } else {
1428 path = g_strdup(filename);
1429 }
1431 g_print("Background RRGGBBAA: %08x\n", bgcolor);
1433 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);
1435 g_print("Bitmap saved as: %s\n", filename);
1437 if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1438 sp_export_png_file(doc, path, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1439 } else {
1440 g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1441 }
1443 g_free (path);
1444 g_slist_free (items);
1445 }
1448 /**
1449 * Perform a PDF/PS/EPS export
1450 *
1451 * \param doc Document to export.
1452 * \param uri URI to export to.
1453 * \param mime MIME type to export as.
1454 */
1456 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1457 {
1458 Inkscape::Extension::DB::OutputList o;
1459 Inkscape::Extension::db.get_output_list(o);
1460 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1461 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1462 i++;
1463 }
1465 if (i == o.end())
1466 {
1467 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1468 return;
1469 }
1471 if (sp_export_id) {
1472 SPObject *o = doc->getObjectById(sp_export_id);
1473 if (o == NULL) {
1474 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1475 return;
1476 }
1477 (*i)->set_param_string ("exportId", sp_export_id);
1478 } else {
1479 (*i)->set_param_string ("exportId", "");
1480 }
1482 if (sp_export_area_page && sp_export_area_drawing) {
1483 g_warning ("You cannot use --export-area-page and --export-area-drawing at the same time; only the former will take effect.");
1484 sp_export_area_drawing = false;
1485 }
1487 if (sp_export_area_drawing) {
1488 (*i)->set_param_bool ("areaDrawing", TRUE);
1489 } else {
1490 (*i)->set_param_bool ("areaDrawing", FALSE);
1491 }
1493 if (sp_export_area_page) {
1494 if (sp_export_eps) {
1495 g_warning ("EPS cannot have its bounding box extend beyond its content, so if your drawing is smaller than the page, --export-area-page will clip it to drawing.");
1496 }
1497 (*i)->set_param_bool ("areaPage", TRUE);
1498 } else {
1499 (*i)->set_param_bool ("areaPage", FALSE);
1500 }
1502 if (!sp_export_area_drawing && !sp_export_area_page && !sp_export_id) {
1503 // neither is set, set page as default for ps/pdf and drawing for eps
1504 if (sp_export_eps) {
1505 try {
1506 (*i)->set_param_bool("areaDrawing", TRUE);
1507 } catch (...) {}
1508 }
1509 }
1511 if (sp_export_text_to_path) {
1512 (*i)->set_param_bool("textToPath", TRUE);
1513 } else {
1514 (*i)->set_param_bool("textToPath", FALSE);
1515 }
1517 if (sp_export_ignore_filters) {
1518 (*i)->set_param_bool("blurToBitmap", FALSE);
1519 } else {
1520 (*i)->set_param_bool("blurToBitmap", TRUE);
1522 gdouble dpi = 90.0;
1523 if (sp_export_dpi) {
1524 dpi = atof(sp_export_dpi);
1525 if ((dpi < 1) || (dpi > 10000.0)) {
1526 g_warning("DPI value %s out of range [1 - 10000]. Using 90 dpi instead.", sp_export_dpi);
1527 dpi = 90;
1528 }
1529 }
1531 (*i)->set_param_int("resolution", (int) dpi);
1532 }
1534 (*i)->save(doc, uri);
1535 }
1537 #ifdef WIN32
1538 /**
1539 * Export a document to EMF
1540 *
1541 * \param doc Document to export.
1542 * \param uri URI to export to.
1543 * \param mime MIME type to export as (should be "image/x-emf")
1544 */
1546 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1547 {
1548 Inkscape::Extension::DB::OutputList o;
1549 Inkscape::Extension::db.get_output_list(o);
1550 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1551 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1552 i++;
1553 }
1555 if (i == o.end())
1556 {
1557 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1558 return;
1559 }
1561 (*i)->save(doc, uri);
1562 }
1563 #endif //WIN32
1565 #ifdef WIN32
1566 bool replaceArgs( int& argc, char**& argv )
1567 {
1568 bool worked = false;
1570 #ifdef REPLACEARGS_DEBUG
1571 MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1572 #endif // REPLACEARGS_DEBUG
1574 wchar_t* line = GetCommandLineW();
1575 if ( line )
1576 {
1577 #ifdef REPLACEARGS_DEBUG
1578 {
1579 gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1580 if ( utf8Line )
1581 {
1582 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1583 {
1584 char tmp[strlen(safe) + 32];
1585 snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1586 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1587 }
1588 }
1589 }
1590 #endif // REPLACEARGS_DEBUG
1592 int numArgs = 0;
1593 wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1595 #ifdef REPLACEARGS_ANSI
1596 // test code for trying things on Win95/98/ME
1597 if ( !parsed )
1598 {
1599 #ifdef REPLACEARGS_DEBUG
1600 MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1601 #endif // REPLACEARGS_DEBUG
1602 int lineLen = wcslen(line) + 1;
1603 wchar_t* lineDup = new wchar_t[lineLen];
1604 wcsncpy( lineDup, line, lineLen );
1606 int pos = 0;
1607 bool inQuotes = false;
1608 bool inWhitespace = true;
1609 std::vector<int> places;
1610 while ( lineDup[pos] )
1611 {
1612 if ( inQuotes )
1613 {
1614 if ( lineDup[pos] == L'"' )
1615 {
1616 inQuotes = false;
1617 }
1618 }
1619 else if ( lineDup[pos] == L'"' )
1620 {
1621 inQuotes = true;
1622 inWhitespace = false;
1623 places.push_back(pos);
1624 }
1625 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1626 {
1627 if ( !inWhitespace )
1628 {
1629 inWhitespace = true;
1630 lineDup[pos] = 0;
1631 }
1632 }
1633 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1634 {
1635 inWhitespace = false;
1636 places.push_back(pos);
1637 }
1638 else
1639 {
1640 // consume
1641 }
1642 pos++;
1643 }
1644 #ifdef REPLACEARGS_DEBUG
1645 {
1646 char tmp[256];
1647 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1648 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1649 }
1650 #endif // REPLACEARGS_DEBUG
1652 wchar_t** block = new wchar_t*[places.size()];
1653 int i = 0;
1654 for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1655 {
1656 block[i++] = &lineDup[*it];
1657 }
1658 parsed = block;
1659 numArgs = places.size();
1660 }
1661 #endif // REPLACEARGS_ANSI
1663 if ( parsed )
1664 {
1665 std::vector<wchar_t*>expandedArgs;
1666 if ( numArgs > 0 )
1667 {
1668 expandedArgs.push_back( parsed[0] );
1669 }
1671 for ( int i1 = 1; i1 < numArgs; i1++ )
1672 {
1673 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1674 wildcarded &= parsed[i1][0] != L'"';
1675 wildcarded &= parsed[i1][0] != L'-';
1676 if ( wildcarded )
1677 {
1678 #ifdef REPLACEARGS_ANSI
1679 WIN32_FIND_DATAA data;
1680 #else
1681 WIN32_FIND_DATAW data;
1682 #endif // REPLACEARGS_ANSI
1684 memset((void *)&data, 0, sizeof(data));
1686 int baseLen = wcslen(parsed[i1]) + 2;
1687 wchar_t* base = new wchar_t[baseLen];
1688 wcsncpy( base, parsed[i1], baseLen );
1689 wchar_t* last = wcsrchr( base, L'\\' );
1690 if ( last )
1691 {
1692 last[1] = 0;
1693 }
1694 else
1695 {
1696 base[0] = 0;
1697 }
1698 baseLen = wcslen( base );
1700 #ifdef REPLACEARGS_ANSI
1701 char target[MAX_PATH];
1702 if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1703 {
1704 HANDLE hf = FindFirstFileA( target, &data );
1705 #else
1706 HANDLE hf = FindFirstFileW( parsed[i1], &data );
1707 #endif // REPLACEARGS_ANSI
1708 if ( hf != INVALID_HANDLE_VALUE )
1709 {
1710 BOOL found = TRUE;
1711 do
1712 {
1713 #ifdef REPLACEARGS_ANSI
1714 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1715 if ( howMany > 0 )
1716 {
1717 howMany += baseLen;
1718 wchar_t* tmp = new wchar_t[howMany + 1];
1719 wcsncpy( tmp, base, howMany + 1 );
1720 MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1721 expandedArgs.push_back( tmp );
1722 found = FindNextFileA( hf, &data );
1723 }
1724 #else
1725 int howMany = wcslen(data.cFileName) + baseLen;
1726 wchar_t* tmp = new wchar_t[howMany + 1];
1727 wcsncpy( tmp, base, howMany + 1 );
1728 wcsncat( tmp, data.cFileName, howMany + 1 );
1729 expandedArgs.push_back( tmp );
1730 found = FindNextFileW( hf, &data );
1731 #endif // REPLACEARGS_ANSI
1732 } while ( found );
1734 FindClose( hf );
1735 }
1736 else
1737 {
1738 expandedArgs.push_back( parsed[i1] );
1739 }
1740 #ifdef REPLACEARGS_ANSI
1741 }
1742 #endif // REPLACEARGS_ANSI
1744 delete[] base;
1745 }
1746 else
1747 {
1748 expandedArgs.push_back( parsed[i1] );
1749 }
1750 }
1752 {
1753 wchar_t** block = new wchar_t*[expandedArgs.size()];
1754 int iz = 0;
1755 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1756 {
1757 block[iz++] = *it;
1758 }
1759 parsed = block;
1760 numArgs = expandedArgs.size();
1761 }
1763 std::vector<gchar*> newArgs;
1764 for ( int i = 0; i < numArgs; i++ )
1765 {
1766 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1767 if ( replacement )
1768 {
1769 #ifdef REPLACEARGS_DEBUG
1770 gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1772 if ( safe2 )
1773 {
1774 {
1775 char tmp[1024];
1776 snprintf( tmp, sizeof(tmp), " [%2d] = '%s'", i, safe2 );
1777 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1778 }
1779 g_free( safe2 );
1780 }
1781 #endif // REPLACEARGS_DEBUG
1783 newArgs.push_back( replacement );
1784 }
1785 else
1786 {
1787 newArgs.push_back( blankParam );
1788 }
1789 }
1791 // Now push our munged params to be the new argv and argc
1792 {
1793 char** block = new char*[newArgs.size()];
1794 int iz = 0;
1795 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1796 {
1797 block[iz++] = *it;
1798 }
1799 argv = block;
1800 argc = newArgs.size();
1801 worked = true;
1802 }
1803 }
1804 #ifdef REPLACEARGS_DEBUG
1805 else
1806 {
1807 MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1808 }
1809 #endif // REPLACEARGS_DEBUG
1810 }
1811 #ifdef REPLACEARGS_DEBUG
1812 else
1813 {
1814 {
1815 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1816 }
1818 char* line2 = GetCommandLineA();
1819 if ( line2 )
1820 {
1821 gchar *safe = Inkscape::IO::sanitizeString(line2);
1822 {
1823 {
1824 char tmp[strlen(safe) + 32];
1825 snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1826 MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1827 }
1828 }
1829 }
1830 else
1831 {
1832 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1833 }
1834 }
1835 #endif // REPLACEARGS_DEBUG
1837 return worked;
1838 }
1839 #endif // WIN32
1841 static GSList *
1842 sp_process_args(poptContext ctx)
1843 {
1844 GSList *fl = NULL;
1846 gint a;
1847 while ((a = poptGetNextOpt(ctx)) != -1) {
1848 switch (a) {
1849 case SP_ARG_FILE: {
1850 gchar const *fn = poptGetOptArg(ctx);
1851 if (fn != NULL) {
1852 fl = g_slist_append(fl, g_strdup(fn));
1853 }
1854 break;
1855 }
1856 case SP_ARG_VERSION: {
1857 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1858 exit(0);
1859 break;
1860 }
1861 case SP_ARG_EXTENSIONDIR: {
1862 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1863 exit(0);
1864 break;
1865 }
1866 case SP_ARG_VERB_LIST: {
1867 // This really shouldn't go here, we should init the app.
1868 // But, since we're just exiting in this path, there is
1869 // no harm, and this is really a better place to put
1870 // everything else.
1871 Inkscape::Extension::init();
1872 Inkscape::Verb::list();
1873 exit(0);
1874 break;
1875 }
1876 case SP_ARG_VERB:
1877 case SP_ARG_SELECT: {
1878 gchar const *arg = poptGetOptArg(ctx);
1879 if (arg != NULL) {
1880 // printf("Adding in: %s\n", arg);
1881 new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1882 }
1883 break;
1884 }
1885 case POPT_ERROR_BADOPT: {
1886 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1887 exit(1);
1888 break;
1889 }
1890 default: {
1891 break;
1892 }
1893 }
1894 }
1896 gchar const ** const args = poptGetArgs(ctx);
1897 if (args != NULL) {
1898 for (unsigned i = 0; args[i] != NULL; i++) {
1899 fl = g_slist_append(fl, g_strdup(args[i]));
1900 }
1901 }
1903 return fl;
1904 }
1907 /*
1908 Local Variables:
1909 mode:c++
1910 c-file-style:"stroustrup"
1911 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1912 indent-tabs-mode:nil
1913 fill-column:99
1914 End:
1915 */
1916 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :