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