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 #include <gtk/gtkmessagedialog.h>
33 #ifdef HAVE_IEEEFP_H
34 #include <ieeefp.h>
35 #endif
36 #include <cstring>
37 #include <string>
38 #include <locale.h>
40 #include <popt.h>
41 #ifndef POPT_TABLEEND
42 #define POPT_TABLEEND { NULL, '\0', 0, 0, 0, NULL, NULL }
43 #endif /* Not def: POPT_TABLEEND */
45 #include <libxml/tree.h>
46 #include <glib-object.h>
47 #include <gtk/gtkmain.h>
48 #include <gtk/gtksignal.h>
49 #include <gtk/gtkwindow.h>
50 #include <gtk/gtkbox.h>
52 #include "gc-core.h"
54 #include "macros.h"
55 #include "file.h"
56 #include "document.h"
57 #include "sp-object.h"
58 #include "interface.h"
59 #include "print.h"
60 #include "color.h"
61 #include "sp-item.h"
62 #include "sp-root.h"
63 #include "unit-constants.h"
65 #include "svg/svg.h"
66 #include "svg/svg-color.h"
67 #include "svg/stringstream.h"
69 #include "inkscape-private.h"
70 #include "inkscape-stock.h"
71 #include "inkscape_version.h"
73 #include "sp-namedview.h"
74 #include "sp-guide.h"
75 #include "sp-object-repr.h"
76 #include "xml/repr.h"
78 #include "io/sys.h"
80 #include "debug/logger.h"
81 #include "debug/log-display-config.h"
83 #include "helper/png-write.h"
85 #include <extension/extension.h>
86 #include <extension/system.h>
87 #include <extension/db.h>
88 #include <extension/output.h>
90 #ifdef WIN32
91 //#define REPLACEARGS_ANSI
92 //#define REPLACEARGS_DEBUG
94 #include "registrytool.h"
96 #include "extension/internal/win32.h"
97 using Inkscape::Extension::Internal::PrintWin32;
99 #endif // WIN32
101 #include "extension/init.h"
103 #include <glibmm/i18n.h>
104 #include <gtkmm/main.h>
106 #ifndef HAVE_BIND_TEXTDOMAIN_CODESET
107 #define bind_textdomain_codeset(p,c)
108 #endif
110 #include "application/application.h"
112 #include "main-cmdlineact.h"
114 #include <png.h>
115 #include <errno.h>
117 enum {
118 SP_ARG_NONE,
119 SP_ARG_NOGUI,
120 SP_ARG_GUI,
121 SP_ARG_FILE,
122 SP_ARG_PRINT,
123 SP_ARG_EXPORT_PNG,
124 SP_ARG_EXPORT_DPI,
125 SP_ARG_EXPORT_AREA,
126 SP_ARG_EXPORT_AREA_DRAWING,
127 SP_ARG_EXPORT_AREA_CANVAS,
128 SP_ARG_EXPORT_AREA_SNAP,
129 SP_ARG_EXPORT_WIDTH,
130 SP_ARG_EXPORT_HEIGHT,
131 SP_ARG_EXPORT_ID,
132 SP_ARG_EXPORT_ID_ONLY,
133 SP_ARG_EXPORT_USE_HINTS,
134 SP_ARG_EXPORT_BACKGROUND,
135 SP_ARG_EXPORT_BACKGROUND_OPACITY,
136 SP_ARG_EXPORT_SVG,
137 SP_ARG_EXPORT_PS,
138 SP_ARG_EXPORT_EPS,
139 SP_ARG_EXPORT_PDF,
140 #ifdef WIN32
141 SP_ARG_EXPORT_EMF,
142 #endif //WIN32
143 SP_ARG_EXPORT_TEXT_TO_PATH,
144 SP_ARG_EXTENSIONDIR,
145 SP_ARG_QUERY_X,
146 SP_ARG_QUERY_Y,
147 SP_ARG_QUERY_WIDTH,
148 SP_ARG_QUERY_HEIGHT,
149 SP_ARG_QUERY_ALL,
150 SP_ARG_QUERY_ID,
151 SP_ARG_SHELL,
152 SP_ARG_VERSION,
153 SP_ARG_VACUUM_DEFS,
154 SP_ARG_VERB_LIST,
155 SP_ARG_VERB,
156 SP_ARG_SELECT,
157 SP_ARG_LAST
158 };
160 int sp_main_gui(int argc, char const **argv);
161 int sp_main_console(int argc, char const **argv);
162 static void sp_do_export_png(SPDocument *doc);
163 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const *mime);
164 #ifdef WIN32
165 static void do_export_emf(SPDocument* doc, gchar const* uri, char const *mime);
166 #endif //WIN32
167 static void do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id);
168 static void do_query_all (SPDocument *doc);
169 static void do_query_all_recurse (SPObject *o);
171 static gchar *sp_global_printer = NULL;
172 static gchar *sp_export_png = NULL;
173 static gchar *sp_export_dpi = NULL;
174 static gchar *sp_export_area = NULL;
175 static gboolean sp_export_area_drawing = FALSE;
176 static gboolean sp_export_area_canvas = FALSE;
177 static gchar *sp_export_width = NULL;
178 static gchar *sp_export_height = NULL;
179 static gchar *sp_export_id = NULL;
180 static gchar *sp_export_background = NULL;
181 static gchar *sp_export_background_opacity = NULL;
182 static gboolean sp_export_area_snap = FALSE;
183 static gboolean sp_export_use_hints = FALSE;
184 static gboolean sp_export_id_only = FALSE;
185 static gchar *sp_export_svg = NULL;
186 static gchar *sp_export_ps = NULL;
187 static gchar *sp_export_eps = NULL;
188 static gchar *sp_export_pdf = NULL;
189 #ifdef WIN32
190 static gchar *sp_export_emf = NULL;
191 #endif //WIN32
192 static gboolean sp_export_text_to_path = FALSE;
193 static gboolean sp_export_font = FALSE;
194 static gboolean sp_query_x = FALSE;
195 static gboolean sp_query_y = FALSE;
196 static gboolean sp_query_width = FALSE;
197 static gboolean sp_query_height = FALSE;
198 static gboolean sp_query_all = FALSE;
199 static gchar *sp_query_id = NULL;
200 static int sp_new_gui = FALSE;
201 static gboolean sp_shell = FALSE;
202 static gboolean sp_vacuum_defs = FALSE;
204 static gchar *sp_export_png_utf8 = NULL;
205 static gchar *sp_export_svg_utf8 = NULL;
206 static gchar *sp_global_printer_utf8 = NULL;
209 /**
210 * Reset variables to default values.
211 */
212 static void resetCommandlineGlobals() {
213 sp_global_printer = NULL;
214 sp_export_png = NULL;
215 sp_export_dpi = NULL;
216 sp_export_area = NULL;
217 sp_export_area_drawing = FALSE;
218 sp_export_area_canvas = FALSE;
219 sp_export_width = NULL;
220 sp_export_height = NULL;
221 sp_export_id = NULL;
222 sp_export_background = NULL;
223 sp_export_background_opacity = NULL;
224 sp_export_area_snap = FALSE;
225 sp_export_use_hints = FALSE;
226 sp_export_id_only = FALSE;
227 sp_export_svg = NULL;
228 sp_export_ps = NULL;
229 sp_export_eps = NULL;
230 sp_export_pdf = NULL;
231 #ifdef WIN32
232 sp_export_emf = NULL;
233 #endif //WIN32
234 sp_export_text_to_path = FALSE;
235 sp_export_font = FALSE;
236 sp_query_x = FALSE;
237 sp_query_y = FALSE;
238 sp_query_width = FALSE;
239 sp_query_height = FALSE;
240 sp_query_all = FALSE;
241 sp_query_id = NULL;
242 sp_vacuum_defs = FALSE;
244 sp_export_png_utf8 = NULL;
245 sp_export_svg_utf8 = NULL;
246 sp_global_printer_utf8 = NULL;
247 }
249 #ifdef WIN32
250 static bool replaceArgs( int& argc, char**& argv );
251 #endif
252 static GSList *sp_process_args(poptContext ctx);
253 struct poptOption options[] = {
254 {"version", 'V',
255 POPT_ARG_NONE, NULL, SP_ARG_VERSION,
256 N_("Print the Inkscape version number"),
257 NULL},
259 {"without-gui", 'z',
260 POPT_ARG_NONE, NULL, SP_ARG_NOGUI,
261 N_("Do not use X server (only process files from console)"),
262 NULL},
264 {"with-gui", 'g',
265 POPT_ARG_NONE, NULL, SP_ARG_GUI,
266 N_("Try to use X server (even if $DISPLAY is not set)"),
267 NULL},
269 {"file", 'f',
270 POPT_ARG_STRING, NULL, SP_ARG_FILE,
271 N_("Open specified document(s) (option string may be excluded)"),
272 N_("FILENAME")},
274 {"print", 'p',
275 POPT_ARG_STRING, &sp_global_printer, SP_ARG_PRINT,
276 N_("Print document(s) to specified output file (use '| program' for pipe)"),
277 N_("FILENAME")},
279 {"export-png", 'e',
280 POPT_ARG_STRING, &sp_export_png, SP_ARG_EXPORT_PNG,
281 N_("Export document to a PNG file"),
282 N_("FILENAME")},
284 {"export-dpi", 'd',
285 POPT_ARG_STRING, &sp_export_dpi, SP_ARG_EXPORT_DPI,
286 N_("The resolution used for exporting SVG into bitmap (default 90)"),
287 N_("DPI")},
289 {"export-area", 'a',
290 POPT_ARG_STRING, &sp_export_area, SP_ARG_EXPORT_AREA,
291 N_("Exported area in SVG user units (default is the canvas; 0,0 is lower-left corner)"),
292 N_("x0:y0:x1:y1")},
294 {"export-area-drawing", 'D',
295 POPT_ARG_NONE, &sp_export_area_drawing, SP_ARG_EXPORT_AREA_DRAWING,
296 N_("Exported area is the entire drawing (not canvas)"),
297 NULL},
299 {"export-area-canvas", 'C',
300 POPT_ARG_NONE, &sp_export_area_canvas, SP_ARG_EXPORT_AREA_CANVAS,
301 N_("Exported area is the entire canvas"),
302 NULL},
304 {"export-area-snap", 0,
305 POPT_ARG_NONE, &sp_export_area_snap, SP_ARG_EXPORT_AREA_SNAP,
306 N_("Snap the bitmap export area outwards to the nearest integer values (in SVG user units)"),
307 NULL},
309 {"export-width", 'w',
310 POPT_ARG_STRING, &sp_export_width, SP_ARG_EXPORT_WIDTH,
311 N_("The width of exported bitmap in pixels (overrides export-dpi)"),
312 N_("WIDTH")},
314 {"export-height", 'h',
315 POPT_ARG_STRING, &sp_export_height, SP_ARG_EXPORT_HEIGHT,
316 N_("The height of exported bitmap in pixels (overrides export-dpi)"),
317 N_("HEIGHT")},
319 {"export-id", 'i',
320 POPT_ARG_STRING, &sp_export_id, SP_ARG_EXPORT_ID,
321 N_("The ID of the object to export"),
322 N_("ID")},
324 {"export-id-only", 'j',
325 POPT_ARG_NONE, &sp_export_id_only, SP_ARG_EXPORT_ID_ONLY,
326 // TRANSLATORS: this means: "Only export the object whose id is given in --export-id".
327 // See "man inkscape" for details.
328 N_("Export just the object with export-id, hide all others (only with export-id)"),
329 NULL},
331 {"export-use-hints", 't',
332 POPT_ARG_NONE, &sp_export_use_hints, SP_ARG_EXPORT_USE_HINTS,
333 N_("Use stored filename and DPI hints when exporting (only with export-id)"),
334 NULL},
336 {"export-background", 'b',
337 POPT_ARG_STRING, &sp_export_background, SP_ARG_EXPORT_BACKGROUND,
338 N_("Background color of exported bitmap (any SVG-supported color string)"),
339 N_("COLOR")},
341 {"export-background-opacity", 'y',
342 POPT_ARG_STRING, &sp_export_background_opacity, SP_ARG_EXPORT_BACKGROUND_OPACITY,
343 N_("Background opacity of exported bitmap (either 0.0 to 1.0, or 1 to 255)"),
344 N_("VALUE")},
346 {"export-plain-svg", 'l',
347 POPT_ARG_STRING, &sp_export_svg, SP_ARG_EXPORT_SVG,
348 N_("Export document to plain SVG file (no sodipodi or inkscape namespaces)"),
349 N_("FILENAME")},
351 {"export-ps", 'P',
352 POPT_ARG_STRING, &sp_export_ps, SP_ARG_EXPORT_PS,
353 N_("Export document to a PS file"),
354 N_("FILENAME")},
356 {"export-eps", 'E',
357 POPT_ARG_STRING, &sp_export_eps, SP_ARG_EXPORT_EPS,
358 N_("Export document to an EPS file"),
359 N_("FILENAME")},
361 {"export-pdf", 'A',
362 POPT_ARG_STRING, &sp_export_pdf, SP_ARG_EXPORT_PDF,
363 N_("Export document to a PDF file"),
364 N_("FILENAME")},
366 #ifdef WIN32
367 {"export-emf", 'M',
368 POPT_ARG_STRING, &sp_export_emf, SP_ARG_EXPORT_EMF,
369 N_("Export document to an Enhanced Metafile (EMF) File"),
370 N_("FILENAME")},
371 #endif //WIN32
373 {"export-text-to-path", 'T',
374 POPT_ARG_NONE, &sp_export_text_to_path, SP_ARG_EXPORT_TEXT_TO_PATH,
375 N_("Convert text object to paths on export (EPS)"),
376 NULL},
378 {"query-x", 'X',
379 POPT_ARG_NONE, &sp_query_x, SP_ARG_QUERY_X,
380 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
381 N_("Query the X coordinate of the drawing or, if specified, of the object with --query-id"),
382 NULL},
384 {"query-y", 'Y',
385 POPT_ARG_NONE, &sp_query_y, SP_ARG_QUERY_Y,
386 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
387 N_("Query the Y coordinate of the drawing or, if specified, of the object with --query-id"),
388 NULL},
390 {"query-width", 'W',
391 POPT_ARG_NONE, &sp_query_width, SP_ARG_QUERY_WIDTH,
392 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
393 N_("Query the width of the drawing or, if specified, of the object with --query-id"),
394 NULL},
396 {"query-height", 'H',
397 POPT_ARG_NONE, &sp_query_height, SP_ARG_QUERY_HEIGHT,
398 // TRANSLATORS: "--query-id" is an Inkscape command line option; see "inkscape --help"
399 N_("Query the height of the drawing or, if specified, of the object with --query-id"),
400 NULL},
402 {"query-all", 'S',
403 POPT_ARG_NONE, &sp_query_all, SP_ARG_QUERY_ALL,
404 N_("List id,x,y,w,h for all objects"),
405 NULL},
407 {"query-id", 'I',
408 POPT_ARG_STRING, &sp_query_id, SP_ARG_QUERY_ID,
409 N_("The ID of the object whose dimensions are queried"),
410 N_("ID")},
412 {"extension-directory", 'x',
413 POPT_ARG_NONE, NULL, SP_ARG_EXTENSIONDIR,
414 // TRANSLATORS: this option makes Inkscape print the name (path) of the extension directory
415 N_("Print out the extension directory and exit"),
416 NULL},
418 {"vacuum-defs", 0,
419 POPT_ARG_NONE, &sp_vacuum_defs, SP_ARG_VACUUM_DEFS,
420 N_("Remove unused definitions from the defs section(s) of the document"),
421 NULL},
423 {"verb-list", 0,
424 POPT_ARG_NONE, NULL, SP_ARG_VERB_LIST,
425 N_("List the IDs of all the verbs in Inkscape"),
426 NULL},
428 {"verb", 0,
429 POPT_ARG_STRING, NULL, SP_ARG_VERB,
430 N_("Verb to call when Inkscape opens."),
431 N_("VERB-ID")},
433 {"select", 0,
434 POPT_ARG_STRING, NULL, SP_ARG_SELECT,
435 N_("Object ID to select when Inkscape opens."),
436 N_("OBJECT-ID")},
438 {"shell", 0,
439 POPT_ARG_NONE, &sp_shell, SP_ARG_SHELL,
440 N_("Start Inkscape in interative shell mode."),
441 NULL},
443 POPT_AUTOHELP POPT_TABLEEND
444 };
446 static bool needToRecodeParams = true;
447 gchar * blankParam = g_strdup("");
451 #ifdef WIN32
453 /**
454 * Return the directory of the .exe that is currently running
455 */
456 static Glib::ustring _win32_getExePath()
457 {
458 char exeName[MAX_PATH+1];
459 GetModuleFileName(NULL, exeName, MAX_PATH);
460 char *slashPos = strrchr(exeName, '\\');
461 if (slashPos)
462 *slashPos = '\0';
463 Glib::ustring s = exeName;
464 return s;
465 }
467 /**
468 * Set up the PATH and PYTHONPATH environment variables on
469 * win32
470 */
471 static int _win32_set_inkscape_env(const Glib::ustring &exePath)
472 {
474 char *oldenv = getenv("PATH");
475 Glib::ustring tmp = "PATH=";
476 tmp += exePath;
477 tmp += ";";
478 tmp += exePath;
479 tmp += "\\python;";
480 tmp += exePath;
481 tmp += "\\python\\Scripts;"; // for uniconv.cmd
482 tmp += exePath;
483 tmp += "\\perl";
484 if(oldenv != NULL) {
485 tmp += ";";
486 tmp += oldenv;
487 }
488 _putenv(tmp.c_str());
490 oldenv = getenv("PYTHONPATH");
491 tmp = "PYTHONPATH=";
492 tmp += exePath;
493 tmp += "\\python;";
494 tmp += exePath;
495 tmp += "\\python\\Lib;";
496 tmp += exePath;
497 tmp += "\\python\\DLLs";
498 if(oldenv != NULL) {
499 tmp += ";";
500 tmp += oldenv;
501 }
502 _putenv(tmp.c_str());
504 return 0;
505 }
506 #endif
508 /**
509 * Add INKSCAPE_EXTENSIONDIR to PYTHONPATH so that extensions in users home
510 * can find inkex.py et al. (Bug #197475)
511 */
512 static int set_extensions_env()
513 {
514 char *oldenv = getenv("PYTHONPATH");
515 Glib::ustring tmp = INKSCAPE_EXTENSIONDIR;
516 if (oldenv != NULL) {
517 #ifdef WIN32
518 tmp += ";";
519 #else
520 tmp += ":";
521 #endif
522 tmp += oldenv;
523 }
524 setenv("PYTHONPATH", tmp.c_str(), 1);
526 return 0;
527 }
530 /**
531 * This is the classic main() entry point of the program, though on some
532 * architectures it might be called by something else.
533 */
534 int
535 main(int argc, char **argv)
536 {
537 #ifdef HAVE_FPSETMASK
538 /* This is inherited from Sodipodi code, where it was in #ifdef __FreeBSD__. It's probably
539 safe to remove: the default mask is already 0 in C99, and in current FreeBSD according to
540 the fenv man page on www.freebsd.org, and in glibc according to (libc)FP Exceptions. */
541 fpsetmask(fpgetmask() & ~(FP_X_DZ | FP_X_INV));
542 #endif
544 #ifdef WIN32
545 /*
546 Set the current directory to the directory of the
547 executable. This seems redundant, but is needed for
548 when inkscape.exe is executed from another directory.
549 We use relative paths on win32.
550 HKCR\svgfile\shell\open\command is a good example
551 */
552 Glib::ustring homedir = _win32_getExePath();
553 SetCurrentDirectory(homedir.c_str());
554 _win32_set_inkscape_env(homedir);
555 RegistryTool rt;
556 rt.setPathInfo();
557 #endif
559 // Bug #197475
560 set_extensions_env();
562 /**
563 * Call bindtextdomain() for various machines's paths
564 */
565 #ifdef ENABLE_NLS
566 #ifdef WIN32
567 Glib::ustring localePath = homedir;
568 localePath += "\\";
569 localePath += PACKAGE_LOCALE_DIR;
570 bindtextdomain(GETTEXT_PACKAGE, localePath.c_str());
571 #else
572 #ifdef ENABLE_BINRELOC
573 bindtextdomain(GETTEXT_PACKAGE, BR_LOCALEDIR(""));
574 #else
575 bindtextdomain(GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
576 #endif
577 #endif
578 // Allow the user to override the locale directory by setting
579 // the environment variable INKSCAPE_LOCALEDIR.
580 char *inkscape_localedir = getenv("INKSCAPE_LOCALEDIR");
581 if (inkscape_localedir != NULL) {
582 bindtextdomain(GETTEXT_PACKAGE, inkscape_localedir);
583 }
584 #endif
586 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
588 #ifdef ENABLE_NLS
589 textdomain(GETTEXT_PACKAGE);
590 #endif
592 LIBXML_TEST_VERSION
594 Inkscape::GC::init();
596 Inkscape::Debug::Logger::init();
598 gboolean use_gui;
600 #ifndef WIN32
601 use_gui = (getenv("DISPLAY") != NULL);
602 #else
603 use_gui = TRUE;
604 #endif
605 /* Test whether with/without GUI is forced */
606 for (int i = 1; i < argc; i++) {
607 if (!strcmp(argv[i], "-z")
608 || !strcmp(argv[i], "--without-gui")
609 || !strcmp(argv[i], "-p")
610 || !strncmp(argv[i], "--print", 7)
611 || !strcmp(argv[i], "-e")
612 || !strncmp(argv[i], "--export-png", 12)
613 || !strcmp(argv[i], "-l")
614 || !strncmp(argv[i], "--export-plain-svg", 12)
615 || !strcmp(argv[i], "-i")
616 || !strncmp(argv[i], "--export-area-drawing", 21)
617 || !strcmp(argv[i], "-D")
618 || !strncmp(argv[i], "--export-area-canvas", 20)
619 || !strcmp(argv[i], "-C")
620 || !strncmp(argv[i], "--export-id", 12)
621 || !strcmp(argv[i], "-P")
622 || !strncmp(argv[i], "--export-ps", 11)
623 || !strcmp(argv[i], "-E")
624 || !strncmp(argv[i], "--export-eps", 12)
625 || !strcmp(argv[i], "-A")
626 || !strncmp(argv[i], "--export-pdf", 12)
627 #ifdef WIN32
628 || !strcmp(argv[i], "-M")
629 || !strncmp(argv[i], "--export-emf", 12)
630 #endif //WIN32
631 || !strcmp(argv[i], "-W")
632 || !strncmp(argv[i], "--query-width", 13)
633 || !strcmp(argv[i], "-H")
634 || !strncmp(argv[i], "--query-height", 14)
635 || !strcmp(argv[i], "-S")
636 || !strncmp(argv[i], "--query-all", 11)
637 || !strcmp(argv[i], "-X")
638 || !strncmp(argv[i], "--query-x", 13)
639 || !strcmp(argv[i], "-Y")
640 || !strncmp(argv[i], "--query-y", 14)
641 || !strcmp(argv[i], "--vacuum-defs")
642 || !strncmp(argv[i], "--shell", 7)
643 )
644 {
645 /* main_console handles any exports -- not the gui */
646 use_gui = FALSE;
647 break;
648 } else if (!strcmp(argv[i], "-g") || !strcmp(argv[i], "--with-gui")) {
649 use_gui = TRUE;
650 break;
651 }
652 }
654 #ifdef WIN32
655 #ifndef REPLACEARGS_ANSI
656 if ( PrintWin32::is_os_wide() )
657 #endif // REPLACEARGS_ANSI
658 {
659 // If the call fails, we'll need to convert charsets
660 needToRecodeParams = !replaceArgs( argc, argv );
661 }
662 #endif // WIN32
664 /// \todo Should this be a static object (see inkscape.cpp)?
665 Inkscape::NSApplication::Application app(argc, argv, use_gui, sp_new_gui);
667 return app.run();
668 }
673 void fixupSingleFilename( gchar **orig, gchar **spare )
674 {
675 if ( orig && *orig && **orig ) {
676 GError *error = NULL;
677 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(*orig, -1, NULL, NULL, &error);
678 if ( newFileName )
679 {
680 *orig = newFileName;
681 if ( spare ) {
682 *spare = newFileName;
683 }
684 // g_message("Set a replacement fixup");
685 }
686 }
687 }
691 GSList *fixupFilenameEncoding( GSList* fl )
692 {
693 GSList *newFl = NULL;
694 while ( fl ) {
695 gchar *fn = static_cast<gchar*>(fl->data);
696 fl = g_slist_remove( fl, fl->data );
697 gchar *newFileName = Inkscape::IO::locale_to_utf8_fallback(fn, -1, NULL, NULL, NULL);
698 if ( newFileName ) {
700 if ( 0 )
701 {
702 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
703 gchar *safeNewFn = Inkscape::IO::sanitizeString(newFileName);
704 GtkWidget *w = gtk_message_dialog_new( NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK,
705 "Note: Converted '%s' to '%s'", safeFn, safeNewFn );
706 gtk_dialog_run (GTK_DIALOG (w));
707 gtk_widget_destroy (w);
708 g_free(safeNewFn);
709 g_free(safeFn);
710 }
712 g_free( fn );
713 fn = newFileName;
714 newFileName = 0;
715 }
716 else
717 if ( 0 )
718 {
719 gchar *safeFn = Inkscape::IO::sanitizeString(fn);
720 GtkWidget *w = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, "Error: Unable to convert '%s'", safeFn );
721 gtk_dialog_run (GTK_DIALOG (w));
722 gtk_widget_destroy (w);
723 g_free(safeFn);
724 }
725 newFl = g_slist_append( newFl, fn );
726 }
727 return newFl;
728 }
730 int sp_common_main( int argc, char const **argv, GSList **flDest )
731 {
732 /// \todo fixme: Move these to some centralized location (Lauris)
733 sp_object_type_register("sodipodi:namedview", SP_TYPE_NAMEDVIEW);
734 sp_object_type_register("sodipodi:guide", SP_TYPE_GUIDE);
737 // temporarily switch gettext encoding to locale, so that help messages can be output properly
738 gchar const *charset;
739 g_get_charset(&charset);
741 bind_textdomain_codeset(GETTEXT_PACKAGE, charset);
743 poptContext ctx = poptGetContext(NULL, argc, argv, options, 0);
744 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
745 g_return_val_if_fail(ctx != NULL, 1);
747 /* Collect own arguments */
748 GSList *fl = sp_process_args(ctx);
749 poptFreeContext(ctx);
751 // now switch gettext back to UTF-8 (for GUI)
752 bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
754 // Now let's see if the file list still holds up
755 if ( needToRecodeParams )
756 {
757 fl = fixupFilenameEncoding( fl );
758 }
760 // Check the globals for filename-fixup
761 if ( needToRecodeParams )
762 {
763 fixupSingleFilename( &sp_export_png, &sp_export_png_utf8 );
764 fixupSingleFilename( &sp_export_svg, &sp_export_svg_utf8 );
765 fixupSingleFilename( &sp_global_printer, &sp_global_printer_utf8 );
766 }
767 else
768 {
769 if ( sp_export_png )
770 sp_export_png_utf8 = g_strdup( sp_export_png );
771 if ( sp_export_svg )
772 sp_export_svg_utf8 = g_strdup( sp_export_svg );
773 if ( sp_global_printer )
774 sp_global_printer_utf8 = g_strdup( sp_global_printer );
775 }
777 // Return the list if wanted, else free it up.
778 if ( flDest ) {
779 *flDest = fl;
780 fl = 0;
781 } else {
782 while ( fl ) {
783 g_free( fl->data );
784 fl = g_slist_remove( fl, fl->data );
785 }
786 }
787 return 0;
788 }
790 static void
791 snooper(GdkEvent *event, gpointer /*data*/) {
792 if(inkscape_mapalt()) /* returns the map of the keyboard modifier to map to Alt, zero if no mapping */
793 {
794 GdkModifierType mapping=(GdkModifierType)inkscape_mapalt();
795 switch (event->type) {
796 case GDK_MOTION_NOTIFY:
797 if(event->motion.state & mapping) {
798 event->motion.state|=GDK_MOD1_MASK;
799 }
800 break;
801 case GDK_BUTTON_PRESS:
802 if(event->button.state & mapping) {
803 event->button.state|=GDK_MOD1_MASK;
804 }
805 break;
806 case GDK_KEY_PRESS:
807 if(event->key.state & mapping) {
808 event->key.state|=GDK_MOD1_MASK;
809 }
810 break;
811 default:
812 break;
813 }
814 }
815 gtk_main_do_event (event);
816 }
818 int
819 sp_main_gui(int argc, char const **argv)
820 {
821 Gtk::Main main_instance (&argc, const_cast<char ***>(&argv));
823 GSList *fl = NULL;
824 int retVal = sp_common_main( argc, argv, &fl );
825 g_return_val_if_fail(retVal == 0, 1);
827 inkscape_gtk_stock_init();
829 gdk_event_handler_set((GdkEventFunc)snooper, NULL, NULL);
831 Inkscape::Debug::log_display_config();
833 /* Set default icon */
834 gchar *filename = (gchar *) g_build_filename (INKSCAPE_APPICONDIR, "inkscape.png", NULL);
835 if (Inkscape::IO::file_test(filename, (GFileTest)(G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_SYMLINK))) {
836 gtk_window_set_default_icon_from_file(filename, NULL);
837 }
838 g_free (filename);
839 filename = 0;
841 gboolean create_new = TRUE;
843 /// \todo FIXME BROKEN - non-UTF-8 sneaks in here.
844 inkscape_application_init(argv[0], true);
846 while (fl) {
847 if (sp_file_open((gchar *)fl->data,NULL)) {
848 create_new=FALSE;
849 }
850 fl = g_slist_remove(fl, fl->data);
851 }
852 if (create_new) {
853 sp_file_new_default();
854 }
856 Glib::signal_idle().connect(sigc::ptr_fun(&Inkscape::CmdLineAction::idle));
857 main_instance.run();
859 #ifdef WIN32
860 //We might not need anything here
861 //sp_win32_finish(); <-- this is a NOP func
862 #endif
864 return 0;
865 }
867 /**
868 * Process file list
869 */
870 void sp_process_file_list(GSList *fl)
871 {
872 while (fl) {
873 const gchar *filename = (gchar *)fl->data;
874 SPDocument *doc = Inkscape::Extension::open(NULL, filename);
875 if (doc == NULL) {
876 doc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), filename);
877 }
878 if (doc == NULL) {
879 g_warning("Specified document %s cannot be opened (is it a valid SVG file?)", filename);
880 } else {
881 if (sp_vacuum_defs) {
882 vacuum_document(doc);
883 }
884 if (sp_vacuum_defs && !sp_export_svg) {
885 // save under the name given in the command line
886 sp_repr_save_file(doc->rdoc, filename, SP_SVG_NS_URI);
887 }
888 if (sp_global_printer) {
889 sp_print_document_to_file(doc, sp_global_printer);
890 }
891 if (sp_export_png) {
892 sp_do_export_png(doc);
893 }
894 if (sp_export_svg) {
895 Inkscape::XML::Document *rdoc;
896 Inkscape::XML::Node *repr;
897 rdoc = sp_repr_document_new("svg:svg");
898 repr = rdoc->root();
899 repr = sp_document_root(doc)->updateRepr(rdoc, repr, SP_OBJECT_WRITE_BUILD);
900 sp_repr_save_file(repr->document(), sp_export_svg, SP_SVG_NS_URI);
901 }
902 if (sp_export_ps) {
903 do_export_ps_pdf(doc, sp_export_ps, "image/x-postscript");
904 }
905 if (sp_export_eps) {
906 do_export_ps_pdf(doc, sp_export_eps, "image/x-e-postscript");
907 }
908 if (sp_export_pdf) {
909 do_export_ps_pdf(doc, sp_export_pdf, "application/pdf");
910 }
911 #ifdef WIN32
912 if (sp_export_emf) {
913 do_export_emf(doc, sp_export_emf, "image/x-emf");
914 }
915 #endif //WIN32
916 if (sp_query_all) {
917 do_query_all (doc);
918 } else if (sp_query_width || sp_query_height) {
919 do_query_dimension (doc, true, sp_query_width? NR::X : NR::Y, sp_query_id);
920 } else if (sp_query_x || sp_query_y) {
921 do_query_dimension (doc, false, sp_query_x? NR::X : NR::Y, sp_query_id);
922 }
924 delete doc;
925 }
926 fl = g_slist_remove(fl, fl->data);
927 }
928 }
930 /**
931 * Run the application as an interactive shell, parsing command lines from stdin
932 * Returns -1 on error.
933 */
934 int sp_main_shell(char const* command_name)
935 {
936 int retval = 0;
938 const unsigned int buffer_size = 4096;
939 gchar *command_line = g_strnfill(buffer_size, 0);
940 g_strlcpy(command_line, command_name, buffer_size);
941 gsize offset = g_strlcat(command_line, " ", buffer_size);
942 gsize sizeLeft = buffer_size - offset;
943 gchar *useme = command_line + offset;
945 fprintf(stdout, "Inkscape %s interactive shell mode. Type 'quit' to quit.\n", INKSCAPE_VERSION);
946 fflush(stdout);
947 char* linedata = 0;
948 do {
949 fprintf(stdout, ">");
950 fflush(stdout);
951 if ((linedata = fgets(useme, sizeLeft, stdin))) {
952 size_t len = strlen(useme);
953 if ( (len >= sizeLeft - 1) || (useme[len - 1] != '\n') ) {
954 fprintf(stdout, "ERROR: Command line too long\n");
955 // Consume rest of line
956 retval = -1; // If the while loop completes, this remains -1
957 while (fgets(useme, sizeLeft, stdin) && retval) {
958 len = strlen(command_line);
959 if ( (len < buffer_size) && (command_line[len-1] == '\n') ) {
960 retval = 0;
961 }
962 }
963 } else {
964 useme[--len] = '\0'; // Strip newline
965 if (useme[len - 1] == '\r') {
966 useme[--len] = '\0';
967 }
968 if ( strcmp(useme, "quit") == 0 ) {
969 // Time to quit
970 fflush(stdout);
971 linedata = 0; // mark for exit
972 } else if ( len < 1 ) {
973 // blank string. Do nothing.
974 } else {
975 GError* parseError = 0;
976 gchar** argv = 0;
977 gint argc = 0;
978 if ( g_shell_parse_argv(command_line, &argc, &argv, &parseError) ) {
979 poptContext ctx = poptGetContext(NULL, argc, const_cast<const gchar**>(argv), options, 0);
980 poptSetOtherOptionHelp(ctx, _("[OPTIONS...] [FILE...]\n\nAvailable options:"));
981 if ( ctx ) {
982 GSList *fl = sp_process_args(ctx);
983 sp_process_file_list(fl);
984 poptFreeContext(ctx);
985 } else {
986 retval = 1; // not sure why. But this was the previous return value
987 }
988 resetCommandlineGlobals();
989 g_strfreev(argv);
990 } else {
991 g_warning("Cannot parse commandline: %s", useme);
992 }
993 }
994 }
995 } // if (linedata...
996 } while (linedata && (retval == 0));
998 g_free(command_line);
999 return retval;
1000 }
1002 int sp_main_console(int argc, char const **argv)
1003 {
1004 /* We are started in text mode */
1006 /* Do this g_type_init(), so that we can use Xft/Freetype2 (Pango)
1007 * in a non-Gtk environment. Used in libnrtype's
1008 * FontInstance.cpp and FontFactory.cpp.
1009 * http://mail.gnome.org/archives/gtk-list/2003-December/msg00063.html
1010 */
1011 g_type_init();
1012 char **argv2 = const_cast<char **>(argv);
1013 gtk_init_check( &argc, &argv2 );
1014 //setlocale(LC_ALL, "");
1016 GSList *fl = NULL;
1017 int retVal = sp_common_main( argc, argv, &fl );
1018 g_return_val_if_fail(retVal == 0, 1);
1020 if (fl == NULL && !sp_shell) {
1021 g_print("Nothing to do!\n");
1022 exit(0);
1023 }
1025 inkscape_application_init(argv[0], false);
1027 if (sp_shell) {
1028 sp_main_shell(argv[0]); // Run as interactive shell
1029 exit(0);
1030 } else {
1031 sp_process_file_list(fl); // Normal command line invokation
1032 }
1034 return 0;
1035 }
1037 static void
1038 do_query_dimension (SPDocument *doc, bool extent, NR::Dim2 const axis, const gchar *id)
1039 {
1040 SPObject *o = NULL;
1042 if (id) {
1043 o = doc->getObjectById(id);
1044 if (o) {
1045 if (!SP_IS_ITEM (o)) {
1046 g_warning("Object with id=\"%s\" is not a visible item. Cannot query dimensions.", id);
1047 return;
1048 }
1049 } else {
1050 g_warning("Object with id=\"%s\" is not found. Cannot query dimensions.", id);
1051 return;
1052 }
1053 } else {
1054 o = SP_DOCUMENT_ROOT(doc);
1055 }
1057 if (o) {
1058 sp_document_ensure_up_to_date (doc);
1059 SPItem *item = ((SPItem *) o);
1061 // "true" SVG bbox for scripting
1062 boost::optional<Geom::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
1063 if (area) {
1064 Inkscape::SVGOStringStream os;
1065 if (extent) {
1066 os << area->dimensions()[axis];
1067 } else {
1068 os << area->min()[axis];
1069 }
1070 g_print ("%s", os.str().c_str());
1071 } else {
1072 g_print("0");
1073 }
1074 }
1075 }
1077 static void
1078 do_query_all (SPDocument *doc)
1079 {
1080 SPObject *o = NULL;
1082 o = SP_DOCUMENT_ROOT(doc);
1084 if (o) {
1085 sp_document_ensure_up_to_date (doc);
1086 do_query_all_recurse(o);
1087 }
1088 }
1090 static void
1091 do_query_all_recurse (SPObject *o)
1092 {
1093 SPItem *item = ((SPItem *) o);
1094 if (o->id && SP_IS_ITEM(item)) {
1095 boost::optional<Geom::Rect> area = item->getBounds(sp_item_i2doc_affine(item));
1096 if (area) {
1097 Inkscape::SVGOStringStream os;
1098 os << o->id;
1099 os << "," << area->min()[Geom::X];
1100 os << "," << area->min()[Geom::Y];
1101 os << "," << area->dimensions()[Geom::X];
1102 os << "," << area->dimensions()[Geom::Y];
1103 g_print ("%s\n", os.str().c_str());
1104 }
1105 }
1107 SPObject *child = o->children;
1108 while (child) {
1109 do_query_all_recurse (child);
1110 child = child->next;
1111 }
1112 }
1115 static void
1116 sp_do_export_png(SPDocument *doc)
1117 {
1118 const gchar *filename = NULL;
1119 gdouble dpi = 0.0;
1121 if (sp_export_use_hints && (!sp_export_id && !sp_export_area_drawing)) {
1122 g_warning ("--export-use-hints can only be used with --export-id or --export-area-drawing; ignored.");
1123 }
1125 GSList *items = NULL;
1127 NRRect area;
1128 if (sp_export_id || sp_export_area_drawing) {
1130 SPObject *o = NULL;
1131 SPObject *o_area = NULL;
1132 if (sp_export_id && sp_export_area_drawing) {
1133 o = doc->getObjectById(sp_export_id);
1134 o_area = SP_DOCUMENT_ROOT (doc);
1135 } else if (sp_export_id) {
1136 o = doc->getObjectById(sp_export_id);
1137 o_area = o;
1138 } else if (sp_export_area_drawing) {
1139 o = SP_DOCUMENT_ROOT (doc);
1140 o_area = o;
1141 }
1143 if (o) {
1144 if (!SP_IS_ITEM (o)) {
1145 g_warning("Object with id=\"%s\" is not a visible item. Nothing exported.", sp_export_id);
1146 return;
1147 }
1149 items = g_slist_prepend (items, SP_ITEM(o));
1151 if (sp_export_id_only) {
1152 g_print("Exporting only object with id=\"%s\"; all other objects hidden\n", sp_export_id);
1153 }
1155 if (sp_export_use_hints) {
1157 // retrieve export filename hint
1158 const gchar *fn_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-filename");
1159 if (fn_hint) {
1160 if (sp_export_png) {
1161 g_warning ("Using export filename from the command line (--export-png). Filename hint %s is ignored.", fn_hint);
1162 filename = sp_export_png;
1163 } else {
1164 filename = fn_hint;
1165 }
1166 } else {
1167 g_warning ("Export filename hint not found for the object.");
1168 filename = sp_export_png;
1169 }
1171 // retrieve export dpi hints
1172 const gchar *dpi_hint = SP_OBJECT_REPR(o)->attribute("inkscape:export-xdpi"); // only xdpi, ydpi is always the same now
1173 if (dpi_hint) {
1174 if (sp_export_dpi || sp_export_width || sp_export_height) {
1175 g_warning ("Using bitmap dimensions from the command line (--export-dpi, --export-width, or --export-height). DPI hint %s is ignored.", dpi_hint);
1176 } else {
1177 dpi = atof(dpi_hint);
1178 }
1179 } else {
1180 g_warning ("Export DPI hint not found for the object.");
1181 }
1183 }
1185 // write object bbox to area
1186 sp_document_ensure_up_to_date (doc);
1187 boost::optional<Geom::Rect> areaMaybe;
1188 sp_item_invoke_bbox((SPItem *) o_area, areaMaybe, sp_item_i2r_affine((SPItem *) o_area), TRUE);
1189 if (areaMaybe) {
1190 area = NRRect(areaMaybe);
1191 } else {
1192 g_warning("Unable to determine a valid bounding box. Nothing exported.");
1193 return;
1194 }
1195 } else {
1196 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1197 return;
1198 }
1199 }
1201 if (sp_export_area) {
1202 /* Try to parse area (given in SVG pixels) */
1203 if (!sscanf(sp_export_area, "%lg:%lg:%lg:%lg", &area.x0, &area.y0, &area.x1, &area.y1) == 4) {
1204 g_warning("Cannot parse export area '%s'; use 'x0:y0:x1:y1'. Nothing exported.", sp_export_area);
1205 return;
1206 }
1207 if ((area.x0 >= area.x1) || (area.y0 >= area.y1)) {
1208 g_warning("Export area '%s' has negative width or height. Nothing exported.", sp_export_area);
1209 return;
1210 }
1211 } else if (sp_export_area_canvas || !(sp_export_id || sp_export_area_drawing)) {
1212 /* Export the whole canvas */
1213 sp_document_ensure_up_to_date (doc);
1214 area.x0 = SP_ROOT(doc->root)->x.computed;
1215 area.y0 = SP_ROOT(doc->root)->y.computed;
1216 area.x1 = area.x0 + sp_document_width (doc);
1217 area.y1 = area.y0 + sp_document_height (doc);
1218 }
1220 // set filename and dpi from options, if not yet set from the hints
1221 if (!filename) {
1222 if (!sp_export_png) {
1223 g_warning ("No export filename given and no filename hint. Nothing exported.");
1224 return;
1225 }
1226 filename = sp_export_png;
1227 }
1229 if (sp_export_dpi && dpi == 0.0) {
1230 dpi = atof(sp_export_dpi);
1231 if ((dpi < 0.1) || (dpi > 10000.0)) {
1232 g_warning("DPI value %s out of range [0.1 - 10000.0]. Nothing exported.", sp_export_dpi);
1233 return;
1234 }
1235 g_print("DPI: %g\n", dpi);
1236 }
1238 if (sp_export_area_snap) {
1239 area.x0 = std::floor (area.x0);
1240 area.y0 = std::floor (area.y0);
1241 area.x1 = std::ceil (area.x1);
1242 area.y1 = std::ceil (area.y1);
1243 }
1245 // default dpi
1246 if (dpi == 0.0) {
1247 dpi = PX_PER_IN;
1248 }
1250 unsigned long int width = 0;
1251 unsigned long int height = 0;
1253 if (sp_export_width) {
1254 errno=0;
1255 width = strtoul(sp_export_width, NULL, 0);
1256 if ((width < 1) || (width > PNG_UINT_31_MAX) || (errno == ERANGE) ) {
1257 g_warning("Export width %lu out of range (1 - %lu). Nothing exported.", width, (unsigned long int)PNG_UINT_31_MAX);
1258 return;
1259 }
1260 dpi = (gdouble) width * PX_PER_IN / (area.x1 - area.x0);
1261 }
1263 if (sp_export_height) {
1264 errno=0;
1265 height = strtoul(sp_export_height, NULL, 0);
1266 if ((height < 1) || (height > PNG_UINT_31_MAX)) {
1267 g_warning("Export height %lu out of range (1 - %lu). Nothing exported.", height, (unsigned long int)PNG_UINT_31_MAX);
1268 return;
1269 }
1270 dpi = (gdouble) height * PX_PER_IN / (area.y1 - area.y0);
1271 }
1273 if (!sp_export_width) {
1274 width = (unsigned long int) ((area.x1 - area.x0) * dpi / PX_PER_IN + 0.5);
1275 }
1277 if (!sp_export_height) {
1278 height = (unsigned long int) ((area.y1 - area.y0) * dpi / PX_PER_IN + 0.5);
1279 }
1281 guint32 bgcolor = 0x00000000;
1282 if (sp_export_background) {
1283 // override the page color
1284 bgcolor = sp_svg_read_color(sp_export_background, 0xffffff00);
1285 bgcolor |= 0xff; // default is no opacity
1286 } else {
1287 // read from namedview
1288 Inkscape::XML::Node *nv = sp_repr_lookup_name (doc->rroot, "sodipodi:namedview");
1289 if (nv && nv->attribute("pagecolor"))
1290 bgcolor = sp_svg_read_color(nv->attribute("pagecolor"), 0xffffff00);
1291 if (nv && nv->attribute("inkscape:pageopacity"))
1292 bgcolor |= SP_COLOR_F_TO_U(sp_repr_get_double_attribute (nv, "inkscape:pageopacity", 1.0));
1293 }
1295 if (sp_export_background_opacity) {
1296 // override opacity
1297 gfloat value;
1298 if (sp_svg_number_read_f (sp_export_background_opacity, &value)) {
1299 if (value > 1.0) {
1300 value = CLAMP (value, 1.0f, 255.0f);
1301 bgcolor &= (guint32) 0xffffff00;
1302 bgcolor |= (guint32) floor(value);
1303 } else {
1304 value = CLAMP (value, 0.0f, 1.0f);
1305 bgcolor &= (guint32) 0xffffff00;
1306 bgcolor |= SP_COLOR_F_TO_U(value);
1307 }
1308 }
1309 }
1311 g_print("Background RRGGBBAA: %08x\n", bgcolor);
1313 g_print("Area %g:%g:%g:%g exported to %lu x %lu pixels (%g dpi)\n", area.x0, area.y0, area.x1, area.y1, width, height, dpi);
1315 g_print("Bitmap saved as: %s\n", filename);
1317 if ((width >= 1) && (height >= 1) && (width <= PNG_UINT_31_MAX) && (height <= PNG_UINT_31_MAX)) {
1318 sp_export_png_file(doc, filename, area.x0, area.y0, area.x1, area.y1, width, height, dpi, dpi, bgcolor, NULL, NULL, true, sp_export_id_only ? items : NULL);
1319 } else {
1320 g_warning("Calculated bitmap dimensions %lu %lu are out of range (1 - %lu). Nothing exported.", width, height, (unsigned long int)PNG_UINT_31_MAX);
1321 }
1323 g_slist_free (items);
1324 }
1327 /**
1328 * Perform a PDF/PS/EPS export
1329 *
1330 * \param doc Document to export.
1331 * \param uri URI to export to.
1332 * \param mime MIME type to export as.
1333 */
1335 static void do_export_ps_pdf(SPDocument* doc, gchar const* uri, char const* mime)
1336 {
1337 Inkscape::Extension::DB::OutputList o;
1338 Inkscape::Extension::db.get_output_list(o);
1339 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1340 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1341 i++;
1342 }
1344 if (i == o.end())
1345 {
1346 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1347 return;
1348 }
1350 if (sp_export_id) {
1351 SPObject *o = doc->getObjectById(sp_export_id);
1352 if (o == NULL) {
1353 g_warning("Object with id=\"%s\" was not found in the document. Nothing exported.", sp_export_id);
1354 return;
1355 }
1356 (*i)->set_param_string ("exportId", sp_export_id);
1357 } else {
1358 (*i)->set_param_string ("exportId", "");
1359 }
1361 if (sp_export_area_canvas && sp_export_area_drawing) {
1362 g_warning ("You cannot use --export-area-canvas and --export-area-drawing at the same time; only the former will take effect.");
1363 sp_export_area_drawing = false;
1364 }
1366 if (sp_export_area_drawing) {
1367 (*i)->set_param_bool ("areaDrawing", TRUE);
1368 } else {
1369 (*i)->set_param_bool ("areaDrawing", FALSE);
1370 }
1372 if (sp_export_area_canvas) {
1373 if (sp_export_eps) {
1374 g_warning ("EPS cannot have its bounding box extend beyond its content, so if your drawing is smaller than the canvas, --export-area-canvas will clip it to drawing.");
1375 }
1376 (*i)->set_param_bool ("areaCanvas", TRUE);
1377 } else {
1378 (*i)->set_param_bool ("areaCanvas", FALSE);
1379 }
1381 if (!sp_export_area_drawing && !sp_export_area_canvas && !sp_export_id) {
1382 // neither is set, set canvas as default for ps/pdf and drawing for eps
1383 if (sp_export_eps) {
1384 try {
1385 (*i)->set_param_bool("areaDrawing", TRUE);
1386 } catch (...) {}
1387 }
1388 }
1390 if (sp_export_text_to_path) {
1391 (*i)->set_param_bool("textToPath", TRUE);
1392 } else {
1393 (*i)->set_param_bool("textToPath", FALSE);
1394 }
1396 (*i)->save(doc, uri);
1397 }
1399 #ifdef WIN32
1400 /**
1401 * Export a document to EMF
1402 *
1403 * \param doc Document to export.
1404 * \param uri URI to export to.
1405 * \param mime MIME type to export as (should be "image/x-emf")
1406 */
1408 static void do_export_emf(SPDocument* doc, gchar const* uri, char const* mime)
1409 {
1410 Inkscape::Extension::DB::OutputList o;
1411 Inkscape::Extension::db.get_output_list(o);
1412 Inkscape::Extension::DB::OutputList::const_iterator i = o.begin();
1413 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1414 i++;
1415 }
1417 if (i == o.end())
1418 {
1419 g_warning ("Could not find an extension to export to MIME type %s.", mime);
1420 return;
1421 }
1423 (*i)->save(doc, uri);
1424 }
1425 #endif //WIN32
1427 #ifdef WIN32
1428 bool replaceArgs( int& argc, char**& argv )
1429 {
1430 bool worked = false;
1432 #ifdef REPLACEARGS_DEBUG
1433 MessageBoxA( NULL, "GetCommandLineW() getting called", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1434 #endif // REPLACEARGS_DEBUG
1436 wchar_t* line = GetCommandLineW();
1437 if ( line )
1438 {
1439 #ifdef REPLACEARGS_DEBUG
1440 {
1441 gchar* utf8Line = g_utf16_to_utf8( (gunichar2*)line, -1, NULL, NULL, NULL );
1442 if ( utf8Line )
1443 {
1444 gchar *safe = Inkscape::IO::sanitizeString(utf8Line);
1445 {
1446 char tmp[strlen(safe) + 32];
1447 snprintf( tmp, sizeof(tmp), "GetCommandLineW() = '%s'", safe );
1448 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1449 }
1450 }
1451 }
1452 #endif // REPLACEARGS_DEBUG
1454 int numArgs = 0;
1455 wchar_t** parsed = CommandLineToArgvW( line, &numArgs );
1457 #ifdef REPLACEARGS_ANSI
1458 // test code for trying things on Win95/98/ME
1459 if ( !parsed )
1460 {
1461 #ifdef REPLACEARGS_DEBUG
1462 MessageBoxA( NULL, "Unable to process command-line. Faking it", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1463 #endif // REPLACEARGS_DEBUG
1464 int lineLen = wcslen(line) + 1;
1465 wchar_t* lineDup = new wchar_t[lineLen];
1466 wcsncpy( lineDup, line, lineLen );
1468 int pos = 0;
1469 bool inQuotes = false;
1470 bool inWhitespace = true;
1471 std::vector<int> places;
1472 while ( lineDup[pos] )
1473 {
1474 if ( inQuotes )
1475 {
1476 if ( lineDup[pos] == L'"' )
1477 {
1478 inQuotes = false;
1479 }
1480 }
1481 else if ( lineDup[pos] == L'"' )
1482 {
1483 inQuotes = true;
1484 inWhitespace = false;
1485 places.push_back(pos);
1486 }
1487 else if ( lineDup[pos] == L' ' || lineDup[pos] == L'\t' )
1488 {
1489 if ( !inWhitespace )
1490 {
1491 inWhitespace = true;
1492 lineDup[pos] = 0;
1493 }
1494 }
1495 else if ( inWhitespace && (lineDup[pos] != L' ' && lineDup[pos] != L'\t') )
1496 {
1497 inWhitespace = false;
1498 places.push_back(pos);
1499 }
1500 else
1501 {
1502 // consume
1503 }
1504 pos++;
1505 }
1506 #ifdef REPLACEARGS_DEBUG
1507 {
1508 char tmp[256];
1509 snprintf( tmp, sizeof(tmp), "Counted %d args", places.size() );
1510 MessageBoxA( NULL, tmp, "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1511 }
1512 #endif // REPLACEARGS_DEBUG
1514 wchar_t** block = new wchar_t*[places.size()];
1515 int i = 0;
1516 for ( std::vector<int>::iterator it = places.begin(); it != places.end(); it++ )
1517 {
1518 block[i++] = &lineDup[*it];
1519 }
1520 parsed = block;
1521 numArgs = places.size();
1522 }
1523 #endif // REPLACEARGS_ANSI
1525 if ( parsed )
1526 {
1527 std::vector<wchar_t*>expandedArgs;
1528 if ( numArgs > 0 )
1529 {
1530 expandedArgs.push_back( parsed[0] );
1531 }
1533 for ( int i1 = 1; i1 < numArgs; i1++ )
1534 {
1535 bool wildcarded = (wcschr(parsed[i1], L'?') != NULL) || (wcschr(parsed[i1], L'*') != NULL);
1536 wildcarded &= parsed[i1][0] != L'"';
1537 wildcarded &= parsed[i1][0] != L'-';
1538 if ( wildcarded )
1539 {
1540 #ifdef REPLACEARGS_ANSI
1541 WIN32_FIND_DATAA data;
1542 #else
1543 WIN32_FIND_DATAW data;
1544 #endif // REPLACEARGS_ANSI
1546 memset((void *)&data, 0, sizeof(data));
1548 int baseLen = wcslen(parsed[i1]) + 2;
1549 wchar_t* base = new wchar_t[baseLen];
1550 wcsncpy( base, parsed[i1], baseLen );
1551 wchar_t* last = wcsrchr( base, L'\\' );
1552 if ( last )
1553 {
1554 last[1] = 0;
1555 }
1556 else
1557 {
1558 base[0] = 0;
1559 }
1560 baseLen = wcslen( base );
1562 #ifdef REPLACEARGS_ANSI
1563 char target[MAX_PATH];
1564 if ( WideCharToMultiByte( CP_ACP, 0, parsed[i1], -1, target, sizeof(target), NULL, NULL) )
1565 {
1566 HANDLE hf = FindFirstFileA( target, &data );
1567 #else
1568 HANDLE hf = FindFirstFileW( parsed[i1], &data );
1569 #endif // REPLACEARGS_ANSI
1570 if ( hf != INVALID_HANDLE_VALUE )
1571 {
1572 BOOL found = TRUE;
1573 do
1574 {
1575 #ifdef REPLACEARGS_ANSI
1576 int howMany = MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, NULL, 0 );
1577 if ( howMany > 0 )
1578 {
1579 howMany += baseLen;
1580 wchar_t* tmp = new wchar_t[howMany + 1];
1581 wcsncpy( tmp, base, howMany + 1 );
1582 MultiByteToWideChar( CP_ACP, 0, data.cFileName, -1, tmp + baseLen, howMany + 1 - baseLen );
1583 expandedArgs.push_back( tmp );
1584 found = FindNextFileA( hf, &data );
1585 }
1586 #else
1587 int howMany = wcslen(data.cFileName) + baseLen;
1588 wchar_t* tmp = new wchar_t[howMany + 1];
1589 wcsncpy( tmp, base, howMany + 1 );
1590 wcsncat( tmp, data.cFileName, howMany + 1 );
1591 expandedArgs.push_back( tmp );
1592 found = FindNextFileW( hf, &data );
1593 #endif // REPLACEARGS_ANSI
1594 } while ( found );
1596 FindClose( hf );
1597 }
1598 else
1599 {
1600 expandedArgs.push_back( parsed[i1] );
1601 }
1602 #ifdef REPLACEARGS_ANSI
1603 }
1604 #endif // REPLACEARGS_ANSI
1606 delete[] base;
1607 }
1608 else
1609 {
1610 expandedArgs.push_back( parsed[i1] );
1611 }
1612 }
1614 {
1615 wchar_t** block = new wchar_t*[expandedArgs.size()];
1616 int iz = 0;
1617 for ( std::vector<wchar_t*>::iterator it = expandedArgs.begin(); it != expandedArgs.end(); it++ )
1618 {
1619 block[iz++] = *it;
1620 }
1621 parsed = block;
1622 numArgs = expandedArgs.size();
1623 }
1625 std::vector<gchar*> newArgs;
1626 for ( int i = 0; i < numArgs; i++ )
1627 {
1628 gchar* replacement = g_utf16_to_utf8( (gunichar2*)parsed[i], -1, NULL, NULL, NULL );
1629 if ( replacement )
1630 {
1631 #ifdef REPLACEARGS_DEBUG
1632 gchar *safe2 = Inkscape::IO::sanitizeString(replacement);
1634 if ( safe2 )
1635 {
1636 {
1637 char tmp[1024];
1638 snprintf( tmp, sizeof(tmp), " [%2d] = '%s'", i, safe2 );
1639 MessageBoxA( NULL, tmp, "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1640 }
1641 g_free( safe2 );
1642 }
1643 #endif // REPLACEARGS_DEBUG
1645 newArgs.push_back( replacement );
1646 }
1647 else
1648 {
1649 newArgs.push_back( blankParam );
1650 }
1651 }
1653 // Now push our munged params to be the new argv and argc
1654 {
1655 char** block = new char*[newArgs.size()];
1656 int iz = 0;
1657 for ( std::vector<char*>::iterator it = newArgs.begin(); it != newArgs.end(); it++ )
1658 {
1659 block[iz++] = *it;
1660 }
1661 argv = block;
1662 argc = newArgs.size();
1663 worked = true;
1664 }
1665 }
1666 #ifdef REPLACEARGS_DEBUG
1667 else
1668 {
1669 MessageBoxA( NULL, "Unable to process command-line", "CommandLineToArgvW", MB_OK | MB_ICONINFORMATION );
1670 }
1671 #endif // REPLACEARGS_DEBUG
1672 }
1673 #ifdef REPLACEARGS_DEBUG
1674 else
1675 {
1676 {
1677 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineW()", "GetCommandLineW", MB_OK | MB_ICONINFORMATION );
1678 }
1680 char* line2 = GetCommandLineA();
1681 if ( line2 )
1682 {
1683 gchar *safe = Inkscape::IO::sanitizeString(line2);
1684 {
1685 {
1686 char tmp[strlen(safe) + 32];
1687 snprintf( tmp, sizeof(tmp), "GetCommandLineA() = '%s'", safe );
1688 MessageBoxA( NULL, tmp, "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1689 }
1690 }
1691 }
1692 else
1693 {
1694 MessageBoxA( NULL, "Unable to fetch result from GetCommandLineA()", "GetCommandLineA", MB_OK | MB_ICONINFORMATION );
1695 }
1696 }
1697 #endif // REPLACEARGS_DEBUG
1699 return worked;
1700 }
1701 #endif // WIN32
1703 static GSList *
1704 sp_process_args(poptContext ctx)
1705 {
1706 GSList *fl = NULL;
1708 gint a;
1709 while ((a = poptGetNextOpt(ctx)) != -1) {
1710 switch (a) {
1711 case SP_ARG_FILE: {
1712 gchar const *fn = poptGetOptArg(ctx);
1713 if (fn != NULL) {
1714 fl = g_slist_append(fl, g_strdup(fn));
1715 }
1716 break;
1717 }
1718 case SP_ARG_VERSION: {
1719 printf("Inkscape %s (%s)\n", INKSCAPE_VERSION, __DATE__);
1720 exit(0);
1721 break;
1722 }
1723 case SP_ARG_EXTENSIONDIR: {
1724 printf("%s\n", INKSCAPE_EXTENSIONDIR);
1725 exit(0);
1726 break;
1727 }
1728 case SP_ARG_VERB_LIST: {
1729 // This really shouldn't go here, we should init the app.
1730 // But, since we're just exiting in this path, there is
1731 // no harm, and this is really a better place to put
1732 // everything else.
1733 Inkscape::Extension::init();
1734 Inkscape::Verb::list();
1735 exit(0);
1736 break;
1737 }
1738 case SP_ARG_VERB:
1739 case SP_ARG_SELECT: {
1740 gchar const *arg = poptGetOptArg(ctx);
1741 if (arg != NULL) {
1742 // printf("Adding in: %s\n", arg);
1743 new Inkscape::CmdLineAction((a == SP_ARG_VERB), arg);
1744 }
1745 break;
1746 }
1747 case POPT_ERROR_BADOPT: {
1748 g_warning ("Invalid option %s", poptBadOption(ctx, 0));
1749 exit(1);
1750 break;
1751 }
1752 default: {
1753 break;
1754 }
1755 }
1756 }
1758 gchar const ** const args = poptGetArgs(ctx);
1759 if (args != NULL) {
1760 for (unsigned i = 0; args[i] != NULL; i++) {
1761 fl = g_slist_append(fl, g_strdup(args[i]));
1762 }
1763 }
1765 return fl;
1766 }
1769 /*
1770 Local Variables:
1771 mode:c++
1772 c-file-style:"stroustrup"
1773 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1774 indent-tabs-mode:nil
1775 fill-column:99
1776 End:
1777 */
1778 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :