summaryrefslogtreecommitdiffstats
path: root/src/extension/implementation
diff options
context:
space:
mode:
Diffstat (limited to 'src/extension/implementation')
-rw-r--r--src/extension/implementation/.cvsignore5
-rw-r--r--src/extension/implementation/Makefile_insert13
-rw-r--r--src/extension/implementation/implementation.cpp181
-rw-r--r--src/extension/implementation/implementation.h127
-rw-r--r--src/extension/implementation/makefile.in17
-rw-r--r--src/extension/implementation/plugin-link.h68
-rw-r--r--src/extension/implementation/plugin.cpp328
-rw-r--r--src/extension/implementation/plugin.h121
-rw-r--r--src/extension/implementation/script.cpp1053
-rw-r--r--src/extension/implementation/script.h86
10 files changed, 1999 insertions, 0 deletions
diff --git a/src/extension/implementation/.cvsignore b/src/extension/implementation/.cvsignore
new file mode 100644
index 000000000..e8014d011
--- /dev/null
+++ b/src/extension/implementation/.cvsignore
@@ -0,0 +1,5 @@
+Makefile
+Makefile.in
+.deps
+makefile
+.dirstamp
diff --git a/src/extension/implementation/Makefile_insert b/src/extension/implementation/Makefile_insert
new file mode 100644
index 000000000..5d70216d2
--- /dev/null
+++ b/src/extension/implementation/Makefile_insert
@@ -0,0 +1,13 @@
+## Makefile.am fragment sourced by src/Makefile.am.
+
+extension/implementation/all: extension/implementation/libimplementation.a
+
+extension/implementation/clean:
+ rm -f extension/implementation/libimplementation.a \
+ $(extension_implementation_libimplementation_a_OBJECTS)
+
+extension_implementation_libimplementation_a_SOURCES = \
+ extension/implementation/implementation.cpp \
+ extension/implementation/implementation.h \
+ extension/implementation/script.cpp \
+ extension/implementation/script.h
diff --git a/src/extension/implementation/implementation.cpp b/src/extension/implementation/implementation.cpp
new file mode 100644
index 000000000..c959d50b3
--- /dev/null
+++ b/src/extension/implementation/implementation.cpp
@@ -0,0 +1,181 @@
+/*
+ Author: Ted Gould <ted@gould.cx>
+ Copyright (c) 2003-2005
+
+ This code is licensed under the GNU GPL. See COPYING for details.
+
+ This file is the backend to the extensions system. These are
+ the parts of the system that most users will never see, but are
+ important for implementing the extensions themselves. This file
+ contains the base class for all of that.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+#include "implementation.h"
+
+#include <extension/output.h>
+#include <extension/input.h>
+#include <extension/effect.h>
+
+namespace Inkscape {
+namespace Extension {
+namespace Implementation {
+
+/**
+ * \return Was the load sucessful?
+ * \brief This function is the stub load. It just returns success.
+ * \param module The Extension that should be loaded.
+ */
+bool
+Implementation::load(Inkscape::Extension::Extension *module) {
+ return TRUE;
+} /* Implementation::load */
+
+void
+Implementation::unload(Inkscape::Extension::Extension *module) {
+ return;
+} /* Implementation::unload */
+
+bool
+Implementation::check(Inkscape::Extension::Extension *module) {
+ /* If there are no checks, they all pass */
+ return TRUE;
+} /* Implemenation::check */
+
+Gtk::Widget *
+Implementation::prefs_input(Inkscape::Extension::Input *module, gchar const *filename) {
+ return module->autogui();
+} /* Implementation::prefs_input */
+
+SPDocument *
+Implementation::open(Inkscape::Extension::Input *module, gchar const *filename) {
+ /* throw open_failed(); */
+ return NULL;
+} /* Implementation::open */
+
+Gtk::Widget *
+Implementation::prefs_output(Inkscape::Extension::Output *module) {
+ return module->autogui();
+} /* Implementation::prefs_output */
+
+void
+Implementation::save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *filename) {
+ /* throw save_fail */
+ return;
+} /* Implementation::save */
+
+Gtk::Widget *
+Implementation::prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *view) {
+ return module->autogui();
+} /* Implementation::prefs_effect */
+
+void
+Implementation::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *document) {
+ /* throw filter_fail */
+ return;
+} /* Implementation::filter */
+
+unsigned int
+Implementation::setup(Inkscape::Extension::Print *module)
+{
+ return 0;
+}
+
+unsigned int
+Implementation::set_preview(Inkscape::Extension::Print *module)
+{
+ return 0;
+}
+
+
+unsigned int
+Implementation::begin(Inkscape::Extension::Print *module, SPDocument *doc)
+{
+ return 0;
+}
+
+unsigned int
+Implementation::finish(Inkscape::Extension::Print *module)
+{
+ return 0;
+}
+
+
+/* Rendering methods */
+unsigned int
+Implementation::bind(Inkscape::Extension::Print *module, NRMatrix const *transform, float opacity)
+{
+ return 0;
+}
+
+unsigned int
+Implementation::release(Inkscape::Extension::Print *module)
+{
+ return 0;
+}
+
+unsigned int
+Implementation::comment(Inkscape::Extension::Print *module, char const *comment)
+{
+ return 0;
+}
+
+unsigned int
+Implementation::fill(Inkscape::Extension::Print *module, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *style,
+ NRRect const *pbox, NRRect const *dbox, NRRect const *bbox)
+{
+ return 0;
+}
+
+unsigned int
+Implementation::stroke(Inkscape::Extension::Print *module, NRBPath const *bpath, NRMatrix const *transform, SPStyle const *style,
+ NRRect const *pbox, NRRect const *dbox, NRRect const *bbox)
+{
+ return 0;
+}
+
+unsigned int
+Implementation::image(Inkscape::Extension::Print *module, unsigned char *px, unsigned int w, unsigned int h, unsigned int rs,
+ NRMatrix const *transform, SPStyle const *style)
+{
+ return 0;
+}
+
+unsigned int
+Implementation::text(Inkscape::Extension::Print *module, char const *text,
+ NR::Point p, SPStyle const *style)
+{
+ return 0;
+}
+
+/**
+ \brief Tell the printing engine whether text should be text or path
+ \retval true Render the text as a path
+ \retval false Render text using the text function (above)
+
+ Default value is false because most printing engines will support
+ paths more than they'll support text. (at least they do today)
+*/
+bool
+Implementation::textToPath(Inkscape::Extension::Print *ext)
+{
+ return false;
+}
+
+
+} /* namespace Implementation */
+} /* namespace Extension */
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/extension/implementation/implementation.h b/src/extension/implementation/implementation.h
new file mode 100644
index 000000000..68c2eb04c
--- /dev/null
+++ b/src/extension/implementation/implementation.h
@@ -0,0 +1,127 @@
+/*
+ Author: Ted Gould <ted@gould.cx>
+ Copyright (c) 2003-2005
+
+ This code is licensed under the GNU GPL. See COPYING for details.
+
+ This file is the backend to the extensions system. These are
+ the parts of the system that most users will never see, but are
+ important for implementing the extensions themselves. This file
+ contains the base class for all of that.
+*/
+#ifndef __INKSCAPE_EXTENSION_IMPLEMENTATION_H__
+#define __INKSCAPE_EXTENSION_IMPLEMENTATION_H__
+
+#include <gtk/gtkdialog.h>
+#include <gdkmm/types.h>
+#include <gtkmm/widget.h>
+
+#include "forward.h"
+#include "extension/extension-forward.h"
+#include "libnr/nr-forward.h"
+#include "libnr/nr-point.h"
+
+namespace Inkscape {
+namespace Extension {
+namespace Implementation {
+
+/**
+ * Base class for all implementations of modules. This is whether they are done systematically by
+ * having something like the scripting system, or they are implemented internally they all derive
+ * from this class.
+ */
+class Implementation {
+public:
+ /* ----- Constructor / destructor ----- */
+ Implementation() {}
+
+ virtual ~Implementation() {}
+
+ /* ----- Basic functions for all Extension ----- */
+ virtual bool load(Inkscape::Extension::Extension *module);
+
+ virtual void unload(Inkscape::Extension::Extension *module);
+
+ /** Verify any dependencies. */
+ virtual bool check(Inkscape::Extension::Extension *module);
+
+
+ /* ----- Input functions ----- */
+ /** Find out information about the file. */
+ virtual Gtk::Widget *prefs_input(Inkscape::Extension::Input *module,
+ gchar const *filename);
+
+ virtual SPDocument *open(Inkscape::Extension::Input *module,
+ gchar const *filename);
+
+ /* ----- Output functions ----- */
+ /** Find out information about the file. */
+ virtual Gtk::Widget *prefs_output(Inkscape::Extension::Output *module);
+ virtual void save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *filename);
+
+ /* ----- Effect functions ----- */
+ /** Find out information about the file. */
+ virtual Gtk::Widget * prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View * view);
+ /* TODO: need to figure out what we need here */
+
+ virtual void effect(Inkscape::Extension::Effect *module,
+ Inkscape::UI::View::View *document);
+
+ /* ----- Print functions ----- */
+ virtual unsigned setup(Inkscape::Extension::Print *module);
+ virtual unsigned set_preview(Inkscape::Extension::Print *module);
+
+ virtual unsigned begin(Inkscape::Extension::Print *module,
+ SPDocument *doc);
+ virtual unsigned finish(Inkscape::Extension::Print *module);
+ virtual bool textToPath(Inkscape::Extension::Print *ext);
+
+ /* ----- Rendering methods ----- */
+ virtual unsigned bind(Inkscape::Extension::Print *module,
+ NRMatrix const *transform,
+ float opacity);
+ virtual unsigned release(Inkscape::Extension::Print *module);
+ virtual unsigned comment(Inkscape::Extension::Print *module, const char * comment);
+ virtual unsigned fill(Inkscape::Extension::Print *module,
+ NRBPath const *bpath,
+ NRMatrix const *ctm,
+ SPStyle const *style,
+ NRRect const *pbox,
+ NRRect const *dbox,
+ NRRect const *bbox);
+ virtual unsigned stroke(Inkscape::Extension::Print *module,
+ NRBPath const *bpath,
+ NRMatrix const *transform,
+ SPStyle const *style,
+ NRRect const *pbox,
+ NRRect const *dbox,
+ NRRect const *bbox);
+ virtual unsigned image(Inkscape::Extension::Print *module,
+ unsigned char *px,
+ unsigned int w,
+ unsigned int h,
+ unsigned int rs,
+ NRMatrix const *transform,
+ SPStyle const *style);
+ virtual unsigned text(Inkscape::Extension::Print *module,
+ char const *text,
+ NR::Point p,
+ SPStyle const *style);
+};
+
+} /* namespace Implementation */
+} /* namespace Extension */
+} /* namespace Inkscape */
+
+#endif /* __INKSCAPE_EXTENSION_IMPLEMENTATION_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/extension/implementation/makefile.in b/src/extension/implementation/makefile.in
new file mode 100644
index 000000000..c0bf21190
--- /dev/null
+++ b/src/extension/implementation/makefile.in
@@ -0,0 +1,17 @@
+# Convenience stub makefile to call the real Makefile.
+
+@SET_MAKE@
+
+# Explicit so that it's the default rule.
+all:
+ cd ../.. && $(MAKE) extension/implementation/all
+
+clean %.a %.o:
+ cd ../.. && $(MAKE) extension/implementation/$@
+
+.PHONY: all clean
+
+OBJEXT = @OBJEXT@
+
+.SUFFIXES:
+.SUFFIXES: .a .$(OBJEXT)
diff --git a/src/extension/implementation/plugin-link.h b/src/extension/implementation/plugin-link.h
new file mode 100644
index 000000000..740b8952a
--- /dev/null
+++ b/src/extension/implementation/plugin-link.h
@@ -0,0 +1,68 @@
+/** \file
+ * Plugin prototypes.
+ *
+ * This header describes which prototypes plugins should use when
+ * creating their functions. This header is also used by the internal
+ * plugins code to guarantee consistency.
+ *
+ * Author: Ted Gould <ted@gould.cx>
+ * Copyright (c) 2004-2005
+ *
+ * This code is licensed under the GNU GPL. See COPYING for details.
+ */
+
+#ifndef __INKSCAPE_EXTENSION_IMPLEMENTATION_PLUGIN_LINK_H__
+#define __INKSCAPE_EXTENSION_IMPLEMENTATION_PLUGIN_LINK_H__
+
+#include <gtk/gtkdialog.h>
+#include <gtkmm/widget.h>
+
+/** \todo This needs to go away eventually. */
+#include "document.h"
+
+/** \brief A simple typedef to make it so that inkscape_extension can
+ be used before I figure out what makes sense here */
+typedef void inkscape_extension;
+/** \brief The C prototype of a load function. */
+typedef int (*inkscape_plugin_load)(inkscape_extension * in_ext);
+/** \brief The C prototype of an unload function. */
+typedef void (*inkscape_plugin_unload)(inkscape_extension * in_ext);
+/** \brief The C prototype of an open function. */
+typedef SPDocument *(*inkscape_plugin_open)(inkscape_extension * in_ext, const gchar * filename);
+/** \brief The C prototype of an input prefs function. */
+typedef Gtk::Widget * (*inkscape_plugin_prefs_input)(inkscape_extension * in_ext, gchar const * filename);
+/** \brief The C prototype of an effect function. */
+typedef void (*inkscape_plugin_effect)(inkscape_extension * in_ext, Inkscape::UI::View::View * view);
+/** \brief The C prototype of an effect prefs function. */
+typedef Gtk::Widget * (*inkscape_plugin_prefs_effect)(inkscape_extension * in_ext, Inkscape::UI::View::View * view);
+
+/** \brief The name of the symbol for the plugin. Should match
+ \c INKSCAPE_PLUGIN_NAME_STR (minus the quotes). */
+#define INKSCAPE_PLUGIN_NAME inkscape_plugin_table
+/** \brief The name of the table to define the plugin as a string. This
+ should be the same as \c INKSCAPE_PLUGIN_NAME but with quotes. */
+#define INKSCAPE_PLUGIN_NAME_STR "inkscape_plugin_table"
+/** \brief The version of the plugin interface that is being used. This
+ should always be used in the version entry in the \c inkscape_plugin_function_table
+ version entry. This way compiled plugins can be detected. */
+#define INKSCAPE_PLUGIN_VERSION 0
+
+/** \brief A structure containing all the functions that should be called
+ to make the plugin work. */
+typedef struct {
+ int version; /**< The interface version used. Should
+ always be \c INKSCAPE_PLUGIN_VERSION. */
+ inkscape_plugin_load load; /**< Load function, called on first use */
+ inkscape_plugin_unload unload; /**< Unload function, called when Inkscape is
+ finished with the plugin */
+ inkscape_plugin_open open; /**< Open function, called to open a file
+ for Inkscape */
+ inkscape_plugin_prefs_input prefs_input; /**< Input preferences function, called to get
+ further parameters for an input plugin. */
+ inkscape_plugin_effect effect; /**< Effect function, called to cause an effect
+ on a document. */
+ inkscape_plugin_prefs_effect prefs_effect;/**< Effect preferences, on call could cause settings
+ on a document. */
+} inkscape_plugin_function_table;
+
+#endif /* __INKSCAPE_EXTENSION_IMPLEMENTATION_PLUGIN_LINK_H__ */
diff --git a/src/extension/implementation/plugin.cpp b/src/extension/implementation/plugin.cpp
new file mode 100644
index 000000000..0bc3267f4
--- /dev/null
+++ b/src/extension/implementation/plugin.cpp
@@ -0,0 +1,328 @@
+/** \file
+ * The implementation of pluggable objects into Inkscape.
+ *
+ * Author: Ted Gould <ted@gould.cx>
+ * Copyright (c) 2004-2005
+ *
+ * This code is licensed under the GNU GPL. See COPYING for details.
+ *
+ * This file implements loadable modules for Inkscape.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <stdio.h>
+
+#include <glibmm/module.h>
+#include <glibmm/fileutils.h>
+#include <path-prefix.h>
+#include "extension/extension.h"
+#include "xml/repr.h"
+#include "plugin.h"
+#include "plugin-link.h"
+
+namespace Inkscape {
+namespace Extension {
+namespace Implementation {
+
+/** \brief Create an object by nulling everything out. */
+Plugin::Plugin(void)
+{
+ _module = NULL;
+ _symTable = NULL;
+
+ return;
+}
+
+/**
+ \brief Oh, so someone actually wants to use this plugin! We better
+ go and grab it then!
+ \param module Unused except to pass to the plugin's load function.
+ \return Whether the load was successful. Hopefully always TRUE.
+
+ Okay, first things first, are modules supported on this platform? That
+ is a good first check. Also, is this plugin already loaded? If so
+ don't reload it.
+
+ If all those are true we need to figure out the filename that needs
+ to be loaded. This involves going through the definition of the plugin
+ for the \c name field. It is then run though the \c build_path function
+ to add the .so or .dll on the end. The path that is used is the
+ \c INKSCAPE_PLUGINDIR.
+
+ The module is then loaded into RAM by Glib. I'm sure there is lots
+ of magic involved here -- but we don't have to worry about it, we
+ just check to make sure it worked.
+
+ After it is loaded into memory the function lookup table is grabbed
+ and then the load function for the plugin is called.
+*/
+bool
+Plugin::load(Inkscape::Extension::Extension *module)
+{
+ if (!Glib::Module::get_supported()) {
+ return FALSE;
+ }
+
+ if (module->loaded()) {
+ return TRUE;
+ }
+
+ Inkscape::XML::Node * child_repr = sp_repr_children(module->get_repr());
+ const gchar * name = NULL;
+ while (child_repr != NULL) {
+ if (!strcmp(child_repr->name(), "plugin")) {
+ child_repr = sp_repr_children(child_repr);
+ while (child_repr != NULL) {
+ if (!strcmp(child_repr->name(), "name")) {
+ name = sp_repr_children(child_repr)->content();
+ }
+ child_repr = sp_repr_next(child_repr);
+ }
+ }
+ child_repr = sp_repr_next(child_repr);
+ }
+
+ if (name == NULL) {
+ return FALSE;
+ }
+
+ std::string path = Glib::Module::build_path(INKSCAPE_PLUGINDIR, name);
+ // std::cout << "Load path: " << path << std::endl;
+ _module = new Glib::Module(path);
+
+ if (!(bool)_module || _module == NULL) {
+ printf("Loading failed\n");
+ return FALSE;
+ }
+
+ /* Grab symbols */
+ void * voidpntr;
+ if (!_module->get_symbol(INKSCAPE_PLUGIN_NAME_STR, voidpntr)) {
+ // printf("Error loading library\n");
+ // std::cout << "Last error: " << _module->get_last_error() << std::endl;
+ return FALSE;
+ }
+ _symTable = (inkscape_plugin_function_table *)voidpntr;
+
+ if (_symTable->version != INKSCAPE_PLUGIN_VERSION) {
+ /* Someday this could adapt older versions so that we could
+ be compatible -- but not today */
+ return FALSE;
+ }
+
+ if (_symTable->load != NULL) {
+ return (bool)_symTable->load((inkscape_extension *)module);
+ }
+
+ return TRUE;
+}
+
+/**
+ \brief No one is interested in this plugin for now. It is removed
+ to save memory.
+ \param module The module of this implementation, passed to unload.
+ \return None.
+
+ Call the unload function of the plugin, then delete the symbol table
+ and the module itself. Put everything back to NULL.
+*/
+void
+Plugin::unload(Inkscape::Extension::Extension *module)
+{
+ _symTable->unload((inkscape_extension *)module);
+ _symTable = NULL;
+ delete _module;
+ _module = NULL;
+ return;
+}
+
+/**
+ \brief Just check to make sure everything exists. This makes sure
+ that the loadable file exits.
+ \param module Unused.
+ \return The status of the check. TRUE means that it passed.
+
+ This function builds the path name out of the XML definition of the
+ plugin. It does this by looking for the \c name parameter. It then
+ uses \c build_path to add the .so or .dll and adds in the \c INKSCAPE_PLUGINDIR
+ to the front of it. Then the \c file_test function is used to make
+ sure it exists.
+*/
+bool
+Plugin::check(Inkscape::Extension::Extension *module)
+{
+ Inkscape::XML::Node * child_repr = sp_repr_children(module->get_repr());
+ const gchar * name = NULL;
+ while (child_repr != NULL) {
+ if (!strcmp(child_repr->name(), "plugin")) {
+ child_repr = sp_repr_children(child_repr);
+ while (child_repr != NULL) {
+ if (!strcmp(child_repr->name(), "name")) {
+ name = sp_repr_children(child_repr)->content();
+ }
+ child_repr = sp_repr_next(child_repr);
+ }
+ }
+ child_repr = sp_repr_next(child_repr);
+ }
+
+ if (name == NULL) {
+ return FALSE;
+ }
+
+ std::string path = Glib::Module::build_path(INKSCAPE_PLUGINDIR, name);
+ // std::cout << "Path: " << path << std::endl;
+ if (!Glib::file_test(path, Glib::FILE_TEST_EXISTS | Glib::FILE_TEST_IS_EXECUTABLE)) {
+ // std::cout << "Failed!" << std::endl;
+ return FALSE;
+ }
+
+ // std::cout << "No Problem." << std::endl;
+ return TRUE;
+}
+
+Gtk::Widget *
+Plugin::prefs_input(Inkscape::Extension::Input *module, gchar const *filename)
+{
+ return Inkscape::Extension::Implementation::Implementation::prefs_input(module, filename);
+}
+
+/*
+ \brief Function to call the open in the plugin.
+ \param module Passed on
+ \param filename Passed on
+ \return The document that is opened or NULL for error.
+
+ This function looks in the symbol table to see if there is an open
+ function. If there is, it is called, otherwise the standard implementation
+ function is used instead.
+*/
+SPDocument *
+Plugin::open(Inkscape::Extension::Input *module, gchar const *filename)
+{
+ if (_symTable->open != NULL) {
+ return _symTable->open((inkscape_extension *)module, filename);
+ } else {
+ return Inkscape::Extension::Implementation::Implementation::open(module, filename);
+ }
+}
+
+Gtk::Widget *
+Plugin::prefs_output(Inkscape::Extension::Output *module)
+{
+ return Inkscape::Extension::Implementation::Implementation::prefs_output(module);
+}
+
+void
+Plugin::save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *filename)
+{
+ return Inkscape::Extension::Implementation::Implementation::save(module, doc, filename);
+}
+
+Gtk::Widget *
+Plugin::prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View * view)
+{
+ if (_symTable->prefs_effect != NULL) {
+ return _symTable->prefs_effect((inkscape_extension *)module, view);
+ } else {
+ return Inkscape::Extension::Implementation::Implementation::prefs_effect(module, view);
+ }
+}
+
+void
+Plugin::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *document)
+{
+ if (_symTable->effect != NULL) {
+ return _symTable->effect((inkscape_extension *)module, document);
+ } else {
+ return Inkscape::Extension::Implementation::Implementation::effect(module, document);
+ }
+}
+
+unsigned
+Plugin::setup(Inkscape::Extension::Print *module)
+{
+ return Inkscape::Extension::Implementation::Implementation::setup(module);
+}
+
+unsigned
+Plugin::set_preview(Inkscape::Extension::Print *module)
+{
+ return Inkscape::Extension::Implementation::Implementation::set_preview(module);
+}
+
+unsigned
+Plugin::begin(Inkscape::Extension::Print *module, SPDocument *doc)
+{
+ return Inkscape::Extension::Implementation::Implementation::begin(module, doc);
+}
+
+unsigned
+Plugin::finish(Inkscape::Extension::Print *module)
+{
+ return Inkscape::Extension::Implementation::Implementation::finish(module);
+}
+
+bool
+Plugin::textToPath(Inkscape::Extension::Print *ext)
+{
+ return Inkscape::Extension::Implementation::Implementation::finish(ext);
+}
+
+unsigned
+Plugin::bind(Inkscape::Extension::Print *module, NRMatrix const *transform, float opacity)
+{
+ return Inkscape::Extension::Implementation::Implementation::bind(module, transform, opacity);
+}
+
+unsigned
+Plugin::release(Inkscape::Extension::Print *module)
+{
+ return Inkscape::Extension::Implementation::Implementation::release(module);
+}
+
+unsigned
+Plugin::comment(Inkscape::Extension::Print *module, const char * comment)
+{
+ return Inkscape::Extension::Implementation::Implementation::comment(module,comment);
+}
+
+unsigned
+Plugin::fill(Inkscape::Extension::Print *module, NRBPath const *bpath, NRMatrix const *ctm, SPStyle const *style, NRRect const *pbox, NRRect const *dbox, NRRect const *bbox)
+{
+ return Inkscape::Extension::Implementation::Implementation::fill(module, bpath, ctm, style, pbox, dbox, bbox);
+}
+
+unsigned
+Plugin::stroke(Inkscape::Extension::Print *module, NRBPath const *bpath, NRMatrix const *transform, SPStyle const *style, NRRect const *pbox, NRRect const *dbox, NRRect const *bbox)
+{
+ return Inkscape::Extension::Implementation::Implementation::stroke(module, bpath, transform, style, pbox, dbox, bbox);
+}
+
+unsigned
+Plugin::image(Inkscape::Extension::Print *module, unsigned char *px, unsigned int w, unsigned int h, unsigned int rs, NRMatrix const *transform, SPStyle const *style)
+{
+ return Inkscape::Extension::Implementation::Implementation::image(module, px, w, h, rs, transform, style);
+}
+
+unsigned
+Plugin::text(Inkscape::Extension::Print *module, char const *text, NR::Point p, SPStyle const *style)
+{
+ return Inkscape::Extension::Implementation::Implementation::text(module, text, p, style);
+}
+
+} /* namespace Implementation */
+} /* namespace Extension */
+} /* namespace Inkscape */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/extension/implementation/plugin.h b/src/extension/implementation/plugin.h
new file mode 100644
index 000000000..88658e954
--- /dev/null
+++ b/src/extension/implementation/plugin.h
@@ -0,0 +1,121 @@
+/** \file
+ * Inkscape::Extension::Implementation::Plugin
+ *
+ * Author: Ted Gould <ted@gould.cx>
+ * Copyright (c) 2004-2005
+ *
+ * This code is licensed under the GNU GPL. See COPYING for details.
+ */
+#ifndef __INKSCAPE_EXTENSION_IMPLEMENTATION_PLUGIN_H__
+#define __INKSCAPE_EXTENSION_IMPLEMENTATION_PLUGIN_H__
+
+#include <extension/implementation/implementation.h>
+#include <glibmm/module.h>
+#include "plugin-link.h"
+
+namespace Inkscape {
+namespace Extension {
+namespace Implementation {
+
+/** \brief For the most part this is a direct steal from \c implementation.h
+ in that all the functions have the same prototypes. The two
+ added things are a pointer to the loaded module and a pointer
+ to the symbol table in that. */
+class Plugin : public Implementation {
+ /** \brief A pointer to the module created when loading the plugin. */
+ Glib::Module * _module;
+ /** \brief The symbol table that is in the plugin. It is pulled out
+ here so that it doesn't have to be grabbed as often. */
+ inkscape_plugin_function_table * _symTable;
+
+public:
+ Plugin(void);
+
+ /* ----- Basic functions for all Extension ----- */
+ virtual bool load(Inkscape::Extension::Extension *module);
+
+ virtual void unload(Inkscape::Extension::Extension *module);
+
+ /** Verify any dependencies. */
+ virtual bool check(Inkscape::Extension::Extension *module);
+
+
+ /* ----- Input functions ----- */
+ /** Find out information about the file. */
+ virtual Gtk::Widget *prefs_input(Inkscape::Extension::Input *module,
+ gchar const *filename);
+
+ virtual SPDocument *open(Inkscape::Extension::Input *module,
+ gchar const *filename);
+
+ /* ----- Output functions ----- */
+ /** Find out information about the file. */
+ virtual Gtk::Widget *prefs_output(Inkscape::Extension::Output *module);
+ virtual void save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *filename);
+
+ /* ----- Effect functions ----- */
+ /** Find out information about the file. */
+ virtual Gtk::Widget * prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View * view);
+ /* TODO: need to figure out what we need here */
+
+ virtual void effect(Inkscape::Extension::Effect *module,
+ Inkscape::UI::View::View *document);
+
+ /* ----- Print functions ----- */
+ virtual unsigned setup(Inkscape::Extension::Print *module);
+ virtual unsigned set_preview(Inkscape::Extension::Print *module);
+
+ virtual unsigned begin(Inkscape::Extension::Print *module,
+ SPDocument *doc);
+ virtual unsigned finish(Inkscape::Extension::Print *module);
+ virtual bool textToPath(Inkscape::Extension::Print *ext);
+
+ /* ----- Rendering methods ----- */
+ virtual unsigned bind(Inkscape::Extension::Print *module,
+ NRMatrix const *transform,
+ float opacity);
+ virtual unsigned release(Inkscape::Extension::Print *module);
+ virtual unsigned comment(Inkscape::Extension::Print *module, const char * comment);
+ virtual unsigned fill(Inkscape::Extension::Print *module,
+ NRBPath const *bpath,
+ NRMatrix const *ctm,
+ SPStyle const *style,
+ NRRect const *pbox,
+ NRRect const *dbox,
+ NRRect const *bbox);
+ virtual unsigned stroke(Inkscape::Extension::Print *module,
+ NRBPath const *bpath,
+ NRMatrix const *transform,
+ SPStyle const *style,
+ NRRect const *pbox,
+ NRRect const *dbox,
+ NRRect const *bbox);
+ virtual unsigned image(Inkscape::Extension::Print *module,
+ unsigned char *px,
+ unsigned int w,
+ unsigned int h,
+ unsigned int rs,
+ NRMatrix const *transform,
+ SPStyle const *style);
+ virtual unsigned text(Inkscape::Extension::Print *module,
+ char const *text,
+ NR::Point p,
+ SPStyle const *style);
+};
+
+} /* namespace Implementation */
+} /* namespace Extension */
+} /* namespace Inkscape */
+
+#endif /* __INKSCAPE_EXTENSION_IMPLEMENTATION_PLUGIN_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
diff --git a/src/extension/implementation/script.cpp b/src/extension/implementation/script.cpp
new file mode 100644
index 000000000..f9cc2ee0b
--- /dev/null
+++ b/src/extension/implementation/script.cpp
@@ -0,0 +1,1053 @@
+/** \file
+ * Code for handling extensions (i.e.\ scripts).
+ */
+/*
+ * Authors:
+ * Bryce Harrington <bryce@osdl.org>
+ * Ted Gould <ted@gould.cx>
+ *
+ * Copyright (C) 2002-2005 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#define __INKSCAPE_EXTENSION_IMPLEMENTATION_SCRIPT_C__
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <unistd.h>
+
+#include <errno.h>
+#include <gtkmm/textview.h>
+#include <gtkmm/scrolledwindow.h>
+
+#include "ui/view/view.h"
+#include "desktop-handles.h"
+#include "selection.h"
+#include "sp-namedview.h"
+#include "io/sys.h"
+#include "prefs-utils.h"
+#include "../system.h"
+#include "extension/effect.h"
+#include "extension/db.h"
+#include "script.h"
+
+#include "util/glib-list-iterators.h"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+/** This is the command buffer that gets allocated from the stack */
+#define BUFSIZE (255)
+
+/* Namespaces */
+namespace Inkscape {
+namespace Extension {
+namespace Implementation {
+
+/* Real functions */
+/**
+ \return A script object
+ \brief This function creates a script object and sets up the
+ variables.
+
+ This function just sets the command to NULL. It should get built
+ officially in the load function. This allows for less allocation
+ of memory in the unloaded state.
+*/
+Script::Script() :
+ Implementation(),
+ command(NULL),
+ helper_extension(NULL)
+{
+}
+
+/**
+ \return A string with the complete string with the relative directory expanded
+ \brief This function takes in a Repr that contains a reldir entry
+ and returns that data with the relative directory expanded.
+ Mostly it is here so that relative directories all get used
+ the same way.
+ \param reprin The Inkscape::XML::Node with the reldir in it.
+
+ Basically this function looks at an attribute of the Repr, and makes
+ a decision based on that. Currently, it is only working with the
+ 'extensions' relative directory, but there will be more of them.
+ One thing to notice is that this function always returns an allocated
+ string. This means that the caller of this function can always
+ free what they are given (and should do it too!).
+*/
+gchar *
+Script::solve_reldir(Inkscape::XML::Node *reprin) {
+ gchar const *reldir = reprin->attribute("reldir");
+
+ if (reldir == NULL) {
+ return g_strdup(sp_repr_children(reprin)->content());
+ }
+
+ if (!strcmp(reldir, "extensions")) {
+ for(unsigned int i=0; i<Inkscape::Extension::Extension::search_path.size(); i++) {
+ gchar * filename = g_build_filename(Inkscape::Extension::Extension::search_path[i], sp_repr_children(reprin)->content(), NULL);
+ if ( Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS) ) {
+ return filename;
+ }
+ g_free(filename);
+ }
+ } else {
+ return g_strdup(sp_repr_children(reprin)->content());
+ }
+
+ return NULL;
+}
+
+/**
+ \return Whether the command given exists, including in the path
+ \brief This function is used to find out if something exists for
+ the check command. It can look in the path if required.
+ \param command The command or file that should be looked for
+
+ The first thing that this function does is check to see if the
+ incoming file name has a directory delimiter in it. This would
+ mean that it wants to control the directories, and should be
+ used directly.
+
+ If not, the path is used. Each entry in the path is stepped through,
+ attached to the string, and then tested. If the file is found
+ then a TRUE is returned. If we get all the way through the path
+ then a FALSE is returned, the command could not be found.
+*/
+bool
+Script::check_existance(gchar const *command)
+{
+ if (*command == '\0') {
+ /* We check the simple case first. */
+ return FALSE;
+ }
+
+ if (g_utf8_strchr(command, -1, G_DIR_SEPARATOR) != NULL) {
+ /* Don't search when it contains a slash. */
+ if (Inkscape::IO::file_test(command, G_FILE_TEST_EXISTS))
+ return TRUE;
+ else
+ return FALSE;
+ }
+
+
+ gchar *path = g_strdup(g_getenv("PATH"));
+ if (path == NULL) {
+ /* There is no `PATH' in the environment.
+ The default search path is the current directory */
+ path = g_strdup(G_SEARCHPATH_SEPARATOR_S);
+ }
+ gchar *orig_path = path;
+
+ for (; path != NULL;) {
+ gchar *const local_path = path;
+ path = g_utf8_strchr(path, -1, G_SEARCHPATH_SEPARATOR);
+ if (path == NULL) {
+ break;
+ }
+ /* Not sure whether this is UTF8 happy, but it would seem
+ like it considering that I'm searching (and finding)
+ the ':' character */
+ if (path != local_path && path != NULL) {
+ path[0] = '\0';
+ path++;
+ } else {
+ path = NULL;
+ }
+
+ gchar *final_name;
+ if (local_path == '\0') {
+ final_name = g_strdup(command);
+ } else {
+ final_name = g_build_filename(local_path, command, NULL);
+ }
+
+ if (Inkscape::IO::file_test(final_name, G_FILE_TEST_EXISTS)) {
+ g_free(final_name);
+ g_free(orig_path);
+ return TRUE;
+ }
+
+ g_free(final_name);
+ }
+
+ return FALSE;
+}
+
+/**
+ \return none
+ \brief This function 'loads' an extention, basically it determines
+ the full command for the extention and stores that.
+ \param module The extention to be loaded.
+
+ The most difficult part about this function is finding the actual
+ command through all of the Reprs. Basically it is hidden down a
+ couple of layers, and so the code has to move down too. When
+ the command is actually found, it has its relative directory
+ solved.
+
+ At that point all of the loops are exited, and there is an
+ if statement to make sure they didn't exit because of not finding
+ the command. If that's the case, the extention doesn't get loaded
+ and should error out at a higher level.
+*/
+
+bool
+Script::load(Inkscape::Extension::Extension *module)
+{
+ if (module->loaded()) {
+ return TRUE;
+ }
+
+ helper_extension = NULL;
+
+ /* This should probably check to find the executable... */
+ Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
+ gchar *command_text = NULL;
+ while (child_repr != NULL) {
+ if (!strcmp(child_repr->name(), "script")) {
+ child_repr = sp_repr_children(child_repr);
+ while (child_repr != NULL) {
+ if (!strcmp(child_repr->name(), "command")) {
+ command_text = solve_reldir(child_repr);
+
+ const gchar * interpretstr = child_repr->attribute("interpreter");
+ if (interpretstr != NULL) {
+ struct interpreter_t {
+ gchar * identity;
+ gchar * prefstring;
+ gchar * defaultval;
+ };
+ const interpreter_t interpreterlst[] = {
+ {"perl", "perl-interpreter", "perl"},
+ {"python", "python-interpreter", "python"},
+ {"ruby", "ruby-interpreter", "ruby"},
+ {"shell", "shell-interpreter", "sh"}
+ }; /* Change count below if you change structure */
+ for (unsigned int i = 0; i < 4; i++) {
+ if (!strcmp(interpretstr, interpreterlst[i].identity)) {
+ const gchar * insertText = interpreterlst[i].defaultval;
+ if (prefs_get_string_attribute("extensions", interpreterlst[i].prefstring) != NULL)
+ insertText = prefs_get_string_attribute("extensions", interpreterlst[i].prefstring);
+#ifdef _WIN32
+ else {
+ char szExePath[MAX_PATH];
+ char szCurrentDir[MAX_PATH];
+ GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
+ if (reinterpret_cast<unsigned>(FindExecutable(command_text, szCurrentDir, szExePath)) > 32)
+ insertText = szExePath;
+ }
+#endif
+
+ gchar * temp = command_text;
+ command_text = g_strconcat(insertText, " ", temp, NULL);
+ g_free(temp);
+
+ break;
+ }
+ }
+ }
+ }
+ if (!strcmp(child_repr->name(), "helper_extension")) {
+ helper_extension = g_strdup(sp_repr_children(child_repr)->content());
+ }
+ child_repr = sp_repr_next(child_repr);
+ }
+
+ break;
+ }
+ child_repr = sp_repr_next(child_repr);
+ }
+
+ g_return_val_if_fail(command_text != NULL, FALSE);
+
+ if (command != NULL)
+ g_free(command);
+ command = command_text;
+
+ return TRUE;
+}
+
+/**
+ \return None.
+ \brief Unload this puppy!
+ \param module Extension to be unloaded.
+
+ This function just sets the module to unloaded. It free's the
+ command if it has been allocated.
+*/
+void
+Script::unload(Inkscape::Extension::Extension *module)
+{
+ if (command != NULL) {
+ g_free(command);
+ command = NULL;
+ }
+ if (helper_extension != NULL) {
+ g_free(helper_extension);
+ helper_extension = NULL;
+ }
+
+ return;
+}
+
+/**
+ \return Whether the check passed or not
+ \brief Check every dependency that was given to make sure we should keep this extension
+ \param module The Extension in question
+
+*/
+bool
+Script::check(Inkscape::Extension::Extension *module)
+{
+ Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
+ while (child_repr != NULL) {
+ if (!strcmp(child_repr->name(), "script")) {
+ child_repr = sp_repr_children(child_repr);
+ while (child_repr != NULL) {
+ if (!strcmp(child_repr->name(), "check")) {
+ gchar *command_text = solve_reldir(child_repr);
+ if (command_text != NULL) {
+ /* I've got the command */
+ bool existance;
+
+ existance = check_existance(command_text);
+ g_free(command_text);
+ if (!existance)
+ return FALSE;
+ }
+ }
+
+ if (!strcmp(child_repr->name(), "helper_extension")) {
+ gchar const *helper = sp_repr_children(child_repr)->content();
+ if (Inkscape::Extension::db.get(helper) == NULL) {
+ return FALSE;
+ }
+ }
+
+ child_repr = sp_repr_next(child_repr);
+ }
+
+ break;
+ }
+ child_repr = sp_repr_next(child_repr);
+ }
+
+ return TRUE;
+}
+
+/**
+ \return A dialog for preferences
+ \brief A stub funtion right now
+ \param module Module who's preferences need getting
+ \param filename Hey, the file you're getting might be important
+
+ This function should really do something, right now it doesn't.
+*/
+Gtk::Widget *
+Script::prefs_input(Inkscape::Extension::Input *module, gchar const *filename)
+{
+ /*return module->autogui(); */
+ return NULL;
+}
+
+/**
+ \return A dialog for preferences
+ \brief A stub funtion right now
+ \param module Module whose preferences need getting
+
+ This function should really do something, right now it doesn't.
+*/
+Gtk::Widget *
+Script::prefs_output(Inkscape::Extension::Output *module)
+{
+ /*return module->autogui();*/
+ return NULL;
+}
+
+/**
+ \return A dialog for preferences
+ \brief A stub funtion right now
+ \param module Module who's preferences need getting
+
+ This function should really do something, right now it doesn't.
+*/
+Gtk::Widget *
+Script::prefs_effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *view)
+{
+ return module->autogui();
+}
+
+/**
+ \return A new document that has been opened
+ \brief This function uses a filename that is put in, and calls
+ the extension's command to create an SVG file which is
+ returned.
+ \param module Extension to use.
+ \param filename File to open.
+
+ First things first, this function needs a temporary file name. To
+ create on of those the function g_file_open_tmp is used with
+ the header of ink_ext_.
+
+ The extension is then executed using the 'execute' function
+ with the filname coming in, and the temporary filename. After
+ That executing, the SVG should be in the temporary file.
+
+ Finally, the temporary file is opened using the SVG input module and
+ a document is returned. That document has its filename set to
+ the incoming filename (so that it's not the temporary filename).
+ That document is then returned from this function.
+*/
+SPDocument *
+Script::open(Inkscape::Extension::Input *module, gchar const *filename)
+{
+ int data_read = 0;
+ gint tempfd;
+ gchar *tempfilename_out;
+
+ // FIXME: process the GError instead of passing NULL
+ if ((tempfd = g_file_open_tmp("ink_ext_XXXXXX", &tempfilename_out, NULL)) == -1) {
+ /* Error, couldn't create temporary filename */
+ if (errno == EINVAL) {
+ /* The last six characters of template were not XXXXXX. Now template is unchanged. */
+ perror("Extension::Script: template for filenames is misconfigured.\n");
+ exit(-1);
+ } else if (errno == EEXIST) {
+ /* Now the contents of template are undefined. */
+ perror("Extension::Script: Could not create a unique temporary filename\n");
+ return NULL;
+ } else {
+ perror("Extension::Script: Unknown error creating temporary filename\n");
+ exit(-1);
+ }
+ }
+
+ gsize bytesRead = 0;
+ gsize bytesWritten = 0;
+ GError *error = NULL;
+ gchar *local_filename = g_filename_from_utf8( filename,
+ -1, &bytesRead, &bytesWritten, &error);
+
+ data_read = execute(command, local_filename, tempfilename_out);
+ g_free(local_filename);
+
+ SPDocument *mydoc = NULL;
+ if (data_read > 10) {
+ if (helper_extension == NULL) {
+ mydoc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), tempfilename_out);
+ } else {
+ mydoc = Inkscape::Extension::open(Inkscape::Extension::db.get(helper_extension), tempfilename_out);
+ }
+ }
+
+ if (mydoc != NULL)
+ sp_document_set_uri(mydoc, (const gchar *)filename);
+
+ // make sure we don't leak file descriptors from g_file_open_tmp
+ close(tempfd);
+ // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
+ unlink(tempfilename_out);
+ g_free(tempfilename_out);
+
+ return mydoc;
+}
+
+/**
+ \return none
+ \brief This function uses an extention to save a document. It first
+ creates an SVG file of the document, and then runs it through
+ the script.
+ \param module Extention to be used
+ \param doc Document to be saved
+ \param filename The name to save the final file as
+
+ Well, at some point people need to save - it is really what makes
+ the entire application useful. And, it is possible that someone
+ would want to use an extetion for this, so we need a function to
+ do that eh?
+
+ First things first, the document is saved to a temporary file that
+ is an SVG file. To get the temporary filename g_file_open_tmp is used with
+ ink_ext_ as a prefix. Don't worry, this file gets deleted at the
+ end of the function.
+
+ After we have the SVG file, then extention_execute is called with
+ the temporary file name and the final output filename. This should
+ put the output of the script into the final output file. We then
+ delete the temporary file.
+*/
+void
+Script::save(Inkscape::Extension::Output *module, SPDocument *doc, gchar const *filename)
+{
+ gint tempfd;
+ gchar *tempfilename_in;
+ // FIXME: process the GError instead of passing NULL
+ if ((tempfd = g_file_open_tmp("ink_ext_XXXXXX", &tempfilename_in, NULL)) == -1) {
+ /* Error, couldn't create temporary filename */
+ if (errno == EINVAL) {
+ /* The last six characters of template were not XXXXXX. Now template is unchanged. */
+ perror("Extension::Script: template for filenames is misconfigured.\n");
+ exit(-1);
+ } else if (errno == EEXIST) {
+ /* Now the contents of template are undefined. */
+ perror("Extension::Script: Could not create a unique temporary filename\n");
+ return;
+ } else {
+ perror("Extension::Script: Unknown error creating temporary filename\n");
+ exit(-1);
+ }
+ }
+
+ if (helper_extension == NULL) {
+ Inkscape::Extension::save(Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE), doc, tempfilename_in, FALSE, FALSE, FALSE);
+ } else {
+ Inkscape::Extension::save(Inkscape::Extension::db.get(helper_extension), doc, tempfilename_in, FALSE, FALSE, FALSE);
+ }
+
+ gsize bytesRead = 0;
+ gsize bytesWritten = 0;
+ GError *error = NULL;
+ gchar *local_filename = g_filename_from_utf8( filename,
+ -1, &bytesRead, &bytesWritten, &error);
+
+ execute(command, tempfilename_in, local_filename);
+
+ g_free(local_filename);
+
+ // make sure we don't leak file descriptors from g_file_open_tmp
+ close(tempfd);
+ // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
+ unlink(tempfilename_in);
+ g_free(tempfilename_in);
+}
+
+/**
+ \return none
+ \brief This function uses an extention as a effect on a document.
+ \param module Extention to effect with.
+ \param doc Document to run through the effect.
+
+ This function is a little bit trickier than the previous two. It
+ needs two temporary files to get it's work done. Both of these
+ files have random names created for them using the g_file_open_temp function
+ with the sp_ext_ prefix in the temporary directory. Like the other
+ functions, the temporary files are deleted at the end.
+
+ To save/load the two temporary documents (both are SVG) the internal
+ modules for SVG load and save are used. They are both used through
+ the module system function by passing their keys into the functions.
+
+ The command itself is built a little bit differently than in other
+ functions because the effect support selections. So on the command
+ line a list of all the ids that are selected is included. Currently,
+ this only works for a single selected object, but there will be more.
+ The command string is filled with the data, and then after the execution
+ it is freed.
+
+ The execute function is used at the core of this function
+ to execute the Script on the two SVG documents (actually only one
+ exists at the time, the other is created by that script). At that
+ point both should be full, and the second one is loaded.
+*/
+void
+Script::effect(Inkscape::Extension::Effect *module, Inkscape::UI::View::View *doc)
+{
+ int data_read = 0;
+ SPDocument * mydoc = NULL;
+ gint tempfd_in;
+ gchar *tempfilename_in;
+
+ // FIXME: process the GError instead of passing NULL
+ if ((tempfd_in = g_file_open_tmp("ink_ext_XXXXXX", &tempfilename_in, NULL)) == -1) {
+ /* Error, couldn't create temporary filename */
+ if (errno == EINVAL) {
+ /* The last six characters of template were not XXXXXX. Now template is unchanged. */
+ perror("Extension::Script: template for filenames is misconfigured.\n");
+ exit(-1);
+ } else if (errno == EEXIST) {
+ /* Now the contents of template are undefined. */
+ perror("Extension::Script: Could not create a unique temporary filename\n");
+ return;
+ } else {
+ perror("Extension::Script: Unknown error creating temporary filename\n");
+ exit(-1);
+ }
+ }
+
+ gint tempfd_out;
+ gchar *tempfilename_out;
+ // FIXME: process the GError instead of passing NULL
+ if ((tempfd_out = g_file_open_tmp("ink_ext_XXXXXX", &tempfilename_out, NULL)) == -1) {
+ /* Error, couldn't create temporary filename */
+ if (errno == EINVAL) {
+ /* The last six characters of template were not XXXXXX. Now template is unchanged. */
+ perror("Extension::Script: template for filenames is misconfigured.\n");
+ exit(-1);
+ } else if (errno == EEXIST) {
+ /* Now the contents of template are undefined. */
+ perror("Extension::Script: Could not create a unique temporary filename\n");
+ return;
+ } else {
+ perror("Extension::Script: Unknown error creating temporary filename\n");
+ exit(-1);
+ }
+ }
+
+ Inkscape::Extension::save(Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
+ doc->doc(), tempfilename_in, FALSE, FALSE, FALSE);
+
+ Glib::ustring local_command(command);
+
+ /* fixme: Should be some sort of checking here. Don't know how to do this with structs instead
+ * of classes. */
+ SPDesktop *desktop = (SPDesktop *) doc;
+ if (desktop != NULL) {
+ using Inkscape::Util::GSListConstIterator;
+ GSListConstIterator<SPItem *> selected = SP_DT_SELECTION(desktop)->itemList();
+ while ( selected != NULL ) {
+ local_command += " --id=";
+ local_command += SP_OBJECT_ID(*selected);
+ ++selected;
+ }
+ }
+
+ Glib::ustring * paramString = module->paramString();
+ local_command += *paramString;
+ delete paramString;
+
+ // std::cout << local_command << std::endl;
+
+ data_read = execute(local_command.c_str(), tempfilename_in, tempfilename_out);
+
+ if (data_read > 10)
+ mydoc = Inkscape::Extension::open(Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG), tempfilename_out);
+
+ // make sure we don't leak file descriptors from g_file_open_tmp
+ close(tempfd_in);
+ close(tempfd_out);
+ // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
+ unlink(tempfilename_in);
+ g_free(tempfilename_in);
+ unlink(tempfilename_out);
+ g_free(tempfilename_out);
+
+ /* Do something with mydoc.... */
+ if (mydoc != NULL) {
+ doc->doc()->emitReconstructionStart();
+ copy_doc(doc->doc()->rroot, mydoc->rroot);
+ doc->doc()->emitReconstructionFinish();
+ mydoc->release();
+ }
+}
+
+
+/**
+ \brief A function to take all the svg elements from one document
+ and put them in another.
+ \param oldroot The root node of the document to be replaced
+ \param newroot The root node of the document to replace it with
+
+ This function first deletes all of the data in the old document. It
+ does this by creating a list of what needs to be deleted, and then
+ goes through the list. This two pass approach removes issues with
+ the list being change while parsing through it. Lots of nasty bugs.
+
+ Then, it goes through the new document, duplicating all of the
+ elements and putting them into the old document. The copy
+ is then complete.
+*/
+void
+Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
+{
+ std::vector<Inkscape::XML::Node *> delete_list;
+ for (Inkscape::XML::Node * child = oldroot->firstChild();
+ child != NULL;
+ child = child->next()) {
+ if (!strcmp("sodipodi:namedview", child->name()))
+ continue;
+ if (!strcmp("svg:defs", child->name()))
+ continue;
+ delete_list.push_back(child);
+ }
+ for (unsigned int i = 0; i < delete_list.size(); i++)
+ sp_repr_unparent(delete_list[i]);
+
+ for (Inkscape::XML::Node * child = newroot->firstChild();
+ child != NULL;
+ child = child->next()) {
+ if (!strcmp("sodipodi:namedview", child->name()))
+ continue;
+ if (!strcmp("svg:defs", child->name()))
+ continue;
+ oldroot->appendChild(child->duplicate());
+ }
+
+ /** \todo Restore correct layer */
+ /** \todo Restore correct selection */
+}
+
+/* Helper class used by Script::execute */
+class pipe_t {
+public:
+ /* These functions set errno if they return false.
+ I'm not sure whether that's a good idea or not, but it should be reasonably
+ straightforward to change it if needed. */
+ bool open(char *command, char const *errorFile, int mode);
+ bool close();
+
+ /* These return the number of bytes read/written. */
+ size_t read(void *buffer, size_t size);
+ size_t write(void const *buffer, size_t size);
+
+ enum {
+ mode_read = 1 << 0,
+ mode_write = 1 << 1,
+ };
+
+private:
+#ifdef WIN32
+ /* This is used to translate win32 errors into errno errors.
+ It only recognizes a few win32 errors for the moment though. */
+ static int translate_error(DWORD err);
+
+ HANDLE hpipe;
+#else
+ FILE *ppipe;
+#endif
+};
+
+/**
+ \return none
+ \brief This is the core of the extension file as it actually does
+ the execution of the extension.
+ \param in_command The command to be executed
+ \param filein Filename coming in
+ \param fileout Filename of the out file
+ \return Number of bytes that were read into the output file.
+
+ The first thing that this function does is build the command to be
+ executed. This consists of the first string (in_command) and then
+ the filename for input (filein). This file is put on the command
+ line.
+
+ The next thing is that this function does is open a pipe to the
+ command and get the file handle in the ppipe variable. It then
+ opens the output file with the output file handle. Both of these
+ operations are checked extensively for errors.
+
+ After both are opened, then the data is copied from the output
+ of the pipe into the file out using fread and fwrite. These two
+ functions are used because of their primitive nature they make
+ no assumptions about the data. A buffer is used in the transfer,
+ but the output of fread is stored so the exact number of bytes
+ is handled gracefully.
+
+ At the very end (after the data has been copied) both of the files
+ are closed, and we return to what we were doing.
+*/
+int
+Script::execute (const gchar * in_command, const gchar * filein, const gchar * fileout)
+{
+ g_return_val_if_fail(in_command != NULL, 0);
+ // printf("Executing: %s\n", in_command);
+
+ gchar * errorFile;
+ gint errorFileNum;
+ errorFileNum = g_file_open_tmp("ink_ext_stderr_XXXXXX", &errorFile, NULL);
+ if (errorFileNum != 0) {
+ close(errorFileNum);
+ } else {
+ g_free(errorFile);
+ errorFile = NULL;
+ }
+
+ char *command = g_strdup_printf("%s \"%s\"", in_command, filein);
+ // std::cout << "Command to run: " << command << std::endl;
+
+ pipe_t pipe;
+ bool open_success = pipe.open(command, errorFile, pipe_t::mode_read);
+ g_free(command);
+
+ /* Run script */
+ if (!open_success) {
+ /* Error - could not open pipe - check errno */
+ if (errno == EINVAL) {
+ perror("Extension::Script: Invalid mode argument in popen\n");
+ } else if (errno == ECHILD) {
+ perror("Extension::Script: Cannot obtain child extension status in popen\n");
+ } else {
+ perror("Extension::Script: Unknown error for popen\n");
+ }
+ return 0;
+ }
+
+ Inkscape::IO::dump_fopen_call(fileout, "J");
+ FILE *pfile = Inkscape::IO::fopen_utf8name(fileout, "w");
+
+ if (pfile == NULL) {
+ /* Error - could not open file */
+ if (errno == EINVAL) {
+ perror("Extension::Script: The mode provided to fopen was invalid\n");
+ } else {
+ perror("Extension::Script: Unknown error attempting to open temporary file\n");
+ }
+ return 0;
+ }
+
+ /* Copy pipe output to a temporary file */
+ int amount_read = 0;
+ char buf[BUFSIZE];
+ int num_read;
+ while ((num_read = pipe.read(buf, BUFSIZE)) != 0) {
+ amount_read += num_read;
+ fwrite(buf, 1, num_read, pfile);
+ }
+
+ /* Close file */
+ if (fclose(pfile) == EOF) {
+ if (errno == EBADF) {
+ perror("Extension::Script: The filedescriptor for the temporary file is invalid\n");
+ return 0;
+ } else {
+ perror("Extension::Script: Unknown error closing temporary file\n");
+ }
+ }
+
+ /* Close pipe */
+ if (!pipe.close()) {
+ if (errno == EINVAL) {
+ perror("Extension::Script: Invalid mode set for pclose\n");
+ } else if (errno == ECHILD) {
+ perror("Extension::Script: Could not obtain child status for pclose\n");
+ } else {
+ if (errorFile != NULL) {
+ checkStderr(errorFile, Gtk::MESSAGE_ERROR,
+ _("Inkscape has received an error from the script that it called. "
+ "The text returned with the error is included below. "
+ "Inkscape will continue working, but the action you requested has been cancelled."));
+ } else {
+ perror("Extension::Script: Unknown error for pclose\n");
+ }
+ }
+ /* Could be a lie, but if there is an error, we don't want
+ * to count on what was read being good */
+ amount_read = 0;
+ } else {
+ if (errorFile != NULL) {
+ checkStderr(errorFile, Gtk::MESSAGE_INFO,
+ _("Inkscape has received additional data from the script executed. "
+ "The script did not return an error, but this may indicate the results will not be as expected."));
+ }
+ }
+
+ if (errorFile != NULL) {
+ unlink(errorFile);
+ g_free(errorFile);
+ }
+
+ return amount_read;
+}
+
+/** \brief This function checks the stderr file, and if it has data,
+ shows it in a warning dialog to the user
+ \param filename Filename of the stderr file
+*/
+void
+Script::checkStderr (gchar * filename, Gtk::MessageType type, gchar * message)
+{
+ // magic win32 crlf->lf conversion means the file length is not the same as
+ // the text length, but luckily gtk will accept crlf in textviews so we can
+ // just use binary mode
+ std::ifstream stderrf (filename, std::ios_base::in | std::ios_base::binary);
+ if (!stderrf.is_open()) return;
+
+ stderrf.seekg(0, std::ios::end);
+ int length = stderrf.tellg();
+ if (0 == length) return;
+ stderrf.seekg(0, std::ios::beg);
+
+ Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
+ warning.set_resizable(true);
+
+ Gtk::VBox * vbox = warning.get_vbox();
+
+ /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
+ Gtk::TextView * textview = new Gtk::TextView();
+ textview->set_editable(false);
+ textview->set_wrap_mode(Gtk::WRAP_WORD);
+ textview->show();
+
+ char * buffer = new char [length];
+ stderrf.read(buffer, length);
+ textview->get_buffer()->set_text(buffer, buffer + length);
+ delete buffer;
+ stderrf.close();
+
+ Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
+ scrollwindow->add(*textview);
+ scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+ scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
+ scrollwindow->show();
+
+ vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
+
+ warning.run();
+
+ return;
+}
+
+#ifdef WIN32
+
+bool pipe_t::open(char *command, char const *errorFile, int mode_p) {
+ HANDLE pipe_write;
+
+ // Create pipe
+ {
+ SECURITY_ATTRIBUTES secattrs;
+ ZeroMemory(&secattrs, sizeof(secattrs));
+ secattrs.nLength = sizeof(secattrs);
+ secattrs.lpSecurityDescriptor = 0;
+ secattrs.bInheritHandle = TRUE;
+ HANDLE t_pipe_read = 0;
+ if ( !CreatePipe(&t_pipe_read, &pipe_write, &secattrs, 0) ) {
+ errno = translate_error(GetLastError());
+ return false;
+ }
+ // This duplicate handle makes the read pipe uninheritable
+ if ( !DuplicateHandle(GetCurrentProcess(), t_pipe_read, GetCurrentProcess(), &hpipe, 0, FALSE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS) ) {
+ int en = translate_error(GetLastError());
+ CloseHandle(t_pipe_read);
+ CloseHandle(pipe_write);
+ errno = en;
+ return false;
+ }
+ }
+ // Open stderr file
+ HANDLE hStdErrFile = CreateFile(errorFile, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, 0, NULL);
+ HANDLE hInheritableStdErr;
+ DuplicateHandle(GetCurrentProcess(), hStdErrFile, GetCurrentProcess(), &hInheritableStdErr, 0, TRUE, DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS);
+
+ // Create process
+ {
+ PROCESS_INFORMATION procinfo;
+ STARTUPINFO startupinfo;
+ ZeroMemory(&procinfo, sizeof(procinfo));
+ ZeroMemory(&startupinfo, sizeof(startupinfo));
+ startupinfo.cb = sizeof(startupinfo);
+ //startupinfo.lpReserved = 0;
+ //startupinfo.lpDesktop = 0;
+ //startupinfo.lpTitle = 0;
+ startupinfo.dwFlags = STARTF_USESTDHANDLES;
+ startupinfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ startupinfo.hStdOutput = pipe_write;
+ startupinfo.hStdError = hInheritableStdErr;
+
+ if ( !CreateProcess(NULL, command, NULL, NULL, TRUE, 0, NULL, NULL, &startupinfo, &procinfo) ) {
+ errno = translate_error(GetLastError());
+ return false;
+ }
+ CloseHandle(procinfo.hThread);
+ CloseHandle(procinfo.hProcess);
+ }
+
+ // Close our copy of the write handle
+ CloseHandle(hInheritableStdErr);
+ CloseHandle(pipe_write);
+
+ return true;
+}
+
+bool pipe_t::close() {
+ BOOL retval = CloseHandle(hpipe);
+ if ( !retval ) {
+ errno = translate_error(GetLastError());
+ }
+ return retval != FALSE;
+}
+
+size_t pipe_t::read(void *buffer, size_t size) {
+ DWORD bytes_read = 0;
+ ReadFile(hpipe, buffer, size, &bytes_read, 0);
+ return bytes_read;
+}
+
+size_t pipe_t::write(void const *buffer, size_t size) {
+ DWORD bytes_written = 0;
+ WriteFile(hpipe, buffer, size, &bytes_written, 0);
+ return bytes_written;
+}
+
+int pipe_t::translate_error(DWORD err) {
+ switch (err) {
+ case ERROR_FILE_NOT_FOUND:
+ return ENOENT;
+ case ERROR_INVALID_HANDLE:
+ case ERROR_INVALID_PARAMETER:
+ return EINVAL;
+ default:
+ return 0;
+ }
+}
+
+#else // Win32
+
+bool pipe_t::open(char *command, char const *errorFile, int mode_p) {
+ char popen_mode[4] = {0,0,0,0};
+ char *popen_mode_cur = popen_mode;
+
+ if ( (mode_p & mode_read) != 0 ) {
+ *popen_mode_cur++ = 'r';
+ }
+
+ if ( (mode_p & mode_write) != 0 ) {
+ *popen_mode_cur++ = 'w';
+ }
+
+ /* Get the commandline to be run */
+ if (errorFile != NULL) {
+ char * temp;
+ temp = g_strdup_printf("%s 2> %s", command, errorFile);
+ ppipe = popen(temp, popen_mode);
+ g_free(temp);
+ } else
+ ppipe = popen(command, popen_mode);
+
+ return ppipe != NULL;
+}
+
+bool pipe_t::close() {
+ return fclose(ppipe) == 0;
+}
+
+size_t pipe_t::read(void *buffer, size_t size) {
+ return fread(buffer, 1, size, ppipe);
+}
+
+size_t pipe_t::write(void const *buffer, size_t size) {
+ return fwrite(buffer, 1, size, ppipe);
+}
+
+#endif // (Non-)Win32
+
+
+} /* Inkscape */
+} /* module */
+} /* Implementation */
+
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
diff --git a/src/extension/implementation/script.h b/src/extension/implementation/script.h
new file mode 100644
index 000000000..e13ffbb13
--- /dev/null
+++ b/src/extension/implementation/script.h
@@ -0,0 +1,86 @@
+/*
+ * Code for handling extensions (i.e., scripts)
+ *
+ * Authors:
+ * Bryce Harrington <bryce@osdl.org>
+ * Ted Gould <ted@gould.cx>
+ *
+ * Copyright (C) 2002-2005 Authors
+ *
+ * Released under GNU GPL, read the file 'COPYING' for more information
+ */
+
+#ifndef __INKSCAPE_EXTENSION_IMPEMENTATION_SCRIPT_H__
+#define __INKSCAPE_EXTENSION_IMPEMENTATION_SCRIPT_H__
+
+#include "implementation.h"
+#include <gtkmm/messagedialog.h>
+
+namespace Inkscape {
+namespace XML {
+class Node;
+}
+}
+
+
+namespace Inkscape {
+namespace Extension {
+namespace Implementation {
+
+class Script : public Implementation {
+private:
+ gchar * command; /**< The command that has been dirived from
+ the configuration file with appropriate
+ directories */
+ gchar * helper_extension;
+ /**< This is the extension that will be used
+ as the helper to read in or write out the
+ data */
+ /** This function actually does the work, everything else is preparing
+ for this function. It is the core here */
+ int execute (gchar const *command,
+ gchar const *filein,
+ gchar const *fileout);
+ /** Just a quick function to find and resolve relative paths for
+ the incoming scripts */
+ gchar * solve_reldir (Inkscape::XML::Node *reprin);
+ bool check_existance (gchar const *command);
+ void copy_doc (Inkscape::XML::Node * olddoc, Inkscape::XML::Node * newdoc);
+ void checkStderr (gchar * filename, Gtk::MessageType type, gchar * message);
+
+public:
+ Script (void);
+ virtual bool load (Inkscape::Extension::Extension *module);
+ virtual void unload (Inkscape::Extension::Extension *module);
+ virtual bool check (Inkscape::Extension::Extension *module);
+ virtual Gtk::Widget * prefs_input (Inkscape::Extension::Input *module,
+ gchar const *filename);
+ virtual SPDocument * open (Inkscape::Extension::Input *module,
+ gchar const *filename);
+ virtual Gtk::Widget * prefs_output (Inkscape::Extension::Output *module);
+ virtual void save (Inkscape::Extension::Output *module,
+ SPDocument *doc,
+ gchar const *filename);
+ virtual Gtk::Widget *
+ prefs_effect (Inkscape::Extension::Effect *module,
+ Inkscape::UI::View::View * view);
+ virtual void effect (Inkscape::Extension::Effect *module,
+ Inkscape::UI::View::View *doc);
+
+};
+
+} /* Inkscape */
+} /* Extension */
+} /* Implementation */
+#endif /* __INKSCAPE_EXTENSION_IMPEMENTATION_SCRIPT_H__ */
+
+/*
+ Local Variables:
+ mode:c++
+ c-file-style:"stroustrup"
+ c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
+ indent-tabs-mode:nil
+ fill-column:99
+ End:
+*/
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :