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 // Don't touch the registry (works fine without it) for Inkscape Portable
570 gchar const *val = g_getenv("INKSCAPE_PORTABLE_PROFILE_DIR");
571 if (!val) {
572 RegistryTool rt;
573 rt.setPathInfo();
574 }
575 #endif
577 // Prevents errors like "Unable to wrap GdkPixbuf..." (in nr-filter-image.cpp for example)
578 Gtk::Main::init_gtkmm_internals();
580 // Bug #197475
581 set_extensions_env();
583 /**
584 * Call bindtextdomain() for various machines's paths
585 */
586 #ifdef ENABLE_NLS
587 #ifdef WIN32
588 Glib::ustring localePath = homedir;
589 localePath += "\\";
590 localePath += PACKAGE_LOCALE_DIR;
591 bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
592 #else
593 #ifdef ENABLE_BINRELOC
594 bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
595 #else
596 bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
597 #endif
598 #endif
599 // Allow the user to override the locale directory by setting
600 // the environment variable INKSCAPE_LOCALEDIR.
601 char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
602 if (inkscape_localedir != NULL) {
603 bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
604 }
605 #endif
607 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
609 #ifdef ENABLE_NLS
610 textdomain(GETTEXT_PACKAGE);
611 #endif
613 LIBXML_TEST_VERSION
615 Inkscape::GC::init();
617 Inkscape::Debug::Logger::init();
619 gboolean use_gui;
621 #ifndef WIN32
622 // TODO use g_getenv() and g_setenv() that use filename encoding, which is UTF-8 on Windows
623 use_gui = (getenv("DISPLAY") != NULL);
624 #else
625 use_gui = TRUE;
626 #endif
627 /* Test whether with/without GUI is forced */
628 for (int i = 1; i < argc; i++) {
629 if (!strcmp(argv[i], "-z")
630 || !strcmp(argv[i], "--without-gui")
631 || !strcmp(argv[i], "-p")
632 || !strncmp(argv[i], "--print", 7)
633 || !strcmp(argv[i], "-e")
634 || !strncmp(argv[i], "--export-png", 12)
635 || !strcmp(argv[i], "-l")
636 || !strncmp(argv[i], "--export-plain-svg", 18)
637 || !strcmp(argv[i], "-i")
638 || !strncmp(argv[i], "--export-area-drawing", 21)
639 || !strcmp(argv[i], "-D")
640 || !strncmp(argv[i], "--export-area-page", 18)
641 || !strcmp(argv[i], "-C")
642 || !strncmp(argv[i], "--export-id", 11)
643 || !strcmp(argv[i], "-P")
644 || !strncmp(argv[i], "--export-ps", 11)
645 || !strcmp(argv[i], "-E")
646 || !strncmp(argv[i], "--export-eps", 12)
647 || !strcmp(argv[i], "-A")
648 || !strncmp(argv[i], "--export-pdf", 12)
649 #ifdef WIN32
650 || !strcmp(argv[i], "-M")
651 || !strncmp(argv[i], "--export-emf", 12)
652 #endif //WIN32
653 || !strcmp(argv[i], "-W")
654 || !strncmp(argv[i], "--query-width", 13)
655 || !strcmp(argv[i], "-H")
656 || !strncmp(argv[i], "--query-height", 14)
657 || !strcmp(argv[i], "-S")
658 || !strncmp(argv[i], "--query-all", 11)
659 || !strcmp(argv[i], "-X")
660 || !strncmp(argv[i], "--query-x", 9)
661 || !strcmp(argv[i], "-Y")
662 || !strncmp(argv[i], "--query-y", 9)
663 || !strcmp(argv[i], "--vacuum-defs")
664 || !strcmp(argv[i], "--shell")
665 )
666 {
667 /* main_console handles any exports -- not the gui */
668 use_gui = FALSE;
669 break;
670 } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
671 use_gui = TRUE;
672 break;
673 }
674 }
676 #ifdef WIN32
677 #ifndef REPLACEARGS_ANSI
678 if ( PrintWin32::is_os_wide() )
679 #endif // REPLACEARGS_ANSI
680 {
681 // If the call fails, we'll need to convert charsets
682 needToRecodeParams = !replaceArgs( argc, argv );
683 }
684 #endif // WIN32
686 /// \todo Should this be a static object (see inkscape.cpp)?
687 Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
689 return app.run();
690 }
695 void fixupSingleFilename( gchar **orig, gchar **spare )
696 {
697 if ( orig && *orig && **orig ) {
698 GError *error = NULL;
699 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
700 if ( newFileName )
701 {
702 *orig = newFileName;
703 if ( spare ) {
704 *spare = newFileName;
705 }
706 // g_message("Set a replacement fixup");
707 }
708 }
709 }
713 GSList *fixupFilenameEncoding( GSList* fl )
714 {
715 GSList *newFl = NULL;
716 while ( fl ) {
717 gchar *fn = static_cast<gchar*>(fl->data);
718 fl = g_slist_remove( fl, fl->data );
719 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
720 if ( newFileName ) {
722 if ( 0 )
723 {
724 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
725 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
726 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
727 "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
728 gtk_dialog_run (GTK_DIALOG (w));
729 gtk_widget_destroy (w);
730 g_free(safeNewFn);
731 g_free(safeFn);
732 }
734 g_free( fn );
735 fn = newFileName;
736 newFileName = 0;
737 }
738 else
739 if ( 0 )
740 {
741 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
742 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
743 gtk_dialog_run (GTK_DIALOG (w));
744 gtk_widget_destroy (w);
745 g_free(safeFn);
746 }
747 newFl = g_slist_append( newFl, fn );
748 }
749 return newFl;
750 }
752 int sp_common_main( int argc, char const **argv, GSList **flDest )
753 {
754 /// \todo fixme: Move these to some centralized location (Lauris)
755 sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
756 sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
759 // temporarily switch gettext encoding to locale, so that help messages can be output properly
760 gchar const *charset;
761 g_get_charset(&charset);
763 bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
765 poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
766 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
767 g_return_val_if_fail(ctx != NULL, 1);
769 /* Collect own arguments */
770 GSList *fl = sp_process_args(ctx);
771 poptFreeContext(ctx);
773 // now switch gettext back to UTF-8 (for GUI)
774 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
776 // Now let's see if the file list still holds up
777 if ( needToRecodeParams )
778 {
779 fl = fixupFilenameEncoding( fl );
780 }
782 // Check the globals for filename-fixup
783 if ( needToRecodeParams )
784 {
785 fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
786 fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
787 fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
788 }
789 else
790 {
791 if ( sp_export_png )
792 sp_export_png_utf8 = g_strdup( sp_export_png );
793 if ( sp_export_svg )
794 sp_export_svg_utf8 = g_strdup( sp_export_svg );
795 if ( sp_global_printer )
796 sp_global_printer_utf8 = g_strdup( sp_global_printer );
797 }
799 // Return the list if wanted, else free it up.
800 if ( flDest ) {
801 *flDest = fl;
802 fl = 0;
803 } else {
804 while ( fl ) {
805 g_free( fl->data );
806 fl = g_slist_remove( fl, fl->data );
807 }
808 }
809 return 0;
810 }
812 static void
813 snooper(GdkEvent *event, gpointer /*data*/) {
814 if (inkscape_mapalt()) /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
815 {
816 GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
817 switch (event->type) {
818 case GDK_MOTION_NOTIFY:
819 if(event->motion.state & mapping) {
820 event->motion.state|=GDK_MOD1_MASK;
821 }
822 break;
823 case GDK_BUTTON_PRESS:
824 if(event->button.state & mapping) {
825 event->button.state|=GDK_MOD1_MASK;
826 }
827 break;
828 case GDK_KEY_PRESS:
829 if(event->key.state & mapping) {
830 event->key.state|=GDK_MOD1_MASK;
831 }
832 break;
833 default:
834 break;
835 }
836 }
838 if (inkscape_trackalt()) {
839 // MacOS X with X11 has some problem with the default
840 // xmodmapping. A ~/.xmodmap solution does not work reliably due
841 // to the way we package our executable in a .app that can launch
842 // X11 or use an already-running X11. The same problem has been
843 // reported on Linux but there is no .app/X11 to get in the way
844 // of ~/.xmodmap fixes. So we make this a preference.
845 //
846 // For some reason, Gdk senses changes in Alt (Mod1) state for
847 // many message types, but not for keystrokes! So this ugly hack
848 // tracks what the state of Alt-pressing is, and ensures
849 // GDK_MOD1_MASK is in the event->key.state as appropriate.
850 //
851 static gboolean altL_pressed = FALSE;
852 static gboolean altR_pressed = FALSE;
853 static gboolean alt_pressed = FALSE;
854 guint get_group0_keyval(GdkEventKey* event);
855 guint keyval = 0;
856 switch (event->type) {
857 case GDK_MOTION_NOTIFY:
858 alt_pressed = TRUE && (event->motion.state & GDK_MOD1_MASK);
859 break;
860 case GDK_BUTTON_PRESS:
861 alt_pressed = TRUE && (event->button.state & GDK_MOD1_MASK);
862 break;
863 case GDK_KEY_PRESS:
864 keyval = get_group0_keyval(&event->key);
865 if (keyval == GDK_Alt_L) altL_pressed = TRUE;
866 if (keyval == GDK_Alt_R) altR_pressed = TRUE;
867 alt_pressed = alt_pressed || altL_pressed || altR_pressed;
868 alt_pressed = alt_pressed || (event->button.state & GDK_MOD1_MASK);
869 if (alt_pressed)
870 event->key.state |= GDK_MOD1_MASK;
871 else
872 event->key.state &= ~GDK_MOD1_MASK;
873 break;
874 case GDK_KEY_RELEASE:
875 keyval = get_group0_keyval(&event->key);
876 if (keyval == GDK_Alt_L) altL_pressed = FALSE;
877 if (keyval == GDK_Alt_R) altR_pressed = FALSE;
878 if (!altL_pressed && !altR_pressed)
879 alt_pressed = FALSE;
880 break;
881 default:
882 break;
883 }
884 //printf("alt_pressed: %s\n", alt_pressed? "+" : "-");
885 }
887 gtk_main_do_event (event);
888 }
890 static std::vector<Glib::ustring> getDirectorySet(const gchar* userDir, const gchar* const * systemDirs) {
891 std::vector<Glib::ustring> listing;
892 listing.push_back(userDir);
893 for ( const char* const* cur = systemDirs; *cur; cur++ )
894 {
895 listing.push_back(*cur);
896 }
897 return listing;
898 }
900 int
901 sp_main_gui(int argc, char const **argv)
902 {
903 Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
905 GSList *fl = NULL;
906 int retVal = sp_common_main( argc, argv, &fl );
907 g_return_val_if_fail(retVal == 0, 1);
909 // Add possible icon entry directories
910 std::vector<Glib::ustring> dataDirs = getDirectorySet( g_get_user_data_dir(),
911 g_get_system_data_dirs() );
912 for (std::vector<Glib::ustring>::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it)
913 {
914 std::vector<Glib::ustring> listing;
915 listing.push_back(*it);
916 listing.push_back("inkscape");
917 listing.push_back("icons");
918 Glib::ustring dir = Glib::build_filename(listing);
919 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), dir.c_str());
920 }
922 // Add our icon directory to the search path for icon theme lookups.
923 gchar *usericondir = profile_path("icons");
924 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), usericondir);
925 gtk_icon_theme_append_search_path(gtk_icon_theme_get_default(), INKSCAPE_PIXMAPDIR);
926 g_free(usericondir);
928 gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
929 Inkscape::Debug::log_display_config();
931 // Set default window icon. Obeys the theme.
932 gtk_window_set_default_icon_name("inkscape");
933 // Do things that were previously in inkscape_gtk_stock_init().
934 sp_icon_get_phys_size(GTK_ICON_SIZE_MENU);
935 Inkscape::UI::Widget::Panel::prep();
937 gboolean create_new = TRUE;
939 /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
940 inkscape_application_init(argv[0], true);
942 while (fl) {
943 if (sp_file_open((gchar *)fl->data,NULL)) {
944 create_new=FALSE;
945 }
946 fl = g_slist_remove(fl, fl->data);
947 }
948 if (create_new) {
949 sp_file_new_default();
950 }
952 Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
953 main_instance.run();
955 #ifdef WIN32
956 //We might not need anything here
957 //sp_win32_finish(); <-- this is a NOP func
958 #endif
960 return 0;
961 }
963 /**
964 * Process file list
965 */
966 void sp_process_file_list(GSList *fl)
967 {
968 while (fl) {
969 const gchar *filename = (gchar *)fl->data;
971 SPDocument *doc = NULL;
972 try {
973 doc = Inkscape::Extension::open(NULL, filename);
974 } catch (Inkscape::Extension::Input::no_extension_found &e) {
975 doc = NULL;
976 } catch (Inkscape::Extension::Input::open_failed &e) {
977 doc = NULL;
978 }
980 if (doc == NULL) {
981 try {
982 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
983 } catch (Inkscape::Extension::Input::no_extension_found &e) {
984 doc = NULL;
985 } catch (Inkscape::Extension::Input::open_failed &e) {
986 doc = NULL;
987 }
988 }
989 if (doc == NULL) {
990 g_warning("Specified document %s cannot be opened (does not exist or not a valid SVG file)", filename);
991 } else {
992 if (sp_vacuum_defs) {
993 vacuum_document(doc);
994 }
995 if (sp_vacuum_defs && !sp_export_svg) {
996 // save under the name given in the command line
997 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
998 }
999 if (sp_global_printer) {
1000 sp_print_document_to_file(doc, sp_global_printer);
1001 }
1002 if (sp_export_png || (sp_export_id && sp_export_use_hints)) {
1003 sp_do_export_png(doc);
1004 }
1005 if (sp_export_svg) {
1006 Inkscape::XML::Document *rdoc;
1007 Inkscape::XML::Node *repr;
1008 rdoc = sp_repr_document_new("svg:svg");
1009 repr = rdoc->root();
1010 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
1011 sp_repr_save_rebased_file(repr->document(), sp_export_svg, SP_SVG_NS_URI,
1012 doc->base, sp_export_svg);
1013 }
1014 if (sp_export_ps) {
1015 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
1016 }
1017 if (sp_export_eps) {
1018 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
1019 }
1020 if (sp_export_pdf) {
1021 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
1022 }
1023 #ifdef WIN32
1024 if (sp_export_emf) {
1025 do_export_emf(doc, sp_export_emf, "image/x-emf");
1026 }
1027 #endif //WIN32
1028 if (sp_query_all) {
1029 do_query_all (doc);
1030 } else if (sp_query_width || sp_query_height) {
1031 do_query_dimension (doc, true, sp_query_width? Geom::X : Geom::Y, sp_query_id);
1032 } else if (sp_query_x || sp_query_y) {
1033 do_query_dimension (doc, false, sp_query_x? Geom::X : Geom::Y, sp_query_id);
1034 }
1036 delete doc;
1037 }
1038 fl = g_slist_remove(fl, fl->data);
1039 }
1040 }
1042 /**
1043 * Run the application as an interactive shell, parsing command lines from stdin
1044 * Returns -1 on error.
1045 */
1046 int sp_main_shell(char const* command_name)
1047 {
1048 int retval = 0;
1050 const unsigned int buffer_size = 4096;
1051 gchar *command_line = g_strnfill(buffer_size, 0);
1052 g_strlcpy(command_line, command_name, buffer_size);
1053 gsize offset = g_strlcat(command_line, " ", buffer_size);
1054 gsize sizeLeft = buffer_size - offset;
1055 gchar *useme = command_line + offset;
1057 fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", Inkscape::version_string);
1058 fflush(stdout);
1059 char* linedata = 0;
1060 do {
1061 fprintf(stdout, ">");
1062 fflush(stdout);
1063 if ((linedata = fgets(useme, sizeLeft, stdin))) {
1064 size_t len = strlen(useme);
1065 if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
1066 fprintf(stdout, "ERROR: Command line too long\n");
1067 // Consume rest of line
1068 retval = -1; // If the while loop completes, this remains -1
1069 while (fgets(useme, sizeLeft, stdin) && retval) {
1070 len = strlen(command_line);
1071 if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
1072 retval = 0;
1073 }
1074 }
1075 } else {
1076 useme[--len] = '\0'; // Strip newline
1077 if (useme[len - 1] == '\r') {
1078 useme[--len] = '\0';
1079 }
1080 if ( strcmp(useme, "quit") == 0 ) {
1081 // Time to quit
1082 fflush(stdout);
1083 linedata = 0; // mark for exit
1084 } else if ( len < 1 ) {
1085 // blank string. Do nothing.
1086 } else {
1087 GError* parseError = 0;
1088 gchar** argv = 0;
1089 gint argc = 0;
1090 if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
1091 poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
1092 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
1093 if ( ctx ) {
1094 GSList *fl = sp_process_args(ctx);
1095 sp_process_file_list(fl);
1096 poptFreeContext(ctx);
1097 } else {
1098 retval = 1; // not sure why. But this was the previous return value
1099 }
1100 resetCommandlineGlobals();
1101 g_strfreev(argv);
1102 } else {
1103 g_warning("Cannot parse commandline: %s", useme);
1104 }
1105 }
1106 }
1107 } // if (linedata...
1108 } while (linedata && (retval == 0));
1110 g_free(command_line);
1111 return retval;
1112 }
1114 int sp_main_console(int argc, char const **argv)
1115 {
1116 /* We are started in text mode */
1118 /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1119 * in a non-Gtk environment. Used in libnrtype's
1120 * FontInstance.cpp and FontFactory.cpp.
1121 * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1122 */
1123 g_type_init();
1124 char **argv2 = const_cast<char **>(argv);
1125 gtk_init_check( &argc, &argv2 );
1126 //setlocale(LC_ALL, "");
1128 GSList *fl = NULL;
1129 int retVal = sp_common_main( argc, argv, &fl );
1130 g_return_val_if_fail(retVal == 0, 1);
1132 if (fl == NULL && !sp_shell) {
1133 g_print("Nothing to do!\n");
1134 exit(0);
1135 }
1137 inkscape_application_init(argv[0], false);
1139 if (sp_shell) {
1140 sp_main_shell(argv[0]); // Run as interactive shell
1141 exit(0);
1142 } else {
1143 sp_process_file_list(fl); // Normal command line invokation
1144 }
1146 return 0;
1147 }
1149 static void
1150 do_query_dimension (SPDocument *doc, bool extent, Geom::Dim2 const axis, const gchar *id)
1151 {
1152 SPObject *o = NULL;
1154 if (id) {
1155 o = doc->getObjectById(id);
1156 if (o) {
1157 if (!SP_IS_ITEM (o)) {
1158 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1159 return;
1160 }
1161 } else {
1162 g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1163 return;
1164 }
1165 } else {
1166 o = SP_DOCUMENT_ROOT(doc);
1167 }
1169 if (o) {
1170 sp_document_ensure_up_to_date (doc);
1171 SPItem *item = ((SPItem *) o);
1173 // "true" SVG bbox for scripting
1174 Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1175 if (area) {
1176 Inkscape::SVGOStringStream os;
1177 if (extent) {
1178 os << area->dimensions()[axis];
1179 } else {
1180 os << area->min()[axis];
1181 }
1182 g_print ("%s", os.str().c_str());
1183 } else {
1184 g_print("0");
1185 }
1186 }
1187 }
1189 static void
1190 do_query_all (SPDocument *doc)
1191 {
1192 SPObject *o = NULL;
1194 o = SP_DOCUMENT_ROOT(doc);
1196 if (o) {
1197 sp_document_ensure_up_to_date (doc);
1198 do_query_all_recurse(o);
1199 }
1200 }
1202 static void
1203 do_query_all_recurse (SPObject *o)
1204 {
1205 SPItem *item = ((SPItem *) o);
1206 if (o->id && SP_IS_ITEM(item)) {
1207 Geom::OptRect area = item->getBounds(sp_item_i2doc_affine(item));
1208 if (area) {
1209 Inkscape::SVGOStringStream os;
1210 os << o->id;
1211 os << "," << area->min()[Geom::X];
1212 os << "," << area->min()[Geom::Y];
1213 os << "," << area->dimensions()[Geom::X];
1214 os << "," << area->dimensions()[Geom::Y];
1215 g_print ("%s\n", os.str().c_str());
1216 }
1217 }
1219 SPObject *child = o->children;
1220 while (child) {
1221 do_query_all_recurse (child);
1222 child = child->next;
1223 }
1224 }
1227 static void
1228 sp_do_export_png(SPDocument *doc)
1229 {
1230 const gchar *filename = NULL;
1231 bool filename_from_hint = false;
1232 gdouble dpi = 0.0;
1234 if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1235 g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1236 }
1238 GSList *items = NULL;
1240 Geom::Rect area;
1241 if (sp_export_id || sp_export_area_drawing) {
1243 SPObject *o = NULL;
1244 SPObject *o_area = NULL;
1245 if (sp_export_id && sp_export_area_drawing) {
1246 o = doc->getObjectById(sp_export_id);
1247 o_area = SP_DOCUMENT_ROOT (doc);
1248 } else if (sp_export_id) {
1249 o = doc->getObjectById(sp_export_id);
1250 o_area = o;
1251 } else if (sp_export_area_drawing) {
1252 o = SP_DOCUMENT_ROOT (doc);
1253 o_area = o;
1254 }
1256 if (o) {
1257 if (!SP_IS_ITEM (o)) {
1258 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1259 return;
1260 }
1262 items = g_slist_prepend (items, SP_ITEM(o));
1264 if (sp_export_id_only) {
1265 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1266 }
1268 if (sp_export_use_hints) {
1270 // retrieve export filename hint
1271 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1272 if (fn_hint) {
1273 if (sp_export_png) {
1274 g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1275 filename = sp_export_png;
1276 } else {
1277 filename = fn_hint;
1278 filename_from_hint = true;
1279 }
1280 } else {
1281 g_warning ("Export filename hint not found for the object.");
1282 filename = sp_export_png;
1283 }
1285 // retrieve export dpi hints
1286 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1287 if (dpi_hint) {
1288 if (sp_export_dpi || sp_export_width || sp_export_height) {
1289 g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1290 } else {
1291 dpi = atof(dpi_hint);
1292 }
1293 } else {
1294 g_warning ("Export DPI hint not found for the object.");
1295 }
1297 }
1299 // write object bbox to area
1300 sp_document_ensure_up_to_date (doc);
1301 Geom::OptRect areaMaybe;
1302 sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2d_affine((SPItem *) o_area), TRUE);
1303 if (areaMaybe) {
1304 area = *areaMaybe;
1305 } else {
1306 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1307 return;
1308 }
1309 } else {
1310 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1311 return;
1312 }
1313 }
1315 if (sp_export_area) {
1316 /* Try to parse area (given in SVG pixels) */
1317 gdouble x0,y0,x1,y1;
1318 if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &x0, &y0, &x1, &y1) == 4) {
1319 g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1320 return;
1321 }
1322 area = Geom::Rect(Geom::Interval(x0,x1), Geom::Interval(y0,y1));
1323 } else if (sp_export_area_page || !(sp_export_id || sp_export_area_drawing)) {
1324 /* Export the whole page: note: Inkscape uses 'page' in all menus and dialogs, not 'canvas' */
1325 sp_document_ensure_up_to_date (doc);
1326 Geom::Point origin (SP_ROOT(doc->root)->x.computed, SP_ROOT(doc->root)->y.computed);
1327 area = Geom::Rect(origin, origin + sp_document_dimensions(doc));
1328 }
1330 // set filename and dpi from options, if not yet set from the hints
1331 if (!filename) {
1332 if (!sp_export_png) {
1333 g_warning ("No export filename given and no filename hint. Nothing exported.");
1334 return;
1335 }
1336 filename = sp_export_png;
1337 }
1339 if (sp_export_dpi && dpi == 0.0) {
1340 dpi = atof(sp_export_dpi);
1341 if ((dpi < 0.1) || (dpi > 10000.0)) {
1342 g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1343 return;
1344 }
1345 g_print("DPI: %g\n", dpi);
1346 }
1348 if (sp_export_area_snap) {
1349 round_rectangle_outwards(area);
1350 }
1352 // default dpi
1353 if (dpi == 0.0) {
1354 dpi = PX_PER_IN;
1355 }
1357 unsigned long int width = 0;
1358 unsigned long int height = 0;
1360 if (sp_export_width) {
1361 errno=0;
1362 width = strtoul(sp_export_width, NULL, 0);
1363 if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1364 g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1365 return;
1366 }
1367 dpi = (gdouble) width * PX_PER_IN / area.width();
1368 }
1370 if (sp_export_height) {
1371 errno=0;
1372 height = strtoul(sp_export_height, NULL, 0);
1373 if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1374 g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1375 return;
1376 }
1377 dpi = (gdouble) height * PX_PER_IN / area.height();
1378 }
1380 if (!sp_export_width) {
1381 width = (unsigned long int) (area.width() * dpi / PX_PER_IN + 0.5);
1382 }
1384 if (!sp_export_height) {
1385 height = (unsigned long int) (area.height() * dpi / PX_PER_IN + 0.5);
1386 }
1388 guint32 bgcolor = 0x00000000;
1389 if (sp_export_background) {
1390 // override the page color
1391 bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1392 bgcolor |= 0xff; // default is no opacity
1393 } else {
1394 // read from namedview
1395 Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1396 if (nv && nv->attribute("pagecolor"))
1397 bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1398 if (nv && nv->attribute("inkscape:pageopacity"))
1399 bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1400 }
1402 if (sp_export_background_opacity) {
1403 // override opacity
1404 gfloat value;
1405 if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1406 if (value > 1.0) {
1407 value = CLAMP (value, 1.0f, 255.0f);
1408 bgcolor &= (guint32) 0xffffff00;
1409 bgcolor |= (guint32) floor(value);
1410 } else {
1411 value = CLAMP (value, 0.0f, 1.0f);
1412 bgcolor &= (guint32) 0xffffff00;
1413 bgcolor |= SP_COLOR_F_TO_U(value);
1414 }
1415 }
1416 }
1418 gchar *path = 0;
1419 if (filename_from_hint) {
1420 //Make relative paths go from the document location, if possible:
1421 if (!g_path_is_absolute(filename) && doc->uri) {
1422 gchar *dirname = g_path_get_dirname(doc->uri);
1423 if (dirname) {
1424 path = g_build_filename(dirname, filename, NULL);
1425 g_free(dirname);
1426 }
1427 }
1428 if (!path) {
1429 path = g_strdup(filename);
1430 }
1431 } else {
1432 path = g_strdup(filename);
1433 }
1435 g_print("Background RRGGBBAA: %08x\n", bgcolor);
1437 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);
1439 g_print("Bitmap saved as: %s\n", filename);
1441 if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1442 sp_export_png_file(doc, path, area, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1443 } else {
1444 g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1445 }
1447 g_free (path);
1448 g_slist_free (items);
1449 }
1452 /**
1453 * Perform a PDF/PS/EPS export
1454 *
1455 * \param doc Document to export.
1456 * \param uri URI to export to.
1457 * \param mime MIME type to export as.
1458 */
1460 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1461 {
1462 Inkscape::Extension::DB::OutputList o;
1463 Inkscape::Extension::db.get_output_list(o);
1464 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1465 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1466 i++;
1467 }
1469 if (i == o.end())
1470 {
1471 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1472 return;
1473 }
1475 if (sp_export_id) {
1476 SPObject *o = doc->getObjectById(sp_export_id);
1477 if (o == NULL) {
1478 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1479 return;
1480 }
1481 (*i)->set_param_string ("exportId", sp_export_id);
1482 } else {
1483 (*i)->set_param_string ("exportId", "");
1484 }
1486 if (sp_export_area_page && sp_export_area_drawing) {
1487 g_warning ("You cannot use --export-area-page and --export-area-drawing at the same time; only the former will take effect.");
1488 sp_export_area_drawing = false;
1489 }
1491 if (sp_export_area_drawing) {
1492 (*i)->set_param_bool ("areaDrawing", TRUE);
1493 } else {
1494 (*i)->set_param_bool ("areaDrawing", FALSE);
1495 }
1497 if (sp_export_area_page) {
1498 if (sp_export_eps) {
1499 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.");
1500 }
1501 (*i)->set_param_bool ("areaPage", TRUE);
1502 } else {
1503 (*i)->set_param_bool ("areaPage", FALSE);
1504 }
1506 if (!sp_export_area_drawing && !sp_export_area_page && !sp_export_id) {
1507 // neither is set, set page as default for ps/pdf and drawing for eps
1508 if (sp_export_eps) {
1509 try {
1510 (*i)->set_param_bool("areaDrawing", TRUE);
1511 } catch (...) {}
1512 }
1513 }
1515 if (sp_export_text_to_path) {
1516 (*i)->set_param_bool("textToPath", TRUE);
1517 } else {
1518 (*i)->set_param_bool("textToPath", FALSE);
1519 }
1521 if (sp_export_ignore_filters) {
1522 (*i)->set_param_bool("blurToBitmap", FALSE);
1523 } else {
1524 (*i)->set_param_bool("blurToBitmap", TRUE);
1526 gdouble dpi = 90.0;
1527 if (sp_export_dpi) {
1528 dpi = atof(sp_export_dpi);
1529 if ((dpi < 1) || (dpi > 10000.0)) {
1530 g_warning("DPI value %s out of range [1 - 10000]. Using 90 dpi instead.", sp_export_dpi);
1531 dpi = 90;
1532 }
1533 }
1535 (*i)->set_param_int("resolution", (int) dpi);
1536 }
1538 (*i)->save(doc, uri);
1539 }
1541 #ifdef WIN32
1542 /**
1543 * Export a document to EMF
1544 *
1545 * \param doc Document to export.
1546 * \param uri URI to export to.
1547 * \param mime MIME type to export as (should be "image/x-emf")
1548 */
1550 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1551 {
1552 Inkscape::Extension::DB::OutputList o;
1553 Inkscape::Extension::db.get_output_list(o);
1554 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1555 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1556 i++;
1557 }
1559 if (i == o.end())
1560 {
1561 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1562 return;
1563 }
1565 (*i)->save(doc, uri);
1566 }
1567 #endif //WIN32
1569 #ifdef WIN32
1570 bool replaceArgs( int& argc, char**& argv )
1571 {
1572 bool worked = false;
1574 #ifdef REPLACEARGS_DEBUG
1575 MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1576 #endif // REPLACEARGS_DEBUG
1578 wchar_t* line = GetCommandLineW();
1579 if ( line )
1580 {
1581 #ifdef REPLACEARGS_DEBUG
1582 {
1583 gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1584 if ( utf8Line )
1585 {
1586 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1587 {
1588 char tmp[strlen(safe) + 32];
1589 snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1590 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1591 }
1592 }
1593 }
1594 #endif // REPLACEARGS_DEBUG
1596 int numArgs = 0;
1597 wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1599 #ifdef REPLACEARGS_ANSI
1600 // test code for trying things on Win95/98/ME
1601 if ( !parsed )
1602 {
1603 #ifdef REPLACEARGS_DEBUG
1604 MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1605 #endif // REPLACEARGS_DEBUG
1606 int lineLen = wcslen(line) + 1;
1607 wchar_t* lineDup = new wchar_t[lineLen];
1608 wcsncpy( lineDup, line, lineLen );
1610 int pos = 0;
1611 bool inQuotes = false;
1612 bool inWhitespace = true;
1613 std::vector<int> places;
1614 while ( lineDup[pos] )
1615 {
1616 if ( inQuotes )
1617 {
1618 if ( lineDup[pos] == L'"' )
1619 {
1620 inQuotes = false;
1621 }
1622 }
1623 else if ( lineDup[pos] == L'"' )
1624 {
1625 inQuotes = true;
1626 inWhitespace = false;
1627 places.push_back(pos);
1628 }
1629 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1630 {
1631 if ( !inWhitespace )
1632 {
1633 inWhitespace = true;
1634 lineDup[pos] = 0;
1635 }
1636 }
1637 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1638 {
1639 inWhitespace = false;
1640 places.push_back(pos);
1641 }
1642 else
1643 {
1644 // consume
1645 }
1646 pos++;
1647 }
1648 #ifdef REPLACEARGS_DEBUG
1649 {
1650 char tmp[256];
1651 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1652 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1653 }
1654 #endif // REPLACEARGS_DEBUG
1656 wchar_t** block = new wchar_t*[places.size()];
1657 int i = 0;
1658 for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1659 {
1660 block[i++] = &lineDup[*it];
1661 }
1662 parsed = block;
1663 numArgs = places.size();
1664 }
1665 #endif // REPLACEARGS_ANSI
1667 if ( parsed )
1668 {
1669 std::vector<wchar_t*>expandedArgs;
1670 if ( numArgs > 0 )
1671 {
1672 expandedArgs.push_back( parsed[0] );
1673 }
1675 for ( int i1 = 1; i1 < numArgs; i1++ )
1676 {
1677 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1678 wildcarded &= parsed[i1][0] != L'"';
1679 wildcarded &= parsed[i1][0] != L'-';
1680 if ( wildcarded )
1681 {
1682 #ifdef REPLACEARGS_ANSI
1683 WIN32_FIND_DATAA data;
1684 #else
1685 WIN32_FIND_DATAW data;
1686 #endif // REPLACEARGS_ANSI
1688 memset((void *)&data, 0, sizeof(data));
1690 int baseLen = wcslen(parsed[i1]) + 2;
1691 wchar_t* base = new wchar_t[baseLen];
1692 wcsncpy( base, parsed[i1], baseLen );
1693 wchar_t* last = wcsrchr( base, L'\\' );
1694 if ( last )
1695 {
1696 last[1] = 0;
1697 }
1698 else
1699 {
1700 base[0] = 0;
1701 }
1702 baseLen = wcslen( base );
1704 #ifdef REPLACEARGS_ANSI
1705 char target[MAX_PATH];
1706 if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1707 {
1708 HANDLE hf = FindFirstFileA( target, &data );
1709 #else
1710 HANDLE hf = FindFirstFileW( parsed[i1], &data );
1711 #endif // REPLACEARGS_ANSI
1712 if ( hf != INVALID_HANDLE_VALUE )
1713 {
1714 BOOL found = TRUE;
1715 do
1716 {
1717 #ifdef REPLACEARGS_ANSI
1718 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1719 if ( howMany > 0 )
1720 {
1721 howMany += baseLen;
1722 wchar_t* tmp = new wchar_t[howMany + 1];
1723 wcsncpy( tmp, base, howMany + 1 );
1724 MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1725 expandedArgs.push_back( tmp );
1726 found = FindNextFileA( hf, &data );
1727 }
1728 #else
1729 int howMany = wcslen(data.cFileName) + baseLen;
1730 wchar_t* tmp = new wchar_t[howMany + 1];
1731 wcsncpy( tmp, base, howMany + 1 );
1732 wcsncat( tmp, data.cFileName, howMany + 1 );
1733 expandedArgs.push_back( tmp );
1734 found = FindNextFileW( hf, &data );
1735 #endif // REPLACEARGS_ANSI
1736 } while ( found );
1738 FindClose( hf );
1739 }
1740 else
1741 {
1742 expandedArgs.push_back( parsed[i1] );
1743 }
1744 #ifdef REPLACEARGS_ANSI
1745 }
1746 #endif // REPLACEARGS_ANSI
1748 delete[] base;
1749 }
1750 else
1751 {
1752 expandedArgs.push_back( parsed[i1] );
1753 }
1754 }
1756 {
1757 wchar_t** block = new wchar_t*[expandedArgs.size()];
1758 int iz = 0;
1759 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1760 {
1761 block[iz++] = *it;
1762 }
1763 parsed = block;
1764 numArgs = expandedArgs.size();
1765 }
1767 std::vector<gchar*> newArgs;
1768 for ( int i = 0; i < numArgs; i++ )
1769 {
1770 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1771 if ( replacement )
1772 {
1773 #ifdef REPLACEARGS_DEBUG
1774 gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1776 if ( safe2 )
1777 {
1778 {
1779 char tmp[1024];
1780 snprintf( tmp, sizeof(tmp), " [%2d] = '%s'", i, safe2 );
1781 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1782 }
1783 g_free( safe2 );
1784 }
1785 #endif // REPLACEARGS_DEBUG
1787 newArgs.push_back( replacement );
1788 }
1789 else
1790 {
1791 newArgs.push_back( blankParam );
1792 }
1793 }
1795 // Now push our munged params to be the new argv and argc
1796 {
1797 char** block = new char*[newArgs.size()];
1798 int iz = 0;
1799 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1800 {
1801 block[iz++] = *it;
1802 }
1803 argv = block;
1804 argc = newArgs.size();
1805 worked = true;
1806 }
1807 }
1808 #ifdef REPLACEARGS_DEBUG
1809 else
1810 {
1811 MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1812 }
1813 #endif // REPLACEARGS_DEBUG
1814 }
1815 #ifdef REPLACEARGS_DEBUG
1816 else
1817 {
1818 {
1819 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1820 }
1822 char* line2 = GetCommandLineA();
1823 if ( line2 )
1824 {
1825 gchar *safe = Inkscape::IO::sanitizeString(line2);
1826 {
1827 {
1828 char tmp[strlen(safe) + 32];
1829 snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1830 MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1831 }
1832 }
1833 }
1834 else
1835 {
1836 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1837 }
1838 }
1839 #endif // REPLACEARGS_DEBUG
1841 return worked;
1842 }
1843 #endif // WIN32
1845 static GSList *
1846 sp_process_args(poptContext ctx)
1847 {
1848 GSList *fl = NULL;
1850 gint a;
1851 while ((a = poptGetNextOpt(ctx)) != -1) {
1852 switch (a) {
1853 case SP_ARG_FILE: {
1854 gchar const *fn = poptGetOptArg(ctx);
1855 if (fn != NULL) {
1856 fl = g_slist_append(fl, g_strdup(fn));
1857 }
1858 break;
1859 }
1860 case SP_ARG_VERSION: {
1861 printf("Inkscape %s (%s)\n", Inkscape::version_string, __DATE__);
1862 exit(0);
1863 break;
1864 }
1865 case SP_ARG_EXTENSIONDIR: {
1866 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1867 exit(0);
1868 break;
1869 }
1870 case SP_ARG_VERB_LIST: {
1871 // This really shouldn't go here, we should init the app.
1872 // But, since we're just exiting in this path, there is
1873 // no harm, and this is really a better place to put
1874 // everything else.
1875 Inkscape::Extension::init();
1876 Inkscape::Verb::list();
1877 exit(0);
1878 break;
1879 }
1880 case SP_ARG_VERB:
1881 case SP_ARG_SELECT: {
1882 gchar const *arg = poptGetOptArg(ctx);
1883 if (arg != NULL) {
1884 // printf("Adding in: %s\n", arg);
1885 new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1886 }
1887 break;
1888 }
1889 case POPT_ERROR_BADOPT: {
1890 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1891 exit(1);
1892 break;
1893 }
1894 default: {
1895 break;
1896 }
1897 }
1898 }
1900 gchar const ** const args = poptGetArgs(ctx);
1901 if (args != NULL) {
1902 for (unsigned i = 0; args[i] != NULL; i++) {
1903 fl = g_slist_append(fl, g_strdup(args[i]));
1904 }
1905 }
1907 return fl;
1908 }
1911 /*
1912 Local Variables:
1913 mode:c++
1914 c-file-style:"stroustrup"
1915 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1916 indent-tabs-mode:nil
1917 fill-column:99
1918 End:
1919 */
1920 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :