Code

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