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